From b56e8926f03b3659640411fa65c8be9107849e00 Mon Sep 17 00:00:00 2001 From: sonoftunk <59073987+sonoftunk@users.noreply.github.com> Date: Sat, 10 Dec 2022 22:39:23 -0500 Subject: [PATCH] Randomizer Auto Location tracker (#1942) * Initial Auto-Location Tracking * Updates styling for checked items in Check Tracker * Fixes crashing issues with Check Tracker rendering * Auto scroll Check Tracker windows to current area * Checks Tracker only shows checked locations once in game * Fixes issue where Check Tracker would auto-scroll to wrong location when entering a grotto * Fixes gerudo fortress checks showing in tracker when settings have them removed * InfTable Check Location Fixes: Lost Dog Richard, Rolling Gorons, HBA 1000, Thawed Zora * Fixes most Event Chk Inf Checks on Check Tracker, and Frogs * Check Tracker - Song from Malon now works * Check Tracker - Fixes Claim Check check not tracking - Fixes spoiler for Claim Check check when getting Biggoron's Sword - Fixes LACS being spoiled and not tracking * Check Tracker - Fixes #2 for Skull Mask and Mask of Truth checks * Check Tracker - Adds support for merchants - Fixes #7 for Bomchu Salesman - Fixes #12 for Medigoron * Check Tracker - Fixes #8 for Song from Saria * Check Tracker - Fixes #9 for Adult Shooting Gallery * Check Tracker - Fixes #10 for Composer Grave * Check Tracker - Fixes #14 for Song from Ocarina of Time * Check Tracker - Fixes #16 for Shooting the Sun * Check Tracker - Fixes #19 for Darunia's Joy * Save Editor - Adds missing label for Treasure Chest Game * Check Tracker - Fixes Great Faries checks * Check Tracker - Fixes Sheik at Temple * Check Tracker - Fixes Great Fairy auto-scroll to wrong location * Check Tracker - Updates blue warp checks - Fixes Bongo Bongo and Twinrova checks * Check Tracker - Fixes Dampe's Gravedigging Tour * Check Tracker - Fixes Prescription to King Zora * Check Tracker - Fixes Bazaar showing under wrong area on tracker between adult/child * Check Tracker - Fixes scroll for Hyrule Market and Bazaar * Check Tracker - Fixes giving Adult Trade Items shown on tracker: Broken Sword, Saw, Cojiro, Eyeball Froge, and Eyedrops * Cleans up item_location * Check Tracker - Item List - Fixes item list not updating when changing seeds/options - Separates item exclusion list from item check list - Clean up comments * Check Tracker - Adds Skip functionality to buttons * Check Tracker - Refactors adult trade items to RandomizerInf * Check Tracker - Fixes an issue where any time an item is removed the tracker would cause an application crash * Check Tracker - Item names are now dependent on file language * Check Tracker - Fixes Gerudo Card check not showing in Fast setting * Reorders headers to (hopefully) avoid CI build conflicts on linux/consoles * Explicitly defines vector header in randomizer.h to fix compile errors * Complete Refactor of Check Tracker ImGui * Adds missing check tracker files to CMakeLists * Reverts the Item_location whitespace cleanup from ad10807c * Check Tracker - Loading Fixes - Fixes a bug where loading a save file a second time would add all the checks again - Fixes a bug where the check tracker would not render at all until in a save file * Check Tracker - Fixes a bug where checks would sometimes fail to load * Check Tracker - Fixes duplicated checks when using Only on Pause or Only on Combo * Check Tracker - Works around issue where single characters don't show in EnhancementCombobox, and fixes a label for combo buttons held option * Check Tracker - Applies all Check Tracker changes to Item Location * Check Tracker - Updated Check List - Adds Link's pocket, either in KF or HM depending on starting age - Supports Scrubs and Merchants - Uses enums instead of magic numbers - Updates DC Scrub Short Names for MQ * Check Tracker - Adds a performance mode option * Check Tracker - List Fixes (requires new rando generation) - Fixes Frogs Rupees always visible - Fixes Kokiri Sword Chest not showing - Fixes Weird Egg not showing * Check Tracker - MQ List basic support * Check Tracker - MQ/Vanilla anti-spoilers * Check Tracker - Collecting a compass now spoils the area * Check Tracker - Basic Vanilla Support and fixes - Vanilla items show in check tracker, even if many won't auto check - Fixes an issue with Gerudo Fortress Keys - Refactors IsVisibleInCheckTracker to randomizer_check_tracjer * Check Tracker - Fixes merge conflict from 08a487f35e1d760ab9e69b932edbe4da6bcd9edb * Check Tracker - Fixes tracker loading as Floating when default value is set to Windowed * Randomizer Location Exclusion List - Adds Scrubs, Merchants, Shops. Adult Trade items, MQ Dungeons, and Bean Salesman - Fixes Kokiri Sword Chest, Weird Egg, and Frog Song Rupees from misbehaving - Updates Gerudo Fortress logic to be dependent on Open/Fast/Normal Fortress - Removes Invalid Check - Adds RO Enum for MQ settings - Updates magic values to RO enum * Location Exclusion List - Scrubs that are always randomized now always show on the list * For now, removes some extra code only needed for a Check Tracker. * Randomizer Location Exclusion List - Fixes Eyeball Frog from showing up when Adult Trade Items was off * Location Tracker - Updates Eyedrops to use RandomizerInf instead of Chest * Check Tracker - Fixes a bug where Link's Pocket item wasn't acting as checked for area item count * Check Tracker - Fixes a bug where Locations would incorrectly say Vanilla due to the first check in the area being a non-MQ check * Check Tracker - Fixes a crash when Check Tracker is docked but not visible * Check Tracker - Fixes areas being spoiled when loading different save files * Check Tracker - Only reset variables if they are initialized * Check Tracker - Cleans up old comments, unused variables, methods, and macros, adds language TODOs, * Check Tracker - Readds rainbow UI elements after Cosmetics Editor refactor * Check Tracker - Removal of flag lookup that was refactored, no longer needed header, and some trivial whitespace fixes. * Check Tracker - Refactors Index to Bitmask code to a macro * Check Tracker - Changes ordering from alphabetical to RandomizerCheck ordering * Check Tracker - Wraps everything in a namespace to avoid global variable usage * Check Tracker - Explicitly defines for-loop scope * Check Tracker - refactors dungeon lookup from static array to function * Fixes a bug when using EnhancementColor with alpha * Check Tracker - Refactors all colour to use direct conversions * Check Tracker - refactors settings loading and check visibility for readability * Check Tracker - Fixes a bug where the wrong label showed on the Check Tracker BG Color * UIWidgets - Adds Alpha bar to EnhancementColor when using alpha parameter --- soh/CMakeLists.txt | 2 + .../Enhancements/debugger/debugSaveEditor.h | 7 + .../randomizer/3drando/item_location.cpp | 88 +- .../randomizer/3drando/item_location.hpp | 115 +- .../randomizer/3drando/spoiler_log.hpp | 3 + .../Enhancements/randomizer/randomizer.cpp | 15 +- soh/soh/Enhancements/randomizer/randomizer.h | 3 + .../randomizer/randomizer_check_objects.cpp | 24 + .../randomizer/randomizer_check_objects.h | 3 + .../randomizer/randomizer_check_tracker.cpp | 1039 +++++++++++++++++ .../randomizer/randomizer_check_tracker.h | 33 + .../Enhancements/randomizer/randomizer_inf.h | 6 + soh/soh/GameMenuBar.cpp | 16 + soh/soh/OTRGlobals.cpp | 2 + soh/soh/UIWidgets.cpp | 9 +- soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c | 2 + soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c | 2 + soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c | 2 + .../overlays/actors/ovl_En_Toryo/z_en_toryo.c | 1 + 19 files changed, 1276 insertions(+), 96 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/CMakeLists.txt b/soh/CMakeLists.txt index c23c5d807..1994a2b99 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -196,6 +196,7 @@ set(Header_Files__soh__Enhancements__randomizer "soh/Enhancements/randomizer/randomizer_grotto.h" "soh/Enhancements/randomizer/randomizer_inf.h" "soh/Enhancements/randomizer/randomizer_item_tracker.h" + "soh/Enhancements/randomizer/randomizer_check_tracker.h" "soh/Enhancements/randomizer/adult_trade_shuffle.h" "soh/Enhancements/randomizer/randomizer_check_objects.h" "soh/Enhancements/randomizer/draw.h" @@ -327,6 +328,7 @@ set(Source_Files__soh__Enhancements__randomizer "soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp" "soh/Enhancements/randomizer/randomizer_grotto.c" "soh/Enhancements/randomizer/randomizer_item_tracker.cpp" + "soh/Enhancements/randomizer/randomizer_check_tracker.cpp" "soh/Enhancements/randomizer/adult_trade_shuffle.c" "soh/Enhancements/randomizer/randomizer_check_objects.cpp" "soh/Enhancements/randomizer/draw.cpp" diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.h b/soh/soh/Enhancements/debugger/debugSaveEditor.h index 97b2a5062..b857cc21c 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.h +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.h @@ -186,6 +186,7 @@ const std::vector flagTables = { { 0x18, "Obtained Farore's Wind" }, { 0x19, "Obtained Din's Fire" }, { 0x1A, "Obtained Nayru's Love" }, + { 0x1B, "Obtained Treasure Chest Game Reward" }, { 0x1C, "Obtained Grave-Dig Heart Piece" }, { 0x1D, "Obtained Bullet Bag Upgrade (Woods)" }, { 0x1E, "Obtained Deku Stick Upgrade (Stage)" }, @@ -488,5 +489,11 @@ const std::vector flagTables = { { RAND_INF_MERCHANTS_MEDIGORON, "RAND_INF_MERCHANTS_MEDIGORON" }, { RAND_INF_MERCHANTS_CARPET_SALESMAN, "RAND_INF_MERCHANTS_CARPET_SALESMAN" }, + + { RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO, "ADULT_TRADES_LW_TRADE_COJIRO" }, + { RAND_INF_ADULT_TRADES_GV_TRADE_SAW, "ADULT_TRADES_GV_TRADE_SAW" }, + { RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD, "ADULT_TRADES_DMT_TRADE_BROKEN_SWORD" }, + { RAND_INF_ADULT_TRADES_LH_TRADE_FROG, "ADULT_TRADES_LH_TRADE_FROG" }, + { RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS, "ADULT_TRADES_DMT_TRADE_EYEDROPS" }, } }, }; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp index b502dec18..dc4ca4701 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp @@ -11,6 +11,7 @@ //Location definitions static std::array locationTable; +static std::unordered_map locationLookupTable; void LocationTable_Init() { locationTable[NONE] = ItemLocation::Base (RC_UNKNOWN_CHECK, 0xFF, 0xFF, "Invalid Location", NONE, NONE, {}, SpoilerCollectionCheck::None()); @@ -25,7 +26,7 @@ void LocationTable_Init() { //Lost Woods locationTable[LW_NEAR_SHORTCUTS_GROTTO_CHEST] = ItemLocation::Chest (RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, 0x3E, 0x14, "LW Near Shortcuts Grotto Chest", LW_NEAR_SHORTCUTS_GROTTO_CHEST, BLUE_RUPEE, {Category::cLostWoods, Category::cForest, Category::cGrotto}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, 0x3E, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {Category::cLostWoods, Category::cForest,}, SpoilerCollectionCheck::ItemGetInf(30), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); - locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, 0x1F, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cLostWoods, Category::cForest, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(37), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); + locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, 0x1F, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cLostWoods, Category::cForest, Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(0x5B, 0x1F), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, 0x21, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, ODD_POTION, {Category::cLostWoods, Category::cForest, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(57), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, 0x76, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {Category::cLostWoods, Category::cForest, Category::cMinigame}, SpoilerCollectionCheck::ItemGetInf(31), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, 0x60, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {Category::cLostWoods, Category::cForest,}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); @@ -34,8 +35,8 @@ void LocationTable_Init() { locationTable[LW_DEKU_SCRUB_NEAR_BRIDGE] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_BRIDGE, 0x5B, 0x77, "LW Deku Scrub Near Bridge", LW_DEKU_SCRUB_NEAR_BRIDGE, PROGRESSIVE_STICK_UPGRADE, {Category::cLostWoods, Category::cForest, Category::cDekuScrub, Category::cDekuScrubUpgrades}, SpoilerCollectionCheck::Scrub(0x5B, 0x0A), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_GROTTO_REAR] = ItemLocation::GrottoScrub(RC_LW_DEKU_SCRUB_GROTTO_REAR, 0xF5, 0x33, "LW Deku Scrub Grotto Rear", LW_DEKU_SCRUB_GROTTO_REAR, BUY_DEKU_SEEDS_30, {Category::cLostWoods, Category::cForest, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x1F, 0x04), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[LW_DEKU_SCRUB_GROTTO_FRONT] = ItemLocation::GrottoScrub(RC_LW_DEKU_SCRUB_GROTTO_FRONT, 0xF5, 0x79, "LW Deku Scrub Grotto Front", LW_DEKU_SCRUB_GROTTO_FRONT, PROGRESSIVE_NUT_UPGRADE, {Category::cLostWoods, Category::cForest, Category::cDekuScrub, Category::cDekuScrubUpgrades, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x1F, 0x0B), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); - locationTable[DEKU_THEATER_SKULL_MASK] = ItemLocation::Base (RC_DEKU_THEATER_SKULL_MASK, 0x3E, 0x77, "Deku Theater Skull Mask", DEKU_THEATER_SKULL_MASK, PROGRESSIVE_STICK_UPGRADE, {Category::cLostWoods, Category::cForest, Category::cGrotto}, SpoilerCollectionCheck::ItemGetInf(22), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); - locationTable[DEKU_THEATER_MASK_OF_TRUTH] = ItemLocation::Base (RC_DEKU_THEATER_MASK_OF_TRUTH, 0x3E, 0x7A, "Deku Theater Mask of Truth", DEKU_THEATER_MASK_OF_TRUTH, PROGRESSIVE_NUT_UPGRADE, {Category::cLostWoods, Category::cForest, Category::cNeedSpiritualStones, Category::cGrotto}, SpoilerCollectionCheck::ItemGetInf(23), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); + locationTable[DEKU_THEATER_SKULL_MASK] = ItemLocation::Base (RC_DEKU_THEATER_SKULL_MASK, 0x3E, 0x77, "Deku Theater Skull Mask", DEKU_THEATER_SKULL_MASK, PROGRESSIVE_STICK_UPGRADE, {Category::cLostWoods, Category::cForest, Category::cGrotto}, SpoilerCollectionCheck::Chest(0x3E, 0x1F), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); + locationTable[DEKU_THEATER_MASK_OF_TRUTH] = ItemLocation::Base (RC_DEKU_THEATER_MASK_OF_TRUTH, 0x3E, 0x7A, "Deku Theater Mask of Truth", DEKU_THEATER_MASK_OF_TRUTH, PROGRESSIVE_NUT_UPGRADE, {Category::cLostWoods, Category::cForest, Category::cNeedSpiritualStones, Category::cGrotto}, SpoilerCollectionCheck::Chest(0x3E, 0x1E), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); //Sacred Forest Meadow locationTable[SFM_WOLFOS_GROTTO_CHEST] = ItemLocation::Chest (RC_SFM_WOLFOS_GROTTO_CHEST, 0x3E, 0x11, "SFM Wolfos Grotto Chest", SFM_WOLFOS_GROTTO_CHEST, PURPLE_RUPEE, {Category::cSacredForestMeadow, Category::cForest, Category::cGrotto}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); @@ -54,9 +55,9 @@ void LocationTable_Init() { locationTable[LH_CHILD_FISHING] = ItemLocation::Base (RC_LH_CHILD_FISHING, 0x49, 0x3E, "LH Child Fishing", LH_CHILD_FISHING, PIECE_OF_HEART, {Category::cLakeHylia, Category::cMinigame,}, SpoilerCollectionCheck::Fishing(0x02), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_ADULT_FISHING] = ItemLocation::Base (RC_LH_ADULT_FISHING, 0x49, 0x38, "LH Adult Fishing", LH_ADULT_FISHING, PROGRESSIVE_SCALE, {Category::cLakeHylia, Category::cMinigame,}, SpoilerCollectionCheck::Fishing(0x03), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, 0x3E, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {Category::cLakeHylia,}, SpoilerCollectionCheck::ItemGetInf(24), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); - locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, 0x25, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cLakeHylia, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(61), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); + locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, 0x25, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cLakeHylia, Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(0x38, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_UNDERWATER_ITEM] = ItemLocation::Base (RC_LH_UNDERWATER_ITEM, 0x57, 0x15, "LH Underwater Item", LH_UNDERWATER_ITEM, RUTOS_LETTER, {Category::cLakeHylia,}, SpoilerCollectionCheck::EventChkInf(0x31), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); - locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, 0x58, "LH Sun", LH_SUN, FIRE_ARROWS, {Category::cLakeHylia,}, SpoilerCollectionCheck::Chest(0x57, 0x00), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); + locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, 0x58, "LH Sun", LH_SUN, FIRE_ARROWS, {Category::cLakeHylia,}, SpoilerCollectionCheck::Chest(0x57, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_FREESTANDING_POH] = ItemLocation::Collectable(RC_LH_FREESTANDING_POH, 0x57, 0x1E, "LH Freestanding PoH", LH_FREESTANDING_POH, PIECE_OF_HEART, {Category::cLakeHylia,}, SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_LH_DEKU_SCRUB_GROTTO_LEFT, 0xEF, 0x30, "LH Deku Scrub Grotto Left", LH_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cLakeHylia, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x19, 0x01), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); locationTable[LH_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_LH_DEKU_SCRUB_GROTTO_RIGHT, 0xEF, 0x37, "LH Deku Scrub Grotto Right", LH_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cLakeHylia, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x19, 0x06), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA); @@ -64,7 +65,7 @@ void LocationTable_Init() { //Gerudo Valley locationTable[GV_CHEST] = ItemLocation::Chest (RC_GV_CHEST, 0x5A, 0x00, "GV Chest", GV_CHEST, PURPLE_RUPEE, {Category::cGerudoValley, Category::cGerudo,}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); - locationTable[GV_TRADE_SAW] = ItemLocation::Base (RC_GV_TRADE_SAW, 0x5A, 0x22, "GV Trade Saw", GV_TRADE_SAW, BROKEN_SWORD, {Category::cGerudoValley, Category::cGerudo, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(58), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); + locationTable[GV_TRADE_SAW] = ItemLocation::Base (RC_GV_TRADE_SAW, 0x5A, 0x22, "GV Trade Saw", GV_TRADE_SAW, BROKEN_SWORD, {Category::cGerudoValley, Category::cGerudo, Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(0x5A, 0x1F), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GV_WATERFALL_FREESTANDING_POH] = ItemLocation::Collectable(RC_GV_WATERFALL_FREESTANDING_POH, 0x5A, 0x01, "GV Waterfall Freestanding PoH", GV_WATERFALL_FREESTANDING_POH, PIECE_OF_HEART, {Category::cGerudoValley, Category::cGerudo,}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GV_CRATE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GV_CRATE_FREESTANDING_POH, 0x5A, 0x02, "GV Crate Freestanding PoH", GV_CRATE_FREESTANDING_POH, PIECE_OF_HEART, {Category::cGerudoValley, Category::cGerudo,}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[GV_DEKU_SCRUB_GROTTO_REAR] = ItemLocation::GrottoScrub(RC_GV_DEKU_SCRUB_GROTTO_REAR, 0xF0, 0x39, "GV Deku Scrub Grotto Rear", GV_DEKU_SCRUB_GROTTO_REAR, BUY_RED_POTION_30, {Category::cGerudoValley, Category::cGerudo, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x1A, 0x08), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); @@ -82,7 +83,7 @@ void LocationTable_Init() { //Haunted Wasteland locationTable[WASTELAND_CHEST] = ItemLocation::Chest (RC_WASTELAND_CHEST, 0x5E, 0x00, "Wasteland Chest", WASTELAND_CHEST, PURPLE_RUPEE, {Category::cHauntedWasteland,}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); - locationTable[WASTELAND_BOMBCHU_SALESMAN] = ItemLocation::Base (RC_WASTELAND_BOMBCHU_SALESMAN, 0x5E, 0x03, "Wasteland Carpet Salesman", WASTELAND_BOMBCHU_SALESMAN, BOMBCHU_10, {Category::cHauntedWasteland, Category::cMerchant,}, SpoilerCollectionCheck::EventChkInf(0x34), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); + locationTable[WASTELAND_BOMBCHU_SALESMAN] = ItemLocation::Base (RC_WASTELAND_BOMBCHU_SALESMAN, 0x5E, 0x03, "Wasteland Carpet Salesman", WASTELAND_BOMBCHU_SALESMAN, BOMBCHU_10, {Category::cHauntedWasteland, Category::cMerchant,}, SpoilerCollectionCheck::Merchant(0x5E, 0x86), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); //Desert Colossus locationTable[COLOSSUS_FREESTANDING_POH] = ItemLocation::Collectable(RC_COLOSSUS_FREESTANDING_POH, 0x5C, 0x0D, "Colossus Freestanding PoH", COLOSSUS_FREESTANDING_POH, PIECE_OF_HEART, {Category::cDesertColossus,}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); @@ -116,7 +117,7 @@ void LocationTable_Init() { locationTable[KAK_40_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_40_GOLD_SKULLTULA_REWARD, 0x50, 0x03, "Kak 40 Gold Skulltula Reward", KAK_40_GOLD_SKULLTULA_REWARD, BOMBCHU_10, {Category::cKakarikoVillage, Category::cKakariko, Category::cSkulltulaHouse}, SpoilerCollectionCheck::EventChkInf(0xDD), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_50_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_50_GOLD_SKULLTULA_REWARD, 0x50, 0x3E, "Kak 50 Gold Skulltula Reward", KAK_50_GOLD_SKULLTULA_REWARD, PIECE_OF_HEART, {Category::cKakarikoVillage, Category::cKakariko, Category::cSkulltulaHouse}, SpoilerCollectionCheck::EventChkInf(0xDE), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, 0x3E, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {Category::cKakarikoVillage, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); - locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, 0x30, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::ItemGetInf(6), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); + locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, 0x30, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, 0x20, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cKakarikoVillage, Category::cKakariko, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(56), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, 0x1D, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {Category::cKakarikoVillage, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(36), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, 0x0F, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::ItemGetInf(4), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); @@ -131,14 +132,14 @@ void LocationTable_Init() { locationTable[GRAVEYARD_HOOKSHOT_CHEST] = ItemLocation::Chest (RC_GRAVEYARD_HOOKSHOT_CHEST, 0x48, 0x00, "GY Hookshot Chest", GRAVEYARD_HOOKSHOT_CHEST, PROGRESSIVE_HOOKSHOT, {Category::cGraveyard, Category::cKakariko,}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_DAMPE_RACE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, 0x48, 0x07, "GY Dampe Race Freestanding PoH", GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, PIECE_OF_HEART, {Category::cGraveyard, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[GRAVEYARD_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_FREESTANDING_POH, 0x53, 0x04, "GY Freestanding PoH", GRAVEYARD_FREESTANDING_POH, PIECE_OF_HEART, {Category::cGraveyard, Category::cKakariko,}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO); - locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, 0x08, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {Category::cGraveyard, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(20), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); + locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, 0x08, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {Category::cGraveyard, Category::cKakariko,}, SpoilerCollectionCheck::Gravedigger(0x53, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); //Death Mountain locationTable[DMT_CHEST] = ItemLocation::Chest (RC_DMT_CHEST, 0x60, 0x01, "DMT Chest", DMT_CHEST, PURPLE_RUPEE, {Category::cDeathMountainTrail, Category::cDeathMountain,}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); locationTable[DMT_STORMS_GROTTO_CHEST] = ItemLocation::Chest (RC_DMT_STORMS_GROTTO_CHEST, 0x3E, 0x17, "DMT Storms Grotto Chest", DMT_STORMS_GROTTO_CHEST, HUGE_RUPEE, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cGrotto}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); - locationTable[DMT_TRADE_BROKEN_SWORD] = ItemLocation::Base (RC_DMT_TRADE_BROKEN_SWORD, 0x60, 0x23, "DMT Trade Broken Sword", DMT_TRADE_BROKEN_SWORD, PRESCRIPTION, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cAdultTrade}, SpoilerCollectionCheck::Biggoron(0x4), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); - locationTable[DMT_TRADE_EYEDROPS] = ItemLocation::Base (RC_DMT_TRADE_EYEDROPS, 0x60, 0x26, "DMT Trade Eyedrops", DMT_TRADE_EYEDROPS, CLAIM_CHECK, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cAdultTrade}, SpoilerCollectionCheck::Biggoron(0x2), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); - locationTable[DMT_TRADE_CLAIM_CHECK] = ItemLocation::Base (RC_DMT_TRADE_CLAIM_CHECK, 0x60, 0x57, "DMT Trade Claim Check", DMT_TRADE_CLAIM_CHECK, BIGGORON_SWORD, {Category::cDeathMountainTrail, Category::cDeathMountain}, SpoilerCollectionCheck::Biggoron(0x1), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); + locationTable[DMT_TRADE_BROKEN_SWORD] = ItemLocation::Base (RC_DMT_TRADE_BROKEN_SWORD, 0x60, 0x23, "DMT Trade Broken Sword", DMT_TRADE_BROKEN_SWORD, PRESCRIPTION, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(0x60, 0x1D), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); + locationTable[DMT_TRADE_EYEDROPS] = ItemLocation::Base (RC_DMT_TRADE_EYEDROPS, 0x60, 0x26, "DMT Trade Eyedrops", DMT_TRADE_EYEDROPS, CLAIM_CHECK, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(0x60, 0x1E), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); + locationTable[DMT_TRADE_CLAIM_CHECK] = ItemLocation::Base (RC_DMT_TRADE_CLAIM_CHECK, 0x60, 0x57, "DMT Trade Claim Check", DMT_TRADE_CLAIM_CHECK, BIGGORON_SWORD, {Category::cDeathMountainTrail, Category::cDeathMountain}, SpoilerCollectionCheck::Chest(0x60, 0x1F), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); locationTable[DMT_FREESTANDING_POH] = ItemLocation::Collectable(RC_DMT_FREESTANDING_POH, 0x60, 0x1E, "DMT Freestanding PoH", DMT_FREESTANDING_POH, PIECE_OF_HEART, {Category::cDeathMountainTrail, Category::cDeathMountain,}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); //Goron City @@ -147,12 +148,12 @@ void LocationTable_Init() { locationTable[GC_MAZE_CENTER_CHEST] = ItemLocation::Chest (RC_GC_MAZE_CENTER_CHEST, 0x62, 0x02, "GC Maze Center Chest", GC_MAZE_CENTER_CHEST, PURPLE_RUPEE, {Category::cGoronCity,}, SpoilerCollectionCheckGroup::GROUP_GORON_CITY); locationTable[GC_ROLLING_GORON_AS_CHILD] = ItemLocation::Base (RC_GC_ROLLING_GORON_AS_CHILD, 0x62, 0x34, "GC Rolling Goron as Child", GC_ROLLING_GORON_AS_CHILD, PROGRESSIVE_BOMB_BAG, {Category::cGoronCity,}, SpoilerCollectionCheck::InfTable(0x11, 0x06), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); locationTable[GC_ROLLING_GORON_AS_ADULT] = ItemLocation::Base (RC_GC_ROLLING_GORON_AS_ADULT, 0x62, 0x2C, "GC Rolling Goron as Adult", GC_ROLLING_GORON_AS_ADULT, GORON_TUNIC, {Category::cGoronCity,}, SpoilerCollectionCheck::InfTable(0x10, 0x01), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); - locationTable[GC_DARUNIAS_JOY] = ItemLocation::Base (RC_GC_DARUNIAS_JOY, 0x62, 0x54, "GC Darunias Joy", GC_DARUNIAS_JOY, PROGRESSIVE_STRENGTH, {Category::cGoronCity,}, SpoilerCollectionCheck::EventChkInf(0x36), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); + locationTable[GC_DARUNIAS_JOY] = ItemLocation::Base (RC_GC_DARUNIAS_JOY, 0x62, 0x54, "GC Darunias Joy", GC_DARUNIAS_JOY, PROGRESSIVE_STRENGTH, {Category::cGoronCity,}, SpoilerCollectionCheck::Chest(0x62, 0x1E), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); locationTable[GC_POT_FREESTANDING_POH] = ItemLocation::Collectable(RC_GC_POT_FREESTANDING_POH, 0x62, 0x1F, "GC Pot Freestanding PoH", GC_POT_FREESTANDING_POH, PIECE_OF_HEART, {Category::cGoronCity,}, SpoilerCollectionCheckGroup::GROUP_GORON_CITY); locationTable[GC_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_GC_DEKU_SCRUB_GROTTO_LEFT, 0xFB, 0x30, "GC Deku Scrub Grotto Left", GC_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cGoronCity, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x25, 0x01), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); locationTable[GC_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_GC_DEKU_SCRUB_GROTTO_RIGHT, 0xFB, 0x37, "GC Deku Scrub Grotto Right", GC_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cGoronCity, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x25, 0x06), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); locationTable[GC_DEKU_SCRUB_GROTTO_CENTER] = ItemLocation::GrottoScrub(RC_GC_DEKU_SCRUB_GROTTO_CENTER, 0xFB, 0x33, "GC Deku Scrub Grotto Center", GC_DEKU_SCRUB_GROTTO_CENTER, BUY_ARROWS_30, {Category::cGoronCity, Category::cDekuScrub, Category::cGrotto}, SpoilerCollectionCheck::Scrub(0x25, 0x04), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); - locationTable[GC_MEDIGORON] = ItemLocation::Base (RC_GC_MEDIGORON, 0x62, 0x51, "GC Medigoron", GC_MEDIGORON, GIANTS_KNIFE, {Category::cGoronCity, Category::cMerchant,}, SpoilerCollectionCheck::EventChkInf(0x35), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); + locationTable[GC_MEDIGORON] = ItemLocation::Base (RC_GC_MEDIGORON, 0x62, 0x51, "GC Medigoron", GC_MEDIGORON, GIANTS_KNIFE, {Category::cGoronCity, Category::cMerchant,}, SpoilerCollectionCheck::Merchant(0x62, 0x87), SpoilerCollectionCheckGroup::GROUP_GORON_CITY); //Death Mountain Crater locationTable[DMC_UPPER_GROTTO_CHEST] = ItemLocation::Chest (RC_DMC_UPPER_GROTTO_CHEST, 0x3E, 0x1A, "DMC Upper Grotto Chest", DMC_UPPER_GROTTO_CHEST, BOMBS_20, {Category::cDeathMountainCrater, Category::cDeathMountain, Category::cGrotto}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); @@ -166,11 +167,11 @@ void LocationTable_Init() { //Zoras River locationTable[ZR_OPEN_GROTTO_CHEST] = ItemLocation::Chest (RC_ZR_OPEN_GROTTO_CHEST, 0x3E, 0x09, "ZR Open Grotto Chest", ZR_OPEN_GROTTO_CHEST, RED_RUPEE, {Category::cZorasRiver, Category::cGrotto,}, SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); locationTable[ZR_MAGIC_BEAN_SALESMAN] = ItemLocation::Base (RC_ZR_MAGIC_BEAN_SALESMAN, 0x54, 0x16, "ZR Magic Bean Salesman", ZR_MAGIC_BEAN_SALESMAN, MAGIC_BEAN, {Category::cZorasRiver,}, SpoilerCollectionCheck::MagicBeans(0x54, 0x01), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); - locationTable[ZR_FROGS_ZELDAS_LULLABY] = ItemLocation::Base (RC_ZR_FROGS_ZELDAS_LULLABY, 0x54, 0x3E, "ZR Frogs Zelda's Lullaby", ZR_FROGS_ZELDAS_LULLABY, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); - locationTable[ZR_FROGS_EPONAS_SONG] = ItemLocation::Base (RC_ZR_FROGS_EPONAS_SONG, 0x54, 0x3E, "ZR Frogs Epona's Song", ZR_FROGS_EPONAS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); - locationTable[ZR_FROGS_SARIAS_SONG] = ItemLocation::Base (RC_ZR_FROGS_SARIAS_SONG, 0x54, 0x3E, "ZR Frogs Saria's Song", ZR_FROGS_SARIAS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); - locationTable[ZR_FROGS_SUNS_SONG] = ItemLocation::Base (RC_ZR_FROGS_SUNS_SONG, 0x54, 0x3E, "ZR Frogs Sun's Song", ZR_FROGS_SUNS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); - locationTable[ZR_FROGS_SONG_OF_TIME] = ItemLocation::Base (RC_ZR_FROGS_SONG_OF_TIME, 0x54, 0x3E, "ZR Frogs Song of Time", ZR_FROGS_SONG_OF_TIME, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); + locationTable[ZR_FROGS_ZELDAS_LULLABY] = ItemLocation::Base (RC_ZR_FROGS_ZELDAS_LULLABY, 0x54, 0x3E, "ZR Frogs Zelda's Lullaby", ZR_FROGS_ZELDAS_LULLABY, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD1), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); + locationTable[ZR_FROGS_EPONAS_SONG] = ItemLocation::Base (RC_ZR_FROGS_EPONAS_SONG, 0x54, 0x3E, "ZR Frogs Epona's Song", ZR_FROGS_EPONAS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD2), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); + locationTable[ZR_FROGS_SARIAS_SONG] = ItemLocation::Base (RC_ZR_FROGS_SARIAS_SONG, 0x54, 0x3E, "ZR Frogs Saria's Song", ZR_FROGS_SARIAS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD4), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); + locationTable[ZR_FROGS_SUNS_SONG] = ItemLocation::Base (RC_ZR_FROGS_SUNS_SONG, 0x54, 0x3E, "ZR Frogs Sun's Song", ZR_FROGS_SUNS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD3), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); + locationTable[ZR_FROGS_SONG_OF_TIME] = ItemLocation::Base (RC_ZR_FROGS_SONG_OF_TIME, 0x54, 0x3E, "ZR Frogs Song of Time", ZR_FROGS_SONG_OF_TIME, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD5), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); locationTable[ZR_FROGS_IN_THE_RAIN] = ItemLocation::Base (RC_ZR_FROGS_IN_THE_RAIN, 0x54, 0x3E, "ZR Frogs in the Rain", ZR_FROGS_IN_THE_RAIN, PIECE_OF_HEART, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); locationTable[ZR_FROGS_OCARINA_GAME] = ItemLocation::Base (RC_ZR_FROGS_OCARINA_GAME, 0x54, 0x76, "ZR Frogs Ocarina Game", ZR_FROGS_OCARINA_GAME, PIECE_OF_HEART, {Category::cZorasRiver, Category::cMinigame,}, SpoilerCollectionCheck::EventChkInf(0xD0), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); locationTable[ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH, 0x54, 0x04, "ZR Near Open Grotto Freestanding PoH", ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH, PIECE_OF_HEART, {Category::cZorasRiver,}, SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER); @@ -182,7 +183,7 @@ void LocationTable_Init() { locationTable[ZD_CHEST] = ItemLocation::Chest (RC_ZD_CHEST, 0x58, 0x00, "ZD Chest", ZD_CHEST, PIECE_OF_HEART, {Category::cZorasDomain,}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); locationTable[ZD_DIVING_MINIGAME] = ItemLocation::Base (RC_ZD_DIVING_MINIGAME, 0x58, 0x37, "ZD Diving Minigame", ZD_DIVING_MINIGAME, PROGRESSIVE_SCALE, {Category::cZorasDomain, Category::cMinigame,}, SpoilerCollectionCheck::EventChkInf(0x38), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); locationTable[ZD_KING_ZORA_THAWED] = ItemLocation::Base (RC_ZD_KING_ZORA_THAWED, 0x58, 0x2D, "ZD King Zora Thawed", ZD_KING_ZORA_THAWED, ZORA_TUNIC, {Category::cZorasDomain,}, SpoilerCollectionCheck::InfTable(0x13, 0x01), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); - locationTable[ZD_TRADE_PRESCRIPTION] = ItemLocation::Base (RC_ZD_TRADE_PRESCRIPTION, 0x58, 0x24, "ZD Trade Prescription", ZD_TRADE_PRESCRIPTION, EYEBALL_FROG, {Category::cZorasDomain, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(60), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); + locationTable[ZD_TRADE_PRESCRIPTION] = ItemLocation::Base (RC_ZD_TRADE_PRESCRIPTION, 0x58, 0x24, "ZD Trade Prescription", ZD_TRADE_PRESCRIPTION, EYEBALL_FROG, {Category::cZorasDomain, Category::cAdultTrade}, SpoilerCollectionCheck::Chest(0x58, 0x1F), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); //Zoras Fountain locationTable[ZF_ICEBERG_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZF_ICEBERC_FREESTANDING_POH, 0x59, 0x01, "ZF Iceberg Freestanding PoH", ZF_ICEBERG_FREESTANDING_POH, PIECE_OF_HEART, {Category::cZorasFountain,}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); @@ -726,14 +727,14 @@ void LocationTable_Init() { -------------------------------*/ locationTable[LINKS_POCKET] = ItemLocation::Reward (RC_LINKS_POCKET, 0xFF, 0xFF, "Link's Pocket", LINKS_POCKET, LIGHT_MEDALLION, {}, SpoilerCollectionCheck::AlwaysCollected(), SpoilerCollectionCheckGroup::GROUP_KOKIRI_FOREST); - locationTable[QUEEN_GOHMA] = ItemLocation::Reward (RC_QUEEN_GOHMA, 0xFF, DUNGEON_DEKU_TREE, "Queen Gohma", QUEEN_GOHMA, KOKIRI_EMERALD, {}, SpoilerCollectionCheck::EventChkInf(0x09), SpoilerCollectionCheckGroup::GROUP_DUNGEON_DEKU_TREE); - locationTable[KING_DODONGO] = ItemLocation::Reward (RC_KING_DODONGO, 0xFF, DUNGEON_DODONGOS_CAVERN, "King Dodongo", KING_DODONGO, GORON_RUBY, {}, SpoilerCollectionCheck::EventChkInf(0x25), SpoilerCollectionCheckGroup::GROUP_DUNGEON_DODONGOS_CAVERN); - locationTable[BARINADE] = ItemLocation::Reward (RC_BARINADE, 0xFF, DUNGEON_JABUJABUS_BELLY, "Barinade", BARINADE, ZORA_SAPPHIRE, {}, SpoilerCollectionCheck::EventChkInf(0x37), SpoilerCollectionCheckGroup::GROUP_DUNGEON_JABUJABUS_BELLY); - locationTable[PHANTOM_GANON] = ItemLocation::Reward (RC_PHANTOM_GANON, 0xFF, DUNGEON_FOREST_TEMPLE, "Phantom Ganon", PHANTOM_GANON, FOREST_MEDALLION, {}, SpoilerCollectionCheck::EventChkInf(0x48), SpoilerCollectionCheckGroup::GROUP_DUNGEON_FOREST_TEMPLE); - locationTable[VOLVAGIA] = ItemLocation::Reward (RC_VOLVAGIA, 0xFF, DUNGEON_FIRE_TEMPLE, "Volvagia", VOLVAGIA, FIRE_MEDALLION, {}, SpoilerCollectionCheck::EventChkInf(0x49), SpoilerCollectionCheckGroup::GROUP_DUNGEON_FIRE_TEMPLE); - locationTable[MORPHA] = ItemLocation::Reward (RC_MORPHA, 0xFF, DUNGEON_WATER_TEMPLE, "Morpha", MORPHA, WATER_MEDALLION, {}, SpoilerCollectionCheck::EventChkInf(0x4A), SpoilerCollectionCheckGroup::GROUP_DUNGEON_WATER_TEMPLE); - locationTable[TWINROVA] = ItemLocation::Reward (RC_TWINROVA, 0xFF, DUNGEON_SPIRIT_TEMPLE, "Twinrova", TWINROVA, SPIRIT_MEDALLION, {}, SpoilerCollectionCheck::EventChkInf(0x47), SpoilerCollectionCheckGroup::GROUP_DUNGEON_SPIRIT_TEMPLE); - locationTable[BONGO_BONGO] = ItemLocation::Reward (RC_BONGO_BONGO, 0xFF, DUNGEON_SHADOW_TEMPLE, "Bongo Bongo", BONGO_BONGO, SHADOW_MEDALLION, {}, SpoilerCollectionCheck::EventChkInf(0x46), SpoilerCollectionCheckGroup::GROUP_DUNGEON_SHADOW_TEMPLE); + locationTable[QUEEN_GOHMA] = ItemLocation::Reward (RC_QUEEN_GOHMA, 0xFF, DUNGEON_DEKU_TREE, "Queen Gohma", QUEEN_GOHMA, KOKIRI_EMERALD, {}, SpoilerCollectionCheck::Chest(0x11, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_DEKU_TREE); + locationTable[KING_DODONGO] = ItemLocation::Reward (RC_KING_DODONGO, 0xFF, DUNGEON_DODONGOS_CAVERN, "King Dodongo", KING_DODONGO, GORON_RUBY, {}, SpoilerCollectionCheck::Chest(0x12, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_DODONGOS_CAVERN); + locationTable[BARINADE] = ItemLocation::Reward (RC_BARINADE, 0xFF, DUNGEON_JABUJABUS_BELLY, "Barinade", BARINADE, ZORA_SAPPHIRE, {}, SpoilerCollectionCheck::Chest(0x13, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_JABUJABUS_BELLY); + locationTable[PHANTOM_GANON] = ItemLocation::Reward (RC_PHANTOM_GANON, 0xFF, DUNGEON_FOREST_TEMPLE, "Phantom Ganon", PHANTOM_GANON, FOREST_MEDALLION, {}, SpoilerCollectionCheck::Chest(0x14, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_FOREST_TEMPLE); + locationTable[VOLVAGIA] = ItemLocation::Reward (RC_VOLVAGIA, 0xFF, DUNGEON_FIRE_TEMPLE, "Volvagia", VOLVAGIA, FIRE_MEDALLION, {}, SpoilerCollectionCheck::Chest(0x15, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_FIRE_TEMPLE); + locationTable[MORPHA] = ItemLocation::Reward (RC_MORPHA, 0xFF, DUNGEON_WATER_TEMPLE, "Morpha", MORPHA, WATER_MEDALLION, {}, SpoilerCollectionCheck::Chest(0x16, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_WATER_TEMPLE); + locationTable[TWINROVA] = ItemLocation::Reward (RC_TWINROVA, 0xFF, DUNGEON_SPIRIT_TEMPLE, "Twinrova", TWINROVA, SPIRIT_MEDALLION, {}, SpoilerCollectionCheck::Chest(0x17, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_SPIRIT_TEMPLE); + locationTable[BONGO_BONGO] = ItemLocation::Reward (RC_BONGO_BONGO, 0xFF, DUNGEON_SHADOW_TEMPLE, "Bongo Bongo", BONGO_BONGO, SHADOW_MEDALLION, {}, SpoilerCollectionCheck::Chest(0x18, 0x1F), SpoilerCollectionCheckGroup::GROUP_DUNGEON_SHADOW_TEMPLE); locationTable[GANON] = ItemLocation::Reward (RC_GANON, 0xFF, 0xF0, "Ganon", NONE, TRIFORCE, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_DUNGEON_GANONS_CASTLE); /*------------------------------- @@ -753,26 +754,26 @@ void LocationTable_Init() { --- CUTSCENES --- -------------------------------*/ - locationTable[TOT_LIGHT_ARROWS_CUTSCENE] = ItemLocation::Delayed(RC_TOT_LIGHT_ARROWS_CUTSCENE, 0xFF, 0x01, "ToT Light Arrow Cutscene", TOT_LIGHT_ARROWS_CUTSCENE, LIGHT_ARROWS, {Category::cTempleOfTime, Category::cMarket}, SpoilerCollectionCheck::EventChkInf(0xC4), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); + locationTable[TOT_LIGHT_ARROWS_CUTSCENE] = ItemLocation::Delayed(RC_TOT_LIGHT_ARROWS_CUTSCENE, 0xFF, 0x01, "ToT Light Arrow Cutscene", TOT_LIGHT_ARROWS_CUTSCENE, LIGHT_ARROWS, {Category::cTempleOfTime, Category::cMarket}, SpoilerCollectionCheck::Chest(0x43, 0x1E), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[LW_GIFT_FROM_SARIA] = ItemLocation::Delayed(RC_LW_GIFT_FROM_SARIA, 0xFF, 0x02, "LW Gift From Saria", LW_GIFT_FROM_SARIA, PROGRESSIVE_OCARINA, {Category::cLostWoods, Category::cForest}, SpoilerCollectionCheck::EventChkInf(0xC1), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); - locationTable[ZF_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_ZF_GREAT_FAIRY_REWARD, 0xFF, 0x10, "ZF Great Fairy Reward", ZF_GREAT_FAIRY_REWARD, FARORES_WIND, {Category::cZorasFountain, Category::cFairies}, SpoilerCollectionCheck::ItemGetInf(16), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); - locationTable[HC_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_HC_GREAT_FAIRY_REWARD, 0xFF, 0x11, "HC Great Fairy Reward", HC_GREAT_FAIRY_REWARD, DINS_FIRE, {Category::cHyruleCastle, Category::cMarket, Category::cFairies}, SpoilerCollectionCheck::ItemGetInf(17), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); - locationTable[COLOSSUS_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_COLOSSUS_GREAT_FAIRY_REWARD, 0xFF, 0x12, "Colossus Great Fairy Reward", COLOSSUS_GREAT_FAIRY_REWARD, NAYRUS_LOVE, {Category::cDesertColossus, Category::cFairies}, SpoilerCollectionCheck::ItemGetInf(18), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); - locationTable[DMT_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_DMT_GREAT_FAIRY_REWARD, 0xFF, 0x13, "DMT Great Fairy Reward", DMT_GREAT_FAIRY_REWARD, PROGRESSIVE_MAGIC_METER, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3B, 0x00), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); - locationTable[DMC_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_DMC_GREAT_FAIRY_REWARD, 0xFF, 0x14, "DMC Great Fairy Reward", DMC_GREAT_FAIRY_REWARD, PROGRESSIVE_MAGIC_METER, {Category::cDeathMountainCrater, Category::cDeathMountain, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3B, 0x01), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); - locationTable[OGC_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_OGC_GREAT_FAIRY_REWARD, 0xFF, 0x15, "OGC Great Fairy Reward", OGC_GREAT_FAIRY_REWARD, DOUBLE_DEFENSE, {Category::cOutsideGanonsCastle, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3B, 0x02), SpoilerCollectionCheckGroup::GROUP_DUNGEON_GANONS_CASTLE); + locationTable[ZF_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_ZF_GREAT_FAIRY_REWARD, 0xFF, 0x10, "ZF Great Fairy Reward", ZF_GREAT_FAIRY_REWARD, FARORES_WIND, {Category::cZorasFountain, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3D, 0x01), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN); + locationTable[HC_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_HC_GREAT_FAIRY_REWARD, 0xFF, 0x11, "HC Great Fairy Reward", HC_GREAT_FAIRY_REWARD, DINS_FIRE, {Category::cHyruleCastle, Category::cMarket, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3D, 0x02), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); + locationTable[COLOSSUS_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_COLOSSUS_GREAT_FAIRY_REWARD, 0xFF, 0x12, "Colossus Great Fairy Reward", COLOSSUS_GREAT_FAIRY_REWARD, NAYRUS_LOVE, {Category::cDesertColossus, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3D, 0x03), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); + locationTable[DMT_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_DMT_GREAT_FAIRY_REWARD, 0xFF, 0x13, "DMT Great Fairy Reward", DMT_GREAT_FAIRY_REWARD, PROGRESSIVE_MAGIC_METER, {Category::cDeathMountainTrail, Category::cDeathMountain, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3B, 0x01), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); + locationTable[DMC_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_DMC_GREAT_FAIRY_REWARD, 0xFF, 0x14, "DMC Great Fairy Reward", DMC_GREAT_FAIRY_REWARD, PROGRESSIVE_MAGIC_METER, {Category::cDeathMountainCrater, Category::cDeathMountain, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3B, 0x02), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); + locationTable[OGC_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_OGC_GREAT_FAIRY_REWARD, 0xFF, 0x15, "OGC Great Fairy Reward", OGC_GREAT_FAIRY_REWARD, DOUBLE_DEFENSE, {Category::cOutsideGanonsCastle, Category::cFairies}, SpoilerCollectionCheck::Chest(0x3B, 0x03), SpoilerCollectionCheckGroup::GROUP_DUNGEON_GANONS_CASTLE); locationTable[SHEIK_IN_FOREST] = ItemLocation::Delayed(RC_SHEIK_IN_FOREST, 0xFF, 0x20, "Sheik in Forest", SHEIK_IN_FOREST, MINUET_OF_FOREST, {Category::cSacredForestMeadow, Category::cForest, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x50), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); locationTable[SHEIK_IN_CRATER] = ItemLocation::Delayed(RC_SHEIK_IN_CRATER, 0xFF, 0x21, "Sheik in Crater", SHEIK_IN_CRATER, BOLERO_OF_FIRE, {Category::cDeathMountainCrater, Category::cDeathMountain, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x51), SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN); locationTable[SHEIK_IN_ICE_CAVERN] = ItemLocation::Delayed(RC_SHEIK_IN_ICE_CAVERN, 0xFF, 0x22, "Sheik in Ice Cavern", SHEIK_IN_ICE_CAVERN, SERENADE_OF_WATER, {Category::cIceCavern, Category::cSong, Category::cSongDungeonReward}, SpoilerCollectionCheck::EventChkInf(0x52), SpoilerCollectionCheckGroup::GROUP_DUNGEON_ICE_CAVERN); locationTable[SHEIK_AT_COLOSSUS] = ItemLocation::Delayed(RC_SHEIK_AT_COLOSSUS, 0xFF, 0x23, "Sheik at Colossus", SHEIK_AT_COLOSSUS, REQUIEM_OF_SPIRIT, {Category::cDesertColossus, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0xAC), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY); locationTable[SHEIK_IN_KAKARIKO] = ItemLocation::Delayed(RC_SHEIK_IN_KAKARIKO, 0xFF, 0x24, "Sheik in Kakariko", SHEIK_IN_KAKARIKO, NOCTURNE_OF_SHADOW, {Category::cKakarikoVillage, Category::cKakariko, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0xAA), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); - locationTable[SHEIK_AT_TEMPLE] = ItemLocation::Delayed(RC_SHEIK_AT_TEMPLE, 0xFF, 0x25, "Sheik at Temple", SHEIK_AT_TEMPLE, PRELUDE_OF_LIGHT, {Category::cTempleOfTime, Category::cMarket, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x55), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); + locationTable[SHEIK_AT_TEMPLE] = ItemLocation::Delayed(RC_SHEIK_AT_TEMPLE, 0xFF, 0x25, "Sheik at Temple", SHEIK_AT_TEMPLE, PRELUDE_OF_LIGHT, {Category::cTempleOfTime, Category::cMarket, Category::cSong,}, SpoilerCollectionCheck::Chest(0x43, 0x1F), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); locationTable[SONG_FROM_IMPA] = ItemLocation::Delayed(RC_SONG_FROM_IMPA, 0xFF, 0x26, "Song from Impa", SONG_FROM_IMPA, ZELDAS_LULLABY, {Category::cHyruleCastle, Category::cMarket, Category::cSong, Category::cSongDungeonReward}, SpoilerCollectionCheck::EventChkInf(0x59), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE); - locationTable[SONG_FROM_MALON] = ItemLocation::Delayed(RC_SONG_FROM_MALON, 0xFF, 0x27, "Song from Malon", SONG_FROM_MALON, EPONAS_SONG, {Category::cLonLonRanch, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x58), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); - locationTable[SONG_FROM_SARIA] = ItemLocation::Delayed(RC_SONG_FROM_SARIA, 0xFF, 0x28, "Song from Saria", SONG_FROM_SARIA, SARIAS_SONG, {Category::cSacredForestMeadow, Category::cForest, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x57), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); - locationTable[SONG_FROM_COMPOSERS_GRAVE] = ItemLocation::Delayed(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, 0xFF, 0x29, "Song from Composers Grave", SONG_FROM_COMPOSERS_GRAVE, SUNS_SONG, {Category::cGraveyard, Category::cKakariko, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x5A), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); - locationTable[SONG_FROM_OCARINA_OF_TIME] = ItemLocation::Delayed(RC_SONG_FROM_OCARINA_OF_TIME, 0xFF, 0x2A, "Song from Ocarina of Time", SONG_FROM_OCARINA_OF_TIME, SONG_OF_TIME, {Category::cHyruleField, Category::cSong, Category::cNeedSpiritualStones,}, SpoilerCollectionCheck::EventChkInf(0xA9), SpoilerCollectionCheckGroup::GROUP_HYRULE_FIELD); + locationTable[SONG_FROM_MALON] = ItemLocation::Delayed(RC_SONG_FROM_MALON, 0xFF, 0x27, "Song from Malon", SONG_FROM_MALON, EPONAS_SONG, {Category::cLonLonRanch, Category::cSong,}, SpoilerCollectionCheck::Chest(0x63, 0x1F), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH); + locationTable[SONG_FROM_SARIA] = ItemLocation::Delayed(RC_SONG_FROM_SARIA, 0xFF, 0x28, "Song from Saria", SONG_FROM_SARIA, SARIAS_SONG, {Category::cSacredForestMeadow, Category::cForest, Category::cSong,}, SpoilerCollectionCheck::Chest(0x56, 0x1F), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS); + locationTable[SONG_FROM_COMPOSERS_GRAVE] = ItemLocation::Delayed(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, 0xFF, 0x29, "Song from Composers Grave", SONG_FROM_COMPOSERS_GRAVE, SUNS_SONG, {Category::cGraveyard, Category::cKakariko, Category::cSong,}, SpoilerCollectionCheck::Chest(0x41, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); + locationTable[SONG_FROM_OCARINA_OF_TIME] = ItemLocation::Delayed(RC_SONG_FROM_OCARINA_OF_TIME, 0xFF, 0x2A, "Song from Ocarina of Time", SONG_FROM_OCARINA_OF_TIME, SONG_OF_TIME, {Category::cHyruleField, Category::cSong, Category::cNeedSpiritualStones,}, SpoilerCollectionCheck::Chest(0x51, 0x1F), SpoilerCollectionCheckGroup::GROUP_HYRULE_FIELD); locationTable[SONG_FROM_WINDMILL] = ItemLocation::Delayed(RC_SONG_FROM_WINDMILL, 0xFF, 0x2B, "Song from Windmill", SONG_FROM_WINDMILL, SONG_OF_STORMS, {Category::cKakarikoVillage, Category::cKakariko, Category::cSong,}, SpoilerCollectionCheck::EventChkInf(0x5B), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); /*------------------------------- @@ -917,6 +918,9 @@ void LocationTable_Init() { locationTable[DMC_UPPER_GROTTO_GOSSIP_STONE] = ItemLocation::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, 0x00, 0x3A, "DMC Upper Grotto Gossip Stone", {}); locationTable[GANONDORF_HINT] = ItemLocation::OtherHint(RC_GANONDORF_HINT, 0x00, 0x00, "Ganondorf Hint", {}); + + for (int i = NONE; i != KEY_ENUM_MAX; i++) + locationLookupTable.insert(std::make_pair(locationTable[i].GetRandomizerCheck(), static_cast(i))); } std::vector KF_ShopLocations = { @@ -1484,6 +1488,10 @@ ItemLocation* Location(uint32_t locKey) { return &(locationTable[locKey]); } +ItemLocation* Location(RandomizerCheck rc) { + return &(locationTable[locationLookupTable[rc]]); +} + std::vector allLocations = {}; std::vector everyPossibleLocation = {}; diff --git a/soh/soh/Enhancements/randomizer/3drando/item_location.hpp b/soh/soh/Enhancements/randomizer/3drando/item_location.hpp index eaa6e39de..8d9c576d7 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_location.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_location.hpp @@ -83,73 +83,85 @@ enum class ItemLocationType { }; class SpoilerCollectionCheck { -public: - SpoilerCollectionCheckType type = SpoilerCollectionCheckType::SPOILER_CHK_NONE; - uint8_t scene = 0; - uint8_t flag = 0; + public: + SpoilerCollectionCheckType type = SpoilerCollectionCheckType::SPOILER_CHK_NONE; + uint8_t scene = 0; + uint8_t flag = 0; - SpoilerCollectionCheck() {} - SpoilerCollectionCheck(SpoilerCollectionCheckType type_, uint8_t scene_, uint8_t flag_) : type(type_), scene(scene_), flag(flag_) {} + SpoilerCollectionCheck() {} + SpoilerCollectionCheck(SpoilerCollectionCheckType type_, uint8_t scene_, uint8_t flag_) : type(type_), scene(scene_), flag(flag_) {} - static auto None() { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_NONE, 0x00, 0x00); - } + static auto None() { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_NONE, 0x00, 0x00); + } - static auto AlwaysCollected() { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED, 0x00, 0x00); - } + static auto AlwaysCollected() { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED, 0x00, 0x00); + } - static auto ItemGetInf(uint8_t slot) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF, 0x00, slot); - } + static auto ItemGetInf(uint8_t slot) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF, 0x00, slot); + } - static auto EventChkInf(uint8_t flag) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF, 0xFF, flag); - } + static auto EventChkInf(uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF, 0xFF, flag); + } - static auto InfTable(uint8_t offset, uint8_t bit) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_INF_TABLE, offset, bit); - } + static auto InfTable(uint8_t offset, uint8_t bit) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_INF_TABLE, offset, bit); + } - static auto Collectable(uint8_t scene, uint8_t flag) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE, scene, flag); - } + static auto Collectable(uint8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE, scene, flag); + } - static auto Chest(uint8_t scene, uint8_t flag) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_CHEST, scene, flag); - } + static auto Chest(uint8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_CHEST, scene, flag); + } - static auto Cow(uint8_t scene, uint8_t flag) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_COW, scene, flag); - } + static auto Cow(uint8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_COW, scene, flag); + } - static auto Fishing(uint8_t bit) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MINIGAME, 0x00, bit); - } + static auto Fishing(uint8_t bit) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MINIGAME, 0x00, bit); + } - static auto Scrub(uint8_t scene, uint8_t bit) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_SCRUB, scene, bit); - } + static auto Scrub(uint8_t scene, uint8_t bit) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_SCRUB, scene, bit); + } - static auto Biggoron(uint8_t mask) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_BIGGORON, 0x00, mask); - } + static auto Biggoron(uint8_t mask) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_BIGGORON, 0x00, mask); + } - static auto GerudoToken() { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_GERUDO_MEMBERSHIP_CARD, 0x00, 0x00); - } + static auto GerudoToken() { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_GERUDO_MEMBERSHIP_CARD, 0x00, 0x00); + } - static auto BigPoePoints() { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_POE_POINTS, 0x00, 0x00); - } + static auto BigPoePoints() { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_POE_POINTS, 0x00, 0x00); + } + + static auto Gravedigger(uint8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER, scene, flag); + } - static auto ShopItem(uint8_t scene, uint8_t itemSlot) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM, scene, itemSlot); - } + static auto ShopItem(uint8_t scene, uint8_t itemSlot) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM, scene, itemSlot); + } - static auto MagicBeans(uint8_t scene, uint8_t flag) { - return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS, scene, flag); - } + static auto MagicBeans(uint8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS, scene, flag); + } + + static auto Merchant(int8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT, scene, flag); + } + + static auto RandomizerInf(int8_t scene, uint8_t flag) { + return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF, scene, flag); + } }; class ItemLocation { @@ -475,6 +487,7 @@ public: void LocationTable_Init(); ItemLocation* Location(uint32_t locKey); +ItemLocation* Location(RandomizerCheck rc); extern std::vector> ShopLocationLists; diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.hpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.hpp index d64ce0b4b..4c3174140 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.hpp @@ -24,6 +24,9 @@ typedef enum { SPOILER_CHK_POE_POINTS, SPOILER_CHK_SHOP_ITEM, SPOILER_CHK_MAGIC_BEANS, + SPOILER_CHK_MERCHANT, + SPOILER_CHK_GRAVEDIGGER, + SPOILER_CHK_RANDOMIZER_INF, } SpoilerCollectionCheckType; // Location groups for checks, used to group the checks by logical location diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 47f79264e..341c99031 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -35,7 +35,6 @@ using namespace std::literals::string_literals; std::unordered_map SpoilerfileCheckNameToEnum; std::unordered_map SpoilerfileGetNameToEnum; -std::unordered_map> EnumToSpoilerfileGetName; std::multimap, RandomizerCheckObject> checkFromActorMultimap; std::set excludedLocations; @@ -2311,6 +2310,12 @@ std::map rcToRandomizerInf = { { RC_MARKET_BOMBCHU_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_8 }, { RC_GC_MEDIGORON, RAND_INF_MERCHANTS_MEDIGORON }, { RC_WASTELAND_BOMBCHU_SALESMAN, RAND_INF_MERCHANTS_CARPET_SALESMAN }, + { RC_LW_TRADE_COJIRO, RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO }, + { RC_GV_TRADE_SAW, RAND_INF_ADULT_TRADES_GV_TRADE_SAW }, + { RC_DMT_TRADE_BROKEN_SWORD, RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD }, + { RC_LH_TRADE_FROG, RAND_INF_ADULT_TRADES_LH_TRADE_FROG }, + { RC_DMT_TRADE_EYEDROPS, RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS }, + }; RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) { @@ -2542,6 +2547,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 067b66332..98b04f6c4 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "../../../include/ultra64.h" #include "../../../include/z64item.h" #include @@ -46,6 +47,7 @@ class Randomizer { std::unordered_map trialsRequired; std::unordered_set masterQuestDungeons; std::unordered_map merchantPrices; + std::unordered_map> EnumToSpoilerfileGetName; static Sprite* GetSeedTexture(uint8_t index); s16 GetItemModelFromId(s16 itemId); @@ -62,6 +64,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 ccfdddce6..77b89789f 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -894,6 +894,30 @@ std::map RandomizerCheckObjects::GetAllR return rcObjects; } +std::map rcAreaBySceneID = {}; +std::map RandomizerCheckObjects::GetAllRCAreaBySceneID() { + //memoize on first request + if (rcAreaBySceneID.size() == 0) { + for (auto& [randomizerCheck, rcObject] : rcObjects) { + rcAreaBySceneID[rcObject.sceneId] = rcObject.rcArea; + } + //Add checkless Hyrule Market areas to the area return + for (int id = (int)SCENE_ENTRA; id <= (int)SCENE_MARKET_RUINS; id++) { + rcAreaBySceneID[(SceneID)id] = RCAREA_MARKET; + } + } + return rcAreaBySceneID; +} + +RandomizerCheckArea RandomizerCheckObjects::GetRCAreaBySceneID(SceneID sceneId) { + std::map areas = GetAllRCAreaBySceneID(); + auto areaIt = areas.find(sceneId); + if (areaIt == areas.end()) + return RCAREA_INVALID; + else + return areaIt->second; +} + void RandomizerCheckObjects::UpdateImGuiVisibility() { for (auto& [randomizerCheck, locationIt] : rcObjects) { locationIt.visibleInImgui = ( diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.h b/soh/soh/Enhancements/randomizer/randomizer_check_objects.h index f0a8e1fa2..512a24057 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.h @@ -66,6 +66,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; @@ -94,5 +95,7 @@ namespace RandomizerCheckObjects { std::string GetRCAreaName(RandomizerCheckArea area); std::map GetAllRCObjects(); std::map> GetAllRCObjectsByArea(); + std::map GetAllRCAreaBySceneID(); + RandomizerCheckArea GetRCAreaBySceneID(SceneID sceneId); void UpdateImGuiVisibility(); } 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..9152b87a1 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -0,0 +1,1039 @@ +#include "randomizer_check_tracker.h" +#include "../../OTRGlobals.h" +#include +#include "../../UIWidgets.hpp" + +#include +#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); + +namespace CheckTracker { + +void Teardown(); +void InitializeChecks(); +void UpdateChecks(); +void UpdateInventoryChecks(); +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); +void RainbowTick(); +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 + +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 = { 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 + +SceneID DungeonSceneLookupByArea(RandomizerCheckArea area) { + switch (area) { + case RCAREA_DEKU_TREE: return SCENE_YDAN; + case RCAREA_DODONGOS_CAVERN: return SCENE_DDAN; + case RCAREA_JABU_JABUS_BELLY: return SCENE_BDAN; + case RCAREA_FOREST_TEMPLE: return SCENE_BMORI1; + case RCAREA_FIRE_TEMPLE: return SCENE_HIDAN; + case RCAREA_WATER_TEMPLE: return SCENE_MIZUSIN; + case RCAREA_SPIRIT_TEMPLE: return SCENE_JYASINZOU; + case RCAREA_SHADOW_TEMPLE: return SCENE_HAKADAN; + case RCAREA_BOTTOM_OF_THE_WELL: return SCENE_HAKADANCH; + case RCAREA_ICE_CAVERN: return SCENE_ICE_DOUKUTO; + case RCAREA_GERUDO_TRAINING_GROUND: return SCENE_MEN; + case RCAREA_GANONS_CASTLE: return SCENE_GANONTIKA; + default: return SCENE_ID_MAX; + } +} + +// persistent during gameplay +bool initialized = false; +bool doInitialize = false; +std::map checkStatusMap; +u32 areasFullyChecked = 0; +u32 areasSpoiled = 0; +bool showVOrMQ; +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 +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 (!open) { + CVar_SetS32("gCheckTrackerEnabled", 0); + return; + } + + if (doInitialize) + InitializeChecks(); + else if (initialized && (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2)) { + Teardown(); + 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; + } + } + + BeginFloatWindows("Check Tracker", ImGuiWindowFlags_NoScrollbar); + + if (!initialized) { + ImGui::Text("Waiting for file load..."); //TODO Language + EndFloatWindows(); + 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; + areasSpoiled |= (1 << currentArea); + + //Only update the checks if something has changed + if (ShouldUpdateChecks()) { + UpdateChecks(); + UpdateInventoryChecks(); + UpdateOrdering(); + } + + + //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."); + 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 + RainbowTick(); + 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; + bool isThisAreaSpoiled; + 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) + { + + //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, 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 || CVar_GetS32("gCheckTrackerOptionMQSpoilers", 0); + + if (isThisAreaSpoiled) { + if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(obj.rcArea)) { + if (OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(DungeonSceneLookupByArea(obj.rcArea))) + ImGui::Text("(%d/%d) - MQ", areaChecksGotten[obj.rcArea], areaChecksTotal[obj.rcArea]); + else + ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[obj.rcArea], areaChecksTotal[obj.rcArea]); + } else { + ImGui::Text("(%d/%d)", areaChecksGotten[obj.rcArea], areaChecksTotal[obj.rcArea]); + } + } else { + ImGui::Text("???", 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 && isThisAreaSpoiled) + 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", 1)) { + 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, 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(), nullptr, windowFlags); +} +void EndFloatWindows() { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::End(); +} + +bool showShops; +bool showOverworldTokens; +bool showDungeonTokens; +bool showBeans; +bool showScrubs; +bool showMerchants; +bool showCows; +bool showAdultTrade; +bool showKokiriSword; +bool showWeirdEgg; +bool showGerudoCard; +bool showFrogSongRupees; +bool showStartingMapsCompasses; +bool showKeysanity; +bool showGerudoFortressKeys; +bool showBossKeysanity; +bool showGanonBossKey; +bool fortressFast; +bool fortressNormal; + +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 = gSaveContext.n64ddFlag ? ( + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_OFF && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_ZERO_ITEMS) + : false; + showBeans = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MAGIC_BEANS) == RO_GENERIC_YES + : true; + showScrubs = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) != RO_SCRUBS_OFF + : false; + showMerchants = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF + : true; + showCows = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS) == RO_GENERIC_YES + : false; + showAdultTrade = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_YES + : true; + showKokiriSword = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_KOKIRI_SWORD) == RO_GENERIC_YES + : true; + showWeirdEgg = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG) == RO_GENERIC_YES + : true; + showGerudoCard = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) == RO_GENERIC_YES + : true; + showFrogSongRupees = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES) == RO_GENERIC_YES + : false; + showStartingMapsCompasses = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) != RO_DUNGEON_ITEM_LOC_VANILLA + : false; + showKeysanity = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA + : false; + showBossKeysanity = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA + : false; + showGerudoFortressKeys = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS) != RO_GERUDO_KEYS_VANILLA + : false; + showGanonBossKey = gSaveContext.n64ddFlag ? + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) != RO_GANON_BOSS_KEY_VANILLA + : false; + + if (gSaveContext.n64ddFlag) { + 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; + } +} + +bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) { + return + (rcObj.rcArea != RCAREA_INVALID) && // don't show Invalid locations + (rcObj.rcType != RCTYPE_GOSSIP_STONE) && //TODO: Don't show hints until tracker supports them + (rcObj.rcType != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them + (!RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea) || + rcObj.vOrMQ == RCVORMQ_BOTH || + rcObj.vOrMQ == RCVORMQ_MQ && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) || + rcObj.vOrMQ == RCVORMQ_VANILLA && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) + ) && + (rcObj.rcType != RCTYPE_SHOP || showShops) && + (rcObj.rcType != RCTYPE_SCRUB || showScrubs) && + (rcObj.rcType != RCTYPE_MERCHANT || showMerchants) && + (rcObj.rcType != RCTYPE_SKULL_TOKEN || + (showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) || + (showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea)) + ) && + (rcObj.rcType != RCTYPE_COW || showCows) && + (rcObj.rcType != RCTYPE_ADULT_TRADE || showAdultTrade) && + (rcObj.rc != RC_KF_KOKIRI_SWORD_CHEST || showKokiriSword) && + (rcObj.rc != RC_ZR_MAGIC_BEAN_SALESMAN || showBeans) && + (rcObj.rc != RC_HC_MALON_EGG || showWeirdEgg) && + (rcObj.rcType != RCTYPE_FROG_SONG || showFrogSongRupees) && + (rcObj.rcType != RCTYPE_MAP_COMPASS || showStartingMapsCompasses) && + (rcObj.rcType != RCTYPE_SMALL_KEY || showKeysanity) && + (rcObj.rcType != RCTYPE_BOSS_KEY || showBossKeysanity) && + (rcObj.rcType != RCTYPE_GANON_BOSS_KEY || showGanonBossKey) && + (rcObj.rcType != RCTYPE_GF_KEY && rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD || + (showGerudoCard && rcObj.rc == RC_GF_GERUDO_MEMBERSHIP_CARD) || + (fortressNormal && showGerudoFortressKeys && rcObj.rcType == RCTYPE_GF_KEY) || + (fortressFast && showGerudoFortressKeys && rcObj.rc == RC_GF_NORTH_F1_CARPENTER) + ); +} + + +void InitializeChecks() { + if (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) + return; + + int count = 0; + + //Link's Pocket + if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING) { + 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; + } + RandomizerCheckObject linksPocket = { RC_LINKS_POCKET, RCVORMQ_BOTH, RCTYPE_LINKS_POCKET, startingArea, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Link's Pocket", "Link's Pocket" }; + + checks.push_back(linksPocket); + checkStatusMap.emplace(RC_LINKS_POCKET, RCSHOW_SAVED); + count++; + areaChecksTotal[startingArea]++; + areaChecksGotten[startingArea]++; + } + + LoadSettings(); + for (auto& [rcCheck, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) { + if (!IsVisibleInCheckTracker(rcObj)) + continue; + + checks.push_back(rcObj); + checkStatusMap.emplace(rcObj.rc, RCSHOW_UNCHECKED); + count++; + areaChecksTotal[rcObj.rcArea]++; + + if (areaChecksGotten[rcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) + areasSpoiled |= (1 << rcObj.rcArea); + } + + showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) > 0); + //Bug: the above will spoil that everything is vanilla if the random count rolled 0. + // Should use the below instead, but the setting isn't currently saved to the savefile + //showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) != RO_GENERIC_OFF); + + UpdateChecks(); + UpdateInventoryChecks(); + UpdateOrdering(true); + doInitialize = false; + initialized = true; +} + +void Teardown() { + initialized = false; + checkStatusMap.clear(); + areasFullyChecked = 0; + areasSpoiled = 0; + checks.clear(); + lastLocationChecked = RC_UNKNOWN_CHECK; + for (int i = 0; i < sizeof(areaChecksTotal); i++) { + areaChecksTotal[i] = 0; + areaChecksGotten[i] = 0; + } + doInitialize = true; +} + +int slowCheckIdx = 0; +// Checks only one check every call +bool SlowUpdateCheck() { + bool ret = false; + auto checkIt = checks.begin() + slowCheckIdx; + if (checkIt == checks.end()) { + slowCheckIdx = 0; + return false; + } + + RandomizerCheckObject rcObj = *checkIt; + RandomizerCheckShow lastStatus = checkStatusMap.find(rcObj.rc)->second; + if (lastStatus != GetCheckStatus(rcObj, slowCheckIdx)) + ret = true; + + slowCheckIdx++; + return ret; +} + +bool ShouldUpdateChecks() { + // TODO eventually will need to be hooked into game elements rather than just save file + if (CVar_GetS32("gCheckTrackerOptionPerformanceMode", 0)) + return SlowUpdateCheck(); + else + return true; +} + +void UpdateInventoryChecks() { + //For all the areas with compasses, if you have one, spoil the area + for (u8 i = RCAREA_DEKU_TREE; i <= RCAREA_ICE_CAVERN; i++) + if (gSaveContext.inventory.dungeonItems[i - RCAREA_DEKU_TREE] & 0x02) + areasSpoiled |= (1 << i); +} + +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)); + + if (areaChecksGotten[rcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) + areasSpoiled |= (1 << rcObj.rcArea); + } + + lastCheck = &rcObj; + + idx++; + } +} + +void UpdateOrdering(bool init) { + // Sort the entire thing + if (init) { + std::sort(checks.begin(), checks.end(), CompareCheckObject); + return; + } + + //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.rc < j.rc) + return true; + else if (i.rc > j.rc) + 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. Then we'll use CHECKED + + //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) { + ItemLocation* x = Location(obj.rc); + 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_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: + 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_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; + + // 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, 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 + if (*thisCheckStatus != RCSHOW_UNCHECKED) { + switch (*thisCheckStatus) { + case RCSHOW_SAVED: + case RCSHOW_CHECKED: + case RCSHOW_SCUMMED: + if (gSaveContext.n64ddFlag) + txt = OTRGlobals::Instance->gRandomizer + ->EnumToSpoilerfileGetName[gSaveContext.itemLocations[rcObj.rc].get.rgID][gSaveContext.language]; + else if (gSaveContext.language == LANGUAGE_ENG) + txt = ItemFromGIID(rcObj.ogItemId).GetName().english; + else if (gSaveContext.language == LANGUAGE_FRA) + txt = ItemFromGIID(rcObj.ogItemId).GetName().french; + break; + case RCSHOW_SKIPPED: + txt = "Skipped"; //TODO language + break; + case RCSHOW_SEEN: + if (gSaveContext.n64ddFlag) + txt = OTRGlobals::Instance->gRandomizer + ->EnumToSpoilerfileGetName[gSaveContext.itemLocations[rcObj.rc].get.fakeRgID][gSaveContext.language]; + else if (gSaveContext.language == LANGUAGE_ENG) + txt = ItemFromGIID(rcObj.ogItemId).GetName().english; + else if (gSaveContext.language == LANGUAGE_FRA) + txt = ItemFromGIID(rcObj.ogItemId).GetName().french; + break; + case RCSHOW_HINTED: + txt = "Hints are WIP"; // TODO language + break; + } + + 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", + "gCheckTrackerCheckedMainColor", "gCheckTrackerCheckedExtraColor", + "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", + "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", +}; + +int hue = 0; +void RainbowTick() { + float freqHue = hue * 2 * M_PI / (360 * CVar_GetFloat("gCosmetics.RainbowSpeed", 0.6f)); + for (auto cvar : rainbowCVars) { + if (CVar_GetS32((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; + + CVar_SetRGBA(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) { + 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, + ImVec4(main_color.r / 255.0f, main_color.g / 255.0f, main_color.b / 255.0f, main_color.a / 255.0f), + ImVec4(main_default_color.r / 255.0f, main_default_color.g / 255.0f, main_default_color.b / 255.0f, main_default_color.a / 255.0f))) + { + main_color = CVar_GetRGBA(cvarMainName, main_default_color); + }; + ImGui::PopItemWidth(); + + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + if (UIWidgets::EnhancementColor("Details", cvarExtraName, + ImVec4(extra_color.r / 255.0f, extra_color.g / 255.0f, extra_color.b / 255.0f, extra_color.a / 255.0f), + ImVec4(extra_default_color.r / 255.0f, extra_default_color.g / 255.0f, extra_default_color.b / 255.0f, extra_default_color.a / 255.0f))) + { + extra_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 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 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::PushItemWidth(ImGui::GetContentRegionAvail().x); + if (UIWidgets::EnhancementColor("BG Color", "gCheckTrackerBgColor", + ImVec4(Color_Background.r / 255.0f, Color_Background.g / 255.0f, Color_Background.b / 255.0f, Color_Background.a / 255.0f), + ImVec4(Color_Bg_Default.r / 255.0f, Color_Bg_Default.g / 255.0f, Color_Bg_Default.b / 255.0f, Color_Bg_Default.a / 255.0f), + false, true)) + { + Color_Background = CVar_GetRGBA("gCheckTrackerBgColor", Color_Bg_Default); + } + ImGui::PopItemWidth(); + + ImGui::Text("Window Type"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerWindowType", windowType, 2, 1); + + if (CVar_GetS32("gCheckTrackerWindowType", 1) == 0) { + UIWidgets::EnhancementCheckbox("Enable Dragging", "gCheckTrackerHudEditMode"); + UIWidgets::EnhancementCheckbox("Only enable while paused", "gCheckTrackerShowOnlyPaused"); + ImGui::Text("Display Mode"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerDisplayType", displayType, 2, 0); + if (CVar_GetS32("gCheckTrackerDisplayType", 0) > 0) { + ImGui::Text("Combo Button 1"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerComboButton1", buttonStrings, 14, 6); + ImGui::Text("Combo Button 2"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerComboButton2", buttonStrings, 14, 8); + } + } + UIWidgets::EnhancementCheckbox("Performance mode", "gCheckTrackerOptionPerformanceMode"); + UIWidgets::Tooltip("Slows down checking for updates to 1 check per frame. Only required if experiencing poor performance when using Check Tracker."); + 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. "); + + 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); + Color_Background = CVar_GetRGBA("gCheckTrackerBgColor", Color_Bg_Default); + 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(); +} + +} // namespace CheckTracker 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..da0e0d92c --- /dev/null +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -0,0 +1,33 @@ +#pragma once + +namespace CheckTracker { + +// Check tracker check visibility categories +typedef enum { + RCSHOW_UNCHECKED, + RCSHOW_SKIPPED, + RCSHOW_SEEN, + RCSHOW_HINTED, + RCSHOW_CHECKED, + RCSHOW_SCUMMED, + RCSHOW_SAVED, +} RandomizerCheckShow; + +//Converts an index into a Little Endian bitmask, as follows: +//00: 0000000100000000 +//01: 0000001000000000 +//... +//06: 0100000000000000 +//07: 1000000000000000 +//08: 0000000000000001 +//09: 0000000000000010 +//... +//14: 0000000001000000 +//15: 0000000010000000 +//repeat... +#define INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(idx) (0x8000 >> (7 - (idx % 8) + ((idx % 16) / 8) * 8)) + +void InitCheckTracker(); +void DrawCheckTracker(bool& open); + +} // namespace CheckTracker \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index e764f372b..b49165e37 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -143,6 +143,12 @@ typedef enum { RAND_INF_MERCHANTS_CARPET_SALESMAN, RAND_INF_MERCHANTS_MEDIGORON, + RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO, + RAND_INF_ADULT_TRADES_GV_TRADE_SAW, + RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD, + RAND_INF_ADULT_TRADES_LH_TRADE_FROG, + RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS, + // If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16) RAND_INF_MAX, diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index a2fc10010..7bca8e41a 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -1199,6 +1199,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); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index d46a93577..ea633111f 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -39,6 +39,7 @@ #include "Enhancements/randomizer/randomizer.h" #include "Enhancements/randomizer/randomizer_entrance_tracker.h" #include "Enhancements/randomizer/randomizer_item_tracker.h" +#include "Enhancements/randomizer/randomizer_check_tracker.h" #include "Enhancements/randomizer/3drando/random.hpp" #include "Enhancements/gameplaystats.h" #include "Enhancements/n64_weird_frame_data.inc" @@ -446,6 +447,7 @@ extern "C" void InitOTR() { InitItemTracker(); InitEntranceTracker(); InitStatTracker(); + CheckTracker::InitCheckTracker(); OTRExtScanner(); VanillaItemTable_Init(); diff --git a/soh/soh/UIWidgets.cpp b/soh/soh/UIWidgets.cpp index 2282c73ad..ff8c318cb 100644 --- a/soh/soh/UIWidgets.cpp +++ b/soh/soh/UIWidgets.cpp @@ -625,13 +625,14 @@ namespace UIWidgets { } else { + flags |= ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreview; if (ImGui::ColorEdit4(text, (float*)&ColorRGBA, flags)) { Color_RGBA8 colors; - colors.r = ColorRGBA.x / 255; - colors.g = ColorRGBA.y / 255; - colors.b = ColorRGBA.z / 255; - colors.a = ColorRGBA.w / 255; + colors.r = ColorRGBA.x * 255.0; + colors.g = ColorRGBA.y * 255.0; + colors.b = ColorRGBA.z * 255.0; + colors.a = ColorRGBA.w * 255.0; CVar_SetRGBA(cvarName, colors); SohImGui::RequestCvarSaveOnNextTick(); diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c index 420712fa3..787ee36de 100644 --- a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c @@ -658,6 +658,7 @@ s16 EnGo2_GetStateGoronDmtBiggoron(PlayState* play, EnGo2* this) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_BROKEN_SWORD, GI_PRESCRIPTION); Randomizer_ConsumeAdultTradeItem(play, ITEM_SWORD_BROKEN); EnGo2_GetItemEntry(this, play, getItemEntry); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD); } else { u32 getItemId = GI_PRESCRIPTION; EnGo2_GetItem(this, play, getItemId); @@ -1929,6 +1930,7 @@ void EnGo2_BiggoronEyedrops(EnGo2* this, PlayState* play) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_EYEDROPS, GI_CLAIM_CHECK); Randomizer_ConsumeAdultTradeItem(play, ITEM_EYEDROPS); EnGo2_GetItemEntry(this, play, getItemEntry); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS); } else { u32 getItemId = GI_CLAIM_CHECK; EnGo2_GetItem(this, play, getItemId); diff --git a/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c index bb1b07b3c..17efce312 100644 --- a/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c +++ b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c @@ -181,6 +181,7 @@ void func_80A6E740(EnHs* this, PlayState* play) { GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_LW_TRADE_COJIRO, GI_ODD_MUSHROOM); Randomizer_ConsumeAdultTradeItem(play, ITEM_COJIRO); GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO); } else { s32 itemId = GI_ODD_MUSHROOM; func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f); @@ -199,6 +200,7 @@ void func_80A6E7BC(EnHs* this, PlayState* play) { GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_LW_TRADE_COJIRO, GI_ODD_MUSHROOM); Randomizer_ConsumeAdultTradeItem(play, ITEM_COJIRO); GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO); } else { s32 itemId = GI_ODD_MUSHROOM; func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f); diff --git a/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c index 7d648a4b7..1ffca5429 100644 --- a/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c +++ b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c @@ -103,6 +103,7 @@ void func_80AACA94(EnMk* this, PlayState* play) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LH_TRADE_FROG, GI_EYEDROPS); Randomizer_ConsumeAdultTradeItem(play, ITEM_FROG); GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 50.0f); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG); } else { s32 getItemID = GI_EYEDROPS; func_8002F434(&this->actor, play, getItemID, 10000.0f, 50.0f); @@ -117,6 +118,7 @@ void func_80AACB14(EnMk* this, PlayState* play) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LH_TRADE_FROG, GI_EYEDROPS); Randomizer_ConsumeAdultTradeItem(play, ITEM_FROG); GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 50.0f); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG); } else { s32 getItemID = GI_EYEDROPS; func_8002F434(&this->actor, play, getItemID, 10000.0f, 50.0f); diff --git a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c index 2fdfd407f..8b44ea2ad 100644 --- a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c +++ b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c @@ -318,6 +318,7 @@ void func_80B20768(EnToryo* this, PlayState* play) { GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_GV_TRADE_SAW, GI_SWORD_BROKEN); Randomizer_ConsumeAdultTradeItem(play, ITEM_SAW); GiveItemEntryFromActor(&this->actor, play, itemEntry, 100.0f, 10.0f); + Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_GV_TRADE_SAW); } else { s32 itemId = GI_SWORD_BROKEN; func_8002F434(&this->actor, play, itemId, 100.0f, 10.0f);