From 70f3dfa8c59890819c3c4539bdcf75b20f15badd Mon Sep 17 00:00:00 2001 From: Caladius Date: Sun, 17 Nov 2024 14:44:54 -0500 Subject: [PATCH] Built-in Plandomizer Editor (#4532) * Add Plandomizer Editor Window * UI, Ice Trap Editor, Hint Editor * Drop down for previous seeds, wip hash display. * Clean Up, Hash Icon Editor * Updates based on suggestions * Replace Tint with Color * Add Boss Soul Icon * Corrected App Folder Directory and updated from suggesstions. * Add Hints to Junk Pool * Utilize RandomElement for hints * Hint update for pep * apply patch * Fix Sorting issue on Linux * Skeleton Key, Shop Items, Milk, Fishing Pole, Ocarina Buttons, Loser Rupee fixed * Fix stretched note icons. * Remove Triforce, add Triforce Pieces. Centered Song Notes. * Update soh/soh/Enhancements/randomizer/Plandomizer.cpp Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> * Update hint_list.cpp One spelling, removed 1 hint. * Update Plandomizer.cpp --------- Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Co-authored-by: Malkierian --- .../custom-message/CustomMessageManager.cpp | 42 +- .../custom-message/CustomMessageManager.h | 13 +- .../Enhancements/debugger/debugSaveEditor.cpp | 2 +- .../randomizer/3drando/hint_list.cpp | 112 ++ .../Enhancements/randomizer/3drando/hints.cpp | 4 +- .../Enhancements/randomizer/3drando/hints.hpp | 4 +- .../Enhancements/randomizer/Plandomizer.cpp | 1109 +++++++++++++++++ soh/soh/Enhancements/randomizer/Plandomizer.h | 52 + soh/soh/Enhancements/randomizer/rando_hash.h | 2 +- .../Enhancements/randomizer/randomizerTypes.h | 29 + soh/soh/ImGuiUtils.cpp | 11 +- soh/soh/ImGuiUtils.h | 4 +- soh/soh/SohGui.cpp | 4 + soh/soh/SohGui.hpp | 1 + soh/soh/SohMenuBar.cpp | 10 + 15 files changed, 1376 insertions(+), 23 deletions(-) create mode 100644 soh/soh/Enhancements/randomizer/Plandomizer.cpp create mode 100644 soh/soh/Enhancements/randomizer/Plandomizer.h diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index c24b83fb6..3d494b87f 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -20,6 +20,10 @@ static const std::unordered_map percentColors = { { "w { "b", QM_BLUE }, { "c", QM_LBLUE }, { "p", QM_PINK }, { "y", QM_YELLOW }, { "B", QM_BLACK } }; +static const std::unordered_map colorToPercent = { { QM_WHITE, "%w" }, { QM_RED, "%r"}, { QM_GREEN, "%g" }, + { QM_BLUE, "%b" }, { QM_LBLUE, "%c"}, { QM_PINK, "%p" }, + { QM_YELLOW, "%y" }, { QM_BLACK, "%B" } }; + static const std::unordered_map altarIcons = { { "0", ITEM_KOKIRI_EMERALD }, { "1", ITEM_GORON_RUBY }, @@ -147,6 +151,8 @@ void CustomMessage::ProcessMessageFormat(std::string& str, MessageFormat format) CleanString(str); } else if (format == MF_AUTO_FORMAT){ AutoFormatString(str); + }else if (format == MF_ENCODE){ + EncodeColors(str); } } @@ -281,6 +287,12 @@ void CustomMessage::Clean() { } } +void CustomMessage::Encode() { + for (std::string& str : messages) { + EncodeColors(str); + } +} + void CustomMessage::FormatString(std::string& str) const { std::replace(str.begin(), str.end(), '&', NEWLINE()[0]); std::replace(str.begin(), str.end(), '^', WAIT_FOR_INPUT()[0]); @@ -506,7 +518,23 @@ const char* Interface_ReplaceSpecialCharacters(char text[]) { return textChar; } +void CustomMessage::EncodeColors(std::string& str) const { + for (std::string color: colors) { + if (const size_t firstHashtag = str.find('#'); firstHashtag != std::string::npos) { + str.replace(firstHashtag, 1, colorToPercent.at(color)); + if (const size_t secondHashtag = str.find('#', firstHashtag + 1); secondHashtag != std::string::npos) { + str.replace(secondHashtag, 1, "%w"); + } else { + SPDLOG_DEBUG("non-matching hashtags in string: \"%s\"", str); + } + } + } + // Remove any remaining '#' characters. + std::erase(str, '#'); +} + void CustomMessage::ReplaceColors(std::string& str) const { + EncodeColors(str); for (const auto& colorPair : percentColors) { std::string textToReplace = "%"; textToReplace += colorPair.first; @@ -516,18 +544,6 @@ void CustomMessage::ReplaceColors(std::string& str) const { start_pos += textToReplace.length(); } } - for (auto color: colors) { - if (const size_t firstHashtag = str.find('#'); firstHashtag != std::string::npos) { - str.replace(firstHashtag, 1, COLOR(color)); - if (const size_t secondHashtag = str.find('#', firstHashtag + 1); secondHashtag != std::string::npos) { - str.replace(secondHashtag, 1, COLOR(QM_WHITE)); - } else { - SPDLOG_DEBUG("non-matching hashtags in string: \"%s\"", str); - } - } - } - // Remove any remaining '#' characters. - std::erase(str, '#'); } void CustomMessage::ReplaceAltarIcons(std::string& str) const { @@ -619,6 +635,8 @@ CustomMessage CustomMessageManager::RetrieveMessage(std::string tableID, uint16_ message.AutoFormat(); } else if (format == MF_CLEAN){ message.Clean(); + } else if (format == MF_ENCODE){ + message.Encode(); } return message; diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h index 130feda73..7f68f2fab 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -26,7 +26,8 @@ typedef enum { MF_FORMATTED, MF_CLEAN, MF_RAW, - MF_AUTO_FORMAT + MF_AUTO_FORMAT, + MF_ENCODE, } MessageFormat; /** @@ -108,6 +109,11 @@ class CustomMessage { */ void ReplaceSpecialCharacters(std::string& str) const; + /** + * @brief Replaces hashtags with stored colors. + */ + void EncodeColors(std::string& str) const; + /** * @brief Replaces our color variable strings with the OoT control codes. */ @@ -160,6 +166,11 @@ class CustomMessage { */ void Clean(); + /** + * @brief Replaces variable characters with fixed ones to store the sata in string form + */ + void Encode(); + /** * @brief Replaces various symbols with the control codes necessary to * display them in OoT's textboxes for a single string diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index a5126d32e..2a1cf9f49 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -1206,7 +1206,7 @@ void DrawQuestStatusTab() { ImGui::SameLine(); DrawQuestItemButton(QUEST_GERUDO_CARD); - for (const SongMapEntry& entry : songMapping) { + for (const auto& [quest, entry] : songMapping) { if ((entry.id != QUEST_SONG_MINUET) && (entry.id != QUEST_SONG_LULLABY)) { ImGui::SameLine(); } diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index ea082218e..14faa479b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -2253,6 +2253,118 @@ void StaticData::HintTable_Init() { /*german*/ "", /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + hintTextTable[RHT_JUNK_CREW_1] = HintText(CustomMessage("They say that %gGanondorf's Mom%w is going out with %ySqueak%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_2] = HintText(CustomMessage("They say that %gProxySaw%w is still fixing %yCaladius's Bugs%w...", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_3] = HintText(CustomMessage("They say that %gItsHeckinPat%w is still just %yEyeballing it%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_4] = HintText(CustomMessage("They say that %gCaladius%w is working on %yV2%w of something.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_5] = HintText(CustomMessage("They say that %gdice%w is a funny name for a %ytaco%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_6] = HintText(CustomMessage("They say %g2Ship Rando%w is still blocked by %yV3%w...", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_7] = HintText(CustomMessage("They say if you click your heels and say %gframebuffer%w 3 times, %yArchez%w appears!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_8] = HintText(CustomMessage("They say %gVB%w stands for %yVirtual Bananas%w... Probably.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_9] = HintText(CustomMessage("They say %gZeru%w is still routing his %yHundo%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_10] = HintText(CustomMessage("They say %gRaccoonCloud%w is still looking for his %yHover Boots%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_11] = HintText(CustomMessage("They say %gItsHeckinPat%w foreclosed on his %yHut%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_12] = HintText(CustomMessage("They say %gRaccoonCloud%w is part of the %yInner Circle%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_13] = HintText(CustomMessage("They say %gMoonlitxShadows%w is the %rleader%w of the %yDork Army%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_14] = HintText(CustomMessage("They say %gGanondorf%w hates the %yInternet%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_15] = HintText(CustomMessage("They say %gMido's House%w hoards %yTrash%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_16] = HintText(CustomMessage("They say %gSweettalking Ganondorf%w rewards %yHis Heart%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_17] = HintText(CustomMessage("They say %gaMannus%w said %yGo To Bed%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_18] = HintText(CustomMessage("They say %gCaladius%w is a %yPinhead%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_19] = HintText(CustomMessage("They say %gRaccoonCloud%w loves the %yIce Cavern%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_20] = HintText(CustomMessage("They say %gNo One%w should forget %yHover Scrub%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_21] = HintText(CustomMessage("They say %gMoonlitxShadows%w likes to %ySlide%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_23] = HintText(CustomMessage("They say that %gBackwalking%w should be %rBanned%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_24] = HintText(CustomMessage("They say that %gGorons%w should always have %yLong Necks%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_25] = HintText(CustomMessage("They say that %gCaladius%w has a %ytendency to lose his shirt%w!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_26] = HintText(CustomMessage("They say that if your %rSkip keeps Failing%w, you're probably an %yESS Off%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_27] = HintText(CustomMessage("They say that %gLogic%w is just a %ySuggestion%w.", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_28] = HintText(CustomMessage("They say there's %gAlways Logic%w in %yNo Logic%w...", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + + hintTextTable[RHT_JUNK_CREW_29] = HintText(CustomMessage("They said that %rFredomato%w has just %yone more push up%w to do!", + /*german*/ "", + /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*-------------------------- | DUNGEON HINT TEXT | ---------------------------*/ diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index f72212301..9831c2d2b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -113,8 +113,8 @@ StaticHintInfo::StaticHintInfo(HintType _type, std::vector _hintChecks = {}, bool _yourPocket = false, int _num = 0); }; +RandomizerHintTextKey GetRandomJunkHint(); extern void CreateAllHints(); extern void CreateWarpSongTexts(); -void CreateStaticHints(); \ No newline at end of file +void CreateStaticHints(); +RandomizerHintTextKey GetRandomJunkHint(); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.cpp b/soh/soh/Enhancements/randomizer/Plandomizer.cpp new file mode 100644 index 000000000..732ef700b --- /dev/null +++ b/soh/soh/Enhancements/randomizer/Plandomizer.cpp @@ -0,0 +1,1109 @@ +#include "Plandomizer.h" +#include "soh/UIWidgets.hpp" +#include "soh/util.h" +#include +#include "soh/Notification/Notification.h" +#include +#include "soh/Enhancements/randomizer/3drando/hints.hpp" + +#include +#include + +#include "soh/OTRGlobals.h" +#include "soh/ImGuiUtils.h" +#include "soh/Enhancements/randomizer/logic.h" +#include "soh/Enhancements/randomizer/randomizer_check_objects.h" +#include "soh/Enhancements/randomizer/rando_hash.h" +#include "soh/Enhancements/randomizer/3drando/shops.hpp" + +extern "C" { + #include "include/z64item.h" + #include "objects/gameplay_keep/gameplay_keep.h" + extern SaveContext gSaveContext; + extern PlayState* gPlayState; +} + +const std::string randomizeButton = ICON_FA_RANDOM; + +static int32_t correctedItemID = -1; +static int32_t getTabID = TAB_HINTS; + +Rando::Item temporaryItem; +std::string shortName = ""; +std::string logTemp = ""; +std::string lastLoadedSpoiler = ""; +int32_t temporaryItemIndex = -1; +RandomizerCheckArea selectedArea = RCAREA_INVALID; + +ImVec4 itemColor = ImVec4( 1.0f, 1.0f, 1.0f, 1.0f ); +ImTextureID textureID; +ImVec2 imageSize = ImVec2(32.0f, 32.0f); +float imagePadding = 2.0f; +ImVec2 textureUV0 = ImVec2( 0, 0 ); +ImVec2 textureUV1 = ImVec2( 1, 1 ); + +bool shouldPopup = false; +bool shouldTrapPopup = false; +bool shouldRemove = false; + +namespace fs = std::filesystem; +std::vector existingSeedList; + +std::vector spoilerHash; +std::vector plandoHash; +std::vector spoilerLogData; +std::vector plandoLogData; +std::vector> drawnItemsList; + +std::vector spoilerHintData; +std::vector plandoHintData; + +extern std::map rcAreaNames; + +std::unordered_map bossKeyShortNames = { + { RG_FOREST_TEMPLE_BOSS_KEY, "Frst" }, + { RG_FIRE_TEMPLE_BOSS_KEY, "Fire" }, + { RG_WATER_TEMPLE_BOSS_KEY, "Watr" }, + { RG_SPIRIT_TEMPLE_BOSS_KEY, "Sprt" }, + { RG_SHADOW_TEMPLE_BOSS_KEY, "Shdw" }, + { RG_GANONS_CASTLE_BOSS_KEY, "Ganon" }, +}; + +std::unordered_map ocarinaButtonNames = { + { RG_OCARINA_A_BUTTON, "A" }, + { RG_OCARINA_C_UP_BUTTON, "C-UP" }, + { RG_OCARINA_C_DOWN_BUTTON, "C-DWN" }, + { RG_OCARINA_C_LEFT_BUTTON, "C-LFT" }, + { RG_OCARINA_C_RIGHT_BUTTON, "C-RHT" }, +}; + +std::map bossSoulMapping = { + { RG_GOHMA_SOUL, { 0.00f, 1.00f, 0.00f, 1.0f } }, + { RG_KING_DODONGO_SOUL, { 1.00f, 0.00f, 0.39f, 1.0f } }, + { RG_BARINADE_SOUL, { 0.20f, 1.00f, 1.00f, 1.0f } }, + { RG_PHANTOM_GANON_SOUL, { 0.02f, 0.76f, 0.18f, 1.0f } }, + { RG_VOLVAGIA_SOUL, { 0.93f, 0.37f, 0.37f, 1.0f } }, + { RG_MORPHA_SOUL, { 0.33f, 0.71f, 0.87f, 1.0f } }, + { RG_BONGO_BONGO_SOUL, { 0.49f, 0.06f, 0.69f, 1.0f } }, + { RG_TWINROVA_SOUL, { 0.87f, 0.62f, 0.18f, 1.0f } }, + { RG_GANON_SOUL, { 0.31f, 0.31f, 0.31f, 1.0f } } +}; + + +std::vector infiniteItemList = { + RG_GREEN_RUPEE, RG_BLUE_RUPEE, RG_RED_RUPEE, RG_PURPLE_RUPEE, RG_HUGE_RUPEE, + RG_ARROWS_5, RG_ARROWS_10, RG_ARROWS_30, + RG_DEKU_STICK_1, RG_DEKU_SEEDS_30, RG_DEKU_NUTS_5, RG_DEKU_NUTS_10, + RG_BOMBS_5, RG_BOMBS_10, RG_BOMBS_20, RG_BOMBCHU_5, RG_BOMBCHU_10, RG_BOMBCHU_20, + RG_RECOVERY_HEART, RG_ICE_TRAP, RG_SOLD_OUT +}; + +std::unordered_map itemImageMap = { + { RG_NONE, "ITEM_SOLD_OUT" }, + { RG_KOKIRI_SWORD, "ITEM_SWORD_KOKIRI" }, + { RG_GIANTS_KNIFE, "ITEM_SWORD_KNIFE" }, + { RG_BIGGORON_SWORD, "ITEM_SWORD_BGS" }, + { RG_DEKU_SHIELD, "ITEM_SHIELD_DEKU" }, + { RG_HYLIAN_SHIELD, "ITEM_SHIELD_HYLIAN" }, + { RG_MIRROR_SHIELD, "ITEM_SHIELD_MIRROR" }, + { RG_GORON_TUNIC, "ITEM_TUNIC_GORON" }, + { RG_ZORA_TUNIC, "ITEM_TUNIC_ZORA" }, + { RG_IRON_BOOTS, "ITEM_BOOTS_IRON" }, + { RG_HOVER_BOOTS, "ITEM_BOOTS_HOVER" }, + { RG_BOOMERANG, "ITEM_BOOMERANG" }, + { RG_LENS_OF_TRUTH, "ITEM_LENS" }, + { RG_MEGATON_HAMMER, "ITEM_HAMMER" }, + { RG_STONE_OF_AGONY, "ITEM_STONE_OF_AGONY" }, + { RG_DINS_FIRE, "ITEM_DINS_FIRE" }, + { RG_FARORES_WIND, "ITEM_FARORES_WIND" }, + { RG_NAYRUS_LOVE, "ITEM_NAYRUS_LOVE" }, + { RG_FIRE_ARROWS, "ITEM_ARROW_FIRE" }, + { RG_ICE_ARROWS, "ITEM_ARROW_ICE" }, + { RG_LIGHT_ARROWS, "ITEM_ARROW_LIGHT" }, + { RG_GERUDO_MEMBERSHIP_CARD, "ITEM_GERUDO_CARD" }, + { RG_MAGIC_BEAN, "ITEM_BEAN" }, + { RG_MAGIC_BEAN_PACK, "ITEM_BEAN" }, + { RG_DOUBLE_DEFENSE, "ITEM_HEART_CONTAINER" }, + { RG_WEIRD_EGG, "ITEM_WEIRD_EGG" }, + { RG_ZELDAS_LETTER, "ITEM_LETTER_ZELDA" }, + { RG_POCKET_EGG, "ITEM_POCKET_EGG" }, + { RG_COJIRO, "ITEM_COJIRO" }, + { RG_ODD_MUSHROOM, "ITEM_ODD_MUSHROOM" }, + { RG_ODD_POTION, "ITEM_ODD_POTION" }, + { RG_POACHERS_SAW, "ITEM_SAW" }, + { RG_BROKEN_SWORD, "ITEM_SWORD_BROKEN" }, + { RG_PRESCRIPTION, "ITEM_PRESCRIPTION" }, + { RG_EYEBALL_FROG, "ITEM_FROG" }, + { RG_EYEDROPS, "ITEM_EYEDROPS" }, + { RG_CLAIM_CHECK, "ITEM_CLAIM_CHECK" }, + { RG_GOLD_SKULLTULA_TOKEN, "ITEM_SKULL_TOKEN" }, + { RG_PROGRESSIVE_HOOKSHOT, "ITEM_HOOKSHOT" }, + { RG_PROGRESSIVE_STRENGTH, "ITEM_BRACELET" }, + { RG_PROGRESSIVE_BOMB_BAG, "ITEM_BOMB_BAG_30" }, + { RG_PROGRESSIVE_BOW, "ITEM_QUIVER_30" }, + { RG_PROGRESSIVE_SLINGSHOT, "ITEM_SLINGSHOT" }, + { RG_PROGRESSIVE_WALLET, "ITEM_WALLET_ADULT" }, + { RG_PROGRESSIVE_SCALE, "ITEM_SCALE_SILVER" }, + { RG_PROGRESSIVE_NUT_UPGRADE, "ITEM_NUT" }, + { RG_PROGRESSIVE_STICK_UPGRADE, "ITEM_STICK" }, + { RG_PROGRESSIVE_BOMBCHUS, "ITEM_BOMBCHU" }, + { RG_PROGRESSIVE_MAGIC_METER, "ITEM_MAGIC_SMALL" }, + { RG_MAGIC_SINGLE, "ITEM_MAGIC_SMALL" }, + { RG_MAGIC_DOUBLE, "ITEM_MAGIC_LARGE" }, + { RG_PROGRESSIVE_OCARINA, "ITEM_OCARINA_FAIRY" }, + { RG_PROGRESSIVE_GORONSWORD, "ITEM_SWORD_BGS" }, + { RG_EMPTY_BOTTLE, "ITEM_BOTTLE" }, + { RG_BOTTLE_WITH_MILK, "ITEM_MILK_BOTTLE" }, + { RG_BOTTLE_WITH_RED_POTION, "ITEM_POTION_RED" }, + { RG_BOTTLE_WITH_GREEN_POTION, "ITEM_POTION_GREEN" }, + { RG_BOTTLE_WITH_BLUE_POTION, "ITEM_POTION_BLUE" }, + { RG_BOTTLE_WITH_FAIRY, "ITEM_FAIRY" }, + { RG_BOTTLE_WITH_FISH, "ITEM_FISH" }, + { RG_BOTTLE_WITH_BLUE_FIRE, "ITEM_BLUE_FIRE" }, + { RG_BOTTLE_WITH_BUGS, "ITEM_BUG" }, + { RG_BOTTLE_WITH_POE, "ITEM_POE" }, + { RG_RUTOS_LETTER, "ITEM_LETTER_RUTO" }, + { RG_BOTTLE_WITH_BIG_POE, "ITEM_BIG_POE" }, + { RG_ZELDAS_LULLABY, "ITEM_SONG_LULLABY" }, + { RG_EPONAS_SONG, "ITEM_SONG_EPONA" }, + { RG_SARIAS_SONG, "ITEM_SONG_SARIA" }, + { RG_SUNS_SONG, "ITEM_SONG_SUN" }, + { RG_SONG_OF_TIME, "ITEM_SONG_TIME" }, + { RG_SONG_OF_STORMS, "ITEM_SONG_STORMS" }, + { RG_MINUET_OF_FOREST, "ITEM_SONG_MINUET" }, + { RG_BOLERO_OF_FIRE, "ITEM_SONG_BOLERO" }, + { RG_SERENADE_OF_WATER, "ITEM_SONG_SERENADE" }, + { RG_REQUIEM_OF_SPIRIT, "ITEM_SONG_REQUIEM" }, + { RG_NOCTURNE_OF_SHADOW, "ITEM_SONG_NOCTURNE" }, + { RG_PRELUDE_OF_LIGHT, "ITEM_SONG_PRELUDE" }, + { RG_DEKU_TREE_MAP, "ITEM_DUNGEON_MAP" }, + { RG_DODONGOS_CAVERN_MAP, "ITEM_DUNGEON_MAP" }, + { RG_JABU_JABUS_BELLY_MAP, "ITEM_DUNGEON_MAP" }, + { RG_FOREST_TEMPLE_MAP, "ITEM_DUNGEON_MAP" }, + { RG_FIRE_TEMPLE_MAP, "ITEM_DUNGEON_MAP" }, + { RG_WATER_TEMPLE_MAP, "ITEM_DUNGEON_MAP" }, + { RG_SPIRIT_TEMPLE_MAP, "ITEM_DUNGEON_MAP" }, + { RG_SHADOW_TEMPLE_MAP, "ITEM_DUNGEON_MAP" }, + { RG_BOTTOM_OF_THE_WELL_MAP, "ITEM_DUNGEON_MAP" }, + { RG_ICE_CAVERN_MAP, "ITEM_DUNGEON_MAP" }, + { RG_DEKU_TREE_COMPASS, "ITEM_COMPASS" }, + { RG_DODONGOS_CAVERN_COMPASS, "ITEM_COMPASS" }, + { RG_JABU_JABUS_BELLY_COMPASS, "ITEM_COMPASS" }, + { RG_FOREST_TEMPLE_COMPASS, "ITEM_COMPASS" }, + { RG_FIRE_TEMPLE_COMPASS, "ITEM_COMPASS" }, + { RG_WATER_TEMPLE_COMPASS, "ITEM_COMPASS" }, + { RG_SPIRIT_TEMPLE_COMPASS, "ITEM_COMPASS" }, + { RG_SHADOW_TEMPLE_COMPASS, "ITEM_COMPASS" }, + { RG_BOTTOM_OF_THE_WELL_COMPASS, "ITEM_COMPASS" }, + { RG_ICE_CAVERN_COMPASS, "ITEM_COMPASS" }, + { RG_FOREST_TEMPLE_BOSS_KEY, "ITEM_KEY_BOSS" }, + { RG_FIRE_TEMPLE_BOSS_KEY, "ITEM_KEY_BOSS" }, + { RG_WATER_TEMPLE_BOSS_KEY, "ITEM_KEY_BOSS" }, + { RG_SPIRIT_TEMPLE_BOSS_KEY, "ITEM_KEY_BOSS" }, + { RG_SHADOW_TEMPLE_BOSS_KEY, "ITEM_KEY_BOSS" }, + { RG_GANONS_CASTLE_BOSS_KEY, "ITEM_KEY_BOSS" }, + { RG_FOREST_TEMPLE_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_FIRE_TEMPLE_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_WATER_TEMPLE_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_SPIRIT_TEMPLE_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_SHADOW_TEMPLE_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_BOTTOM_OF_THE_WELL_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_GERUDO_FORTRESS_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_GANONS_CASTLE_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_TREASURE_GAME_SMALL_KEY, "ITEM_KEY_SMALL" }, + { RG_KOKIRI_EMERALD, "ITEM_KOKIRI_EMERALD" }, + { RG_GORON_RUBY, "ITEM_GORON_RUBY" }, + { RG_ZORA_SAPPHIRE, "ITEM_ZORA_SAPPHIRE" }, + { RG_FOREST_MEDALLION, "ITEM_MEDALLION_FOREST" }, + { RG_FIRE_MEDALLION, "ITEM_MEDALLION_FIRE" }, + { RG_WATER_MEDALLION, "ITEM_MEDALLION_WATER" }, + { RG_SPIRIT_MEDALLION, "ITEM_MEDALLION_SPIRIT" }, + { RG_SHADOW_MEDALLION, "ITEM_MEDALLION_SHADOW" }, + { RG_LIGHT_MEDALLION, "ITEM_MEDALLION_LIGHT" }, + { RG_RECOVERY_HEART, "ITEM_HEART_GRAYSCALE" }, + { RG_GREEN_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_GREG_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_BLUE_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_RED_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_PURPLE_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_HUGE_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_TREASURE_GAME_GREEN_RUPEE, "ITEM_RUPEE_GRAYSCALE" }, + { RG_PIECE_OF_HEART, "ITEM_HEART_PIECE" }, + { RG_HEART_CONTAINER, "ITEM_HEART_CONTAINER" }, + { RG_ICE_TRAP, "ITEM_ICE_TRAP" }, + { RG_MILK, "ITEM_MILK_BOTTLE"}, + { RG_BOMBS_5, "ITEM_BOMB" }, + { RG_BOMBS_10, "ITEM_BOMB" }, + { RG_BOMBS_20, "ITEM_BOMB" }, + { RG_BUY_BOMBS_525, "ITEM_BOMB" }, + { RG_BUY_BOMBS_535, "ITEM_BOMB" }, + { RG_BUY_BOMBS_10, "ITEM_BOMB" }, + { RG_BUY_BOMBS_20, "ITEM_BOMB" }, + { RG_BUY_BOMBS_30, "ITEM_BOMB" }, + { RG_DEKU_NUTS_5, "ITEM_NUT" }, + { RG_DEKU_NUTS_10, "ITEM_NUT" }, + { RG_BUY_DEKU_NUTS_5, "ITEM_NUT" }, + { RG_BUY_DEKU_NUTS_10, "ITEM_NUT" }, + { RG_BOMBCHU_5, "ITEM_BOMBCHU" }, + { RG_BOMBCHU_10, "ITEM_BOMBCHU" }, + { RG_BOMBCHU_20, "ITEM_BOMBCHU" }, + { RG_BUY_BOMBCHUS_20, "ITEM_BOMBCHU" }, + { RG_ARROWS_5, "ITEM_ARROWS_SMALL" }, + { RG_BUY_ARROWS_10, "ITEM_ARROWS_SMALL" }, + { RG_ARROWS_10, "ITEM_ARROWS_MEDIUM" }, + { RG_BUY_ARROWS_30, "ITEM_ARROWS_MEDIUM" }, + { RG_ARROWS_30, "ITEM_ARROWS_LARGE" }, + { RG_BUY_ARROWS_50, "ITEM_ARROWS_LARGE" }, + { RG_TREASURE_GAME_HEART, "ITEM_HEART_PIECE" }, + { RG_DEKU_SEEDS_30, "ITEM_SEEDS" }, + { RG_BUY_DEKU_SEEDS_30, "ITEM_SEEDS" }, + { RG_BUY_HEART, "ITEM_HEART_GRAYSCALE" }, + { RG_FISHING_POLE, "ITEM_FISHING_POLE" }, + { RG_SOLD_OUT, "ITEM_SOLD_OUT" }, + { RG_TRIFORCE_PIECE, "TRIFORCE_PIECE" }, + { RG_SKELETON_KEY, "ITEM_KEY_SMALL" } +}; + +Rando::Item plandomizerRandoRetrieveItem(RandomizerGet randoGetItem) { + auto randoGetItemEntry = Rando::StaticData::RetrieveItem(randoGetItem); + return randoGetItemEntry; +} + +ImVec4 plandomizerGetItemColor(Rando::Item randoItem) { + itemColor = ImVec4( 1.0f, 1.0f, 1.0f, 1.0f ); + if (randoItem.GetItemType() == ITEMTYPE_SMALLKEY || randoItem.GetItemType() == ITEMTYPE_FORTRESS_SMALLKEY + || randoItem.GetItemType() == ITEMTYPE_BOSSKEY) { + if (randoItem.GetRandomizerGet() == RG_FOREST_TEMPLE_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_FOREST_TEMPLE_KEY_RING) { + itemColor = ImVec4( 0.02f, 0.76f, 0.18f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_FIRE_TEMPLE_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_FIRE_TEMPLE_KEY_RING) { + itemColor = ImVec4( 0.93f, 0.37f, 0.37f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_WATER_TEMPLE_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_WATER_TEMPLE_KEY_RING) { + itemColor = ImVec4( 0.33f, 0.71f, 0.87f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_SPIRIT_TEMPLE_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_SPIRIT_TEMPLE_KEY_RING) { + itemColor = ImVec4( 0.87f, 0.62f, 0.18f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_SHADOW_TEMPLE_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_SHADOW_TEMPLE_KEY_RING) { + itemColor = ImVec4( 0.49f, 0.06f, 0.69f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_BOTTOM_OF_THE_WELL_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_BOTTOM_OF_THE_WELL_KEY_RING) { + itemColor = ImVec4( 0.89f, 0.43f, 1.0f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_GERUDO_TRAINING_GROUNDS_KEY_RING) { + itemColor = ImVec4( 1.0f, 1.0f, 0, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_GERUDO_FORTRESS_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_GERUDO_FORTRESS_KEY_RING) { + itemColor = ImVec4( 1.0f, 1.0f, 1.0f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_GANONS_CASTLE_SMALL_KEY || + randoItem.GetRandomizerGet() == RG_GANONS_CASTLE_KEY_RING) { + itemColor = ImVec4( 0.5f, 0.5f, 0.5f, 1.0f ); + } + return itemColor; + } + if (randoItem.GetItemType() == ITEMTYPE_SONG) { + uint32_t questID = Rando::Logic::RandoGetToQuestItem[randoItem.GetRandomizerGet()]; + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(songMapping.at((QuestItem)questID).name); + itemColor = songMapping.at((QuestItem)questID).color; + imageSize = ImVec2(24.0f, 32.0f); + imagePadding = 6.0f; + return itemColor; + } + if (randoItem.GetRandomizerGet() >= RG_GREEN_RUPEE && randoItem.GetRandomizerGet() <= RG_HUGE_RUPEE) { + if (randoItem.GetRandomizerGet() == RG_GREG_RUPEE || randoItem.GetRandomizerGet() == RG_GREEN_RUPEE + || randoItem.GetRandomizerGet() == RG_TREASURE_GAME_GREEN_RUPEE) { + itemColor = ImVec4( 0.02f, 0.76f, 0.18f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_BLUE_RUPEE) { + itemColor = ImVec4( 0.33f, 0.71f, 0.87f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_RED_RUPEE) { + itemColor = ImVec4( 0.93f, 0.37f, 0.37f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_PURPLE_RUPEE) { + itemColor = ImVec4( 0.89f, 0.43f, 1.0f, 1.0f ); + } else if (randoItem.GetRandomizerGet() == RG_HUGE_RUPEE) { + itemColor = ImVec4( 1.0f, 1.0f, 0, 1.0f ); + } + return itemColor; + } + + if (randoItem.GetRandomizerGet() >= RG_GOHMA_SOUL && randoItem.GetRandomizerGet() <= RG_GANON_SOUL) { + itemColor = bossSoulMapping.at(randoItem.GetRandomizerGet()); + } + + return itemColor; +} + +std::string plandomizerHintsTooltip() { + std::string hintTootip; + hintTootip = + "The following options are available:\n" + "- Use \\n to create New Lines.\n" + "- Use %g to change the text color to Green,\n" + " - %r for Red, %y for Yellow, and %w for White\n" + " can also be used as color examples."; + + return hintTootip; +} + +std::string extractNumberInParentheses(const std::string& text) { + size_t start = text.find('('); + size_t end = text.find(')'); + + if (start != std::string::npos && end != std::string::npos && start < end) { + return text.substr(start + 1, end - start - 1); + } + return ""; +} + +void PlandomizerPopulateSeedList() { + existingSeedList.clear(); + auto spoilerPath = Ship::Context::GetPathRelativeToAppDirectory("Randomizer"); + + for (const auto& entry : std::filesystem::directory_iterator(spoilerPath)) { + if (entry.is_regular_file() && entry.path().extension() == ".json") { + existingSeedList.push_back(entry.path().stem().string()); + } + } +} + +void PlandomizerItemImageCorrection(Rando::Item randoItem) { + textureID = 0; + imageSize = ImVec2( 32.0f, 32.0f ); + imagePadding = 2.0f; + textureUV0 = ImVec2( 0, 0 ); + textureUV1 = ImVec2( 1, 1 ); + + + itemColor = plandomizerGetItemColor(randoItem); + + if (randoItem.GetItemType() == ITEMTYPE_SMALLKEY || randoItem.GetItemType() == ITEMTYPE_FORTRESS_SMALLKEY) { + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("ITEM_KEY_SMALL"); + return; + } + if (randoItem.GetItemType() == ITEMTYPE_BOSSKEY) { + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("ITEM_KEY_BOSS"); + return; + } + + for (auto& map : itemImageMap) { + if (map.first == randoItem.GetRandomizerGet()) { + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(map.second.c_str()); + if (map.second.find("ITEM_ARROWS") != std::string::npos) { + textureUV0 = ImVec2( 0, 1 ); + textureUV1 = ImVec2( 1, 0 ); + } + if (map.second == "ITEM_TRIFORCE" || map.first == RG_SKELETON_KEY) { + textureUV0 = ImVec2( 1, 1 ); + textureUV1 = ImVec2( 0, 0 ); + } + break; + } + } + + if (randoItem.GetRandomizerGet() >= RG_GOHMA_SOUL && randoItem.GetRandomizerGet() <= RG_GANON_SOUL) { + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("BOSS_SOUL"); + } + + if (randoItem.GetRandomizerGet() >= RG_OCARINA_A_BUTTON && randoItem.GetRandomizerGet() <= RG_OCARINA_C_RIGHT_BUTTON) { + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("ITEM_OCARINA_TIME"); + } + + if (textureID == 0) { + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(itemMapping[randoItem.GetGIEntry()->itemId].name); + } +} + +void PlandomizerRandomizeHint(int32_t status, int32_t index) { + if (status == HINT_SINGLE) { + plandoHintData[index].hintText = Rando::StaticData::hintTextTable[GetRandomJunkHint()].GetHintMessage().GetForCurrentLanguage(MF_ENCODE); + } else { + for (auto& hint : plandoHintData) { + hint.hintText = Rando::StaticData::hintTextTable[GetRandomJunkHint()].GetHintMessage().GetForCurrentLanguage(MF_ENCODE); + } + } +} + +void PlandomizerRemoveAllHints() { + if (plandoHintData.size() > 0) { + for (auto& remove : plandoHintData) { + remove.hintText.clear(); + } + } +} + +void PlandomizerSortDrawnItems() { + std::sort(drawnItemsList.begin(), drawnItemsList.end(), + [](const auto& a, const auto& b) { + auto typeA = a.first.GetItemType(); + auto typeB = b.first.GetItemType(); + if (typeA != typeB){ + return typeA < typeB; + } + return a.first.GetRandomizerGet() < b.first.GetRandomizerGet(); + }); +} + +void PlandomizerRemoveAllItems() { + if (drawnItemsList.size() == 1) { + drawnItemsList.clear(); + } + for (auto& remove : plandoLogData) { + if (std::find(infiniteItemList.begin(), infiniteItemList.end(), remove.checkRewardItem.GetRandomizerGet()) == infiniteItemList.end()) { + bool itemExists = false; + for (auto& itemToCheck : drawnItemsList) { + if (itemToCheck.first.GetRandomizerGet() == remove.checkRewardItem.GetRandomizerGet()) { + itemToCheck.second += 1; + itemExists = true; + break; + } + } + if (!itemExists) { + drawnItemsList.push_back(std::make_pair(remove.checkRewardItem, 1)); + } + } + remove.checkRewardItem = plandomizerRandoRetrieveItem(RG_SOLD_OUT); + } + PlandomizerSortDrawnItems(); +} + +void PlandomizerRemoveFromItemList(Rando::Item randoItem) { + if (std::find(infiniteItemList.begin(), infiniteItemList.end(), randoItem.GetRandomizerGet()) == infiniteItemList.end()) { + uint32_t index = 0; + for (auto& itemToCheck : drawnItemsList) { + if (itemToCheck.first.GetRandomizerGet() == randoItem.GetRandomizerGet()) { + if (shouldRemove) { + drawnItemsList.erase(drawnItemsList.begin() + index); + break; + } else { + itemToCheck.second -= 1; + } + } + index++; + } + shouldRemove = false; + } + PlandomizerSortDrawnItems(); +} + +void PlandomizerAddToItemList(Rando::Item randoItem) { + if (std::find(infiniteItemList.begin(), infiniteItemList.end(), randoItem.GetRandomizerGet()) == infiniteItemList.end()) { + bool itemExists = false; + for (auto& itemToCheck : drawnItemsList) { + if (itemToCheck.first.GetRandomizerGet() == randoItem.GetRandomizerGet()) { + itemToCheck.second += 1; + itemExists = true; + break; + } + } + + if (!itemExists) { + drawnItemsList.push_back(std::make_pair(randoItem, 1)); + } + } + PlandomizerSortDrawnItems(); +} + +void PlandomizerSaveSpoilerLog() { + nlohmann::json spoilerSave; + std::string filename = lastLoadedSpoiler; + + std::ifstream inputFile(filename); + if (inputFile.is_open()) { + inputFile >> spoilerSave; + inputFile.close(); + } + + spoilerSave["file_hash"] = { + plandoHash[0], plandoHash[1], plandoHash[2], plandoHash[3], plandoHash[4] + }; + + for (auto& import : plandoHintData) { + spoilerSave["Gossip Stone Hints"][import.hintName] = { + { "type", import.hintType.c_str() }, + { "message", import.hintText.c_str() } + }; + } + + for (auto& import : plandoLogData) { + if (import.checkRewardItem.GetRandomizerGet() == RG_ICE_TRAP) { + spoilerSave["locations"][import.checkName] = { + { "item", import.checkRewardItem.GetName().english }, + { "model", import.iceTrapModel.GetName().english }, + { "trickName", import.iceTrapName.c_str() } + }; + if (import.shopPrice > -1) { + spoilerSave["locations"][import.checkName]["price"] = import.shopPrice; + } + } else if (import.shopPrice > -1) { + spoilerSave["locations"][import.checkName] = { + { "item", import.checkRewardItem.GetName().english }, + { "price", import.shopPrice } + }; + } else { + spoilerSave["locations"][import.checkName] = import.checkRewardItem.GetName().english; + } + } + + std::ofstream outputFile(filename); + if (outputFile.is_open()) { + outputFile << spoilerSave.dump(4); + outputFile.close(); + } +} + +void PlandomizerLoadSpoilerLog(std::string logFile) { + spoilerHash.clear(); + plandoHash.clear(); + spoilerLogData.clear(); + plandoLogData.clear(); + spoilerHintData.clear(); + plandoHintData.clear(); + drawnItemsList.clear(); + + nlohmann::json spoilerLogInput; + auto spoilerPath = Ship::Context::GetPathRelativeToAppDirectory("Randomizer"); + std::string spoilerStr = spoilerPath + "/" + logFile.c_str() + ".json"; + + if (!std::filesystem::exists(spoilerStr)) { + return; + } + + std::ifstream file(spoilerStr); + + if (file.is_open()) { + try { + file >> spoilerLogInput; + file.close(); + + if (spoilerLogInput.contains("file_hash")) { + auto hash = spoilerLogInput["file_hash"]; + for (auto& load : hash) { + spoilerHash.push_back(load); + plandoHash.push_back(load); + } + } + + if (spoilerLogInput.contains("Gossip Stone Hints")) { + auto hints = spoilerLogInput["Gossip Stone Hints"]; + for (auto& [key, value] : hints.items()) { + SpoilerHintObject hintObject; + hintObject.hintName = key.c_str(); + hintObject.hintType = "Hardcoded Message"; + hintObject.hintText = value["message"]; + + spoilerHintData.push_back(hintObject); + plandoHintData.push_back(hintObject); + } + } + + if (spoilerLogInput.contains("locations")) { + auto locations = spoilerLogInput["locations"]; + for (auto& [key, value] : locations.items()) { + if (key == "Ganon" || key == "Completed Triforce") { + continue; + } + SpoilerCheckObject checkObject; + checkObject.checkName = key; + auto type = value; + if (value.is_object()) { + checkObject.checkRewardItem = plandomizerRandoRetrieveItem(Rando::StaticData::itemNameToEnum[value["item"]]); + if (value["price"].is_number()) { + checkObject.shopPrice = value["price"]; + } else { + checkObject.shopPrice = -1; + } + if (checkObject.checkRewardItem.GetRandomizerGet() == RG_ICE_TRAP) { + checkObject.iceTrapModel = plandomizerRandoRetrieveItem(Rando::StaticData::itemNameToEnum[value["model"]]); + checkObject.iceTrapName = value["trickName"]; + } + } else { + checkObject.checkRewardItem = plandomizerRandoRetrieveItem(Rando::StaticData::itemNameToEnum[value.get()]); + checkObject.shopPrice = -1; + if (checkObject.shopPrice == -1 + && checkObject.checkRewardItem.GetName().english.find("Buy") != std::string::npos) { + checkObject.shopPrice = checkObject.checkRewardItem.GetPrice(); + } + } + spoilerLogData.push_back(checkObject); + plandoLogData.push_back(checkObject); + PlandomizerAddToItemList(plandomizerRandoRetrieveItem(RG_SOLD_OUT)); + } + } + } catch (nlohmann::json::parse_error& e) { + Notification::Emit({ .message = "Invalid Spoiler Log Format", .remainingTime = 10.0f }); + } + } + lastLoadedSpoiler = spoilerStr; +} + +void PlandomizerOverlayText(std::pair drawObject ) { + // Overlay the item count text on the existing button + ImVec2 imageMin = ImGui::GetItemRectMin(); + ImVec2 imageMax = ImGui::GetItemRectMax(); + ImVec2 textPos = ImVec2(imageMax.x - ImGui::CalcTextSize(std::to_string(drawObject.second).c_str()).x - 2, + imageMax.y - ImGui::CalcTextSize(std::to_string(drawObject.second).c_str()).y - 2); + + ImGui::SetCursorScreenPos(textPos); + ImGui::Text(std::to_string(drawObject.second).c_str()); + + // Overlay item info + if (drawObject.first.GetRandomizerGet() >= RG_PROGRESSIVE_HOOKSHOT && + drawObject.first.GetRandomizerGet() <= RG_PROGRESSIVE_GORONSWORD) { + textPos = ImVec2(imageMin.x + 2, imageMin.y + 2); + + ImGui::SetCursorScreenPos(textPos); + ImGui::Text("+"); + } + if (extractNumberInParentheses(drawObject.first.GetName().english.c_str()) != "" && + extractNumberInParentheses(drawObject.first.GetName().english.c_str()) != "WINNER" && + extractNumberInParentheses(drawObject.first.GetName().english.c_str()) != "LOSER") { + textPos = ImVec2(imageMin.x + 2, imageMin.y + 2); + + ImGui::SetCursorScreenPos(textPos); + std::string overlayText = "+"; + overlayText += extractNumberInParentheses(drawObject.first.GetName().english.c_str()); + ImGui::Text(overlayText.c_str()); + } + if (drawObject.first.GetRandomizerGet() >= RG_FOREST_TEMPLE_BOSS_KEY && + drawObject.first.GetRandomizerGet() <= RG_GANONS_CASTLE_BOSS_KEY) { + textPos = ImVec2(imageMin.x + 1, imageMin.y + 1); + ImGui::SetCursorScreenPos(textPos); + shortName = ""; + for (auto& name : bossKeyShortNames) { + if (name.first == drawObject.first.GetRandomizerGet()) { + shortName = name.second; + break; + } + } + ImGui::Text(shortName.c_str()); + } + if (drawObject.first.GetRandomizerGet() >= RG_OCARINA_A_BUTTON && + drawObject.first.GetRandomizerGet() <= RG_OCARINA_C_RIGHT_BUTTON) { + textPos = ImVec2(imageMin.x + 1, imageMin.y + 1); + ImGui::SetCursorScreenPos(textPos); + shortName = ""; + for (auto& name : ocarinaButtonNames) { + if (name.first == drawObject.first.GetRandomizerGet()) { + shortName = name.second; + break; + } + } + ImGui::Text(shortName.c_str()); + } +} + +void PlandomizerDrawItemPopup(uint32_t index) { + if (shouldPopup && ImGui::BeginPopup("ItemList")) { + ImGui::SeparatorText("Resources"); + ImGui::BeginTable("Infinite Item Table", 7); + for (auto& item : infiniteItemList) { + ImGui::PushID(item); + ImGui::TableNextColumn(); + PlandomizerItemImageCorrection(plandomizerRandoRetrieveItem(item)); + if (ImGui::ImageButton(textureID, + imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) { + if (std::find(infiniteItemList.begin(), infiniteItemList.end(), plandoLogData[index].checkRewardItem.GetRandomizerGet()) == infiniteItemList.end()) { + PlandomizerAddToItemList(plandoLogData[index].checkRewardItem); + } + plandoLogData[index].checkRewardItem = plandomizerRandoRetrieveItem(item); + ImGui::CloseCurrentPopup(); + } + UIWidgets::Tooltip(plandomizerRandoRetrieveItem(item).GetName().english.c_str()); + PlandomizerOverlayText(std::make_pair(plandomizerRandoRetrieveItem(item), 1)); + ImGui::PopID(); + } + + + ImGui::EndTable(); + ImGui::SeparatorText("Spoiler Log Rewards"); + ImGui::BeginTable("Item Button Table", 8); + uint32_t itemIndex = 0; + + bool isClicked = false; + for (auto& drawSlots : drawnItemsList) { + ImGui::TableNextColumn(); + ImGui::BeginGroup(); + ImGui::PushID(itemIndex); + auto itemToDraw = drawSlots.first; + PlandomizerItemImageCorrection(drawSlots.first); + if (ImGui::ImageButton(textureID, + imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) { + if (itemToDraw.GetRandomizerGet() >= RG_PROGRESSIVE_HOOKSHOT && + itemToDraw.GetRandomizerGet() <= RG_PROGRESSIVE_GORONSWORD) { + plandoLogData[index].checkRewardItem = drawSlots.first; + } else { + plandoLogData[index].checkRewardItem = itemToDraw; + } + temporaryItemIndex = itemIndex; + if (drawSlots.second == 1) { + shouldRemove = true; + } + isClicked = true; + ImGui::CloseCurrentPopup(); + } + if (!isClicked) { + UIWidgets::Tooltip(drawSlots.first.GetName().english.c_str()); + } + ImGui::PopID(); + + PlandomizerOverlayText(drawSlots); + + ImGui::EndGroup(); + itemIndex++; + } + if (isClicked) { + PlandomizerRemoveFromItemList(drawnItemsList[temporaryItemIndex].first); + PlandomizerAddToItemList(temporaryItem); + } + ImGui::EndTable(); + ImGui::EndPopup(); + } +} + +void PlandomizerDrawIceTrapPopUp(uint32_t index) { + if (shouldTrapPopup && ImGui::BeginPopup("TrapList")) { + ImGui::BeginTable("Ice Trap Table", 8); + for (auto& items : itemImageMap) { + if (items.first == RG_ICE_TRAP) { + continue; + } + ImGui::TableNextColumn(); + ImGui::PushID(items.first); + PlandomizerItemImageCorrection(Rando::StaticData::RetrieveItem(items.first)); + if (ImGui::ImageButton(textureID, imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) { + plandoLogData[index].iceTrapModel = Rando::StaticData::RetrieveItem(items.first); + ImGui::CloseCurrentPopup(); + }; + UIWidgets::Tooltip(Rando::StaticData::RetrieveItem(items.first).GetName().english.c_str()); + + auto itemObject = Rando::StaticData::RetrieveItem(items.first); + PlandomizerOverlayText(std::make_pair(itemObject, 1)); + + ImGui::PopID(); + } + ImGui::EndTable(); + ImGui::EndPopup(); + } +} + +void PlandomizerDrawItemSlots(uint32_t index) { + ImGui::PushID(index); + PlandomizerItemImageCorrection(plandoLogData[index].checkRewardItem); + if (ImGui::ImageButton(textureID, imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) { + shouldPopup = true; + temporaryItem = plandoLogData[index].checkRewardItem; + ImGui::OpenPopup("ItemList"); + }; + UIWidgets::Tooltip(plandoLogData[index].checkRewardItem.GetName().english.c_str()); + PlandomizerOverlayText(std::make_pair(plandoLogData[index].checkRewardItem, 1)); + PlandomizerDrawItemPopup(index); + ImGui::PopID(); +} + +void PlandomizerDrawShopSlider(uint32_t index) { + ImGui::PushID(index); + ImGui::Text("Price:"); + ImGui::SameLine(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - 20.0f); + ImGui::SliderInt("", &plandoLogData[index].shopPrice, 0, 999, "%d Rupees"); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void PlandomizerDrawIceTrapSetup(uint32_t index) { + std::string trapTextInput = plandoLogData[index].iceTrapName.c_str(); + + ImGui::PushID(index); + ImGui::BeginTable("IceTrap", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInner); + ImGui::TableSetupColumn("Model", ImGuiTableColumnFlags_WidthFixed, 36.0f); + ImGui::TableSetupColumn("Trap Options"); + ImGui::TableHeadersRow(); + + ImGui::TableNextColumn(); + PlandomizerItemImageCorrection(plandoLogData[index].iceTrapModel); + if (ImGui::ImageButton(textureID, imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) { + shouldTrapPopup = true; + ImGui::OpenPopup("TrapList"); + }; + UIWidgets::Tooltip(plandoLogData[index].iceTrapModel.GetName().english.c_str()); + PlandomizerDrawIceTrapPopUp(index); + ImGui::SameLine(); + ImGui::TableNextColumn(); + ImGui::Text("Name: "); + ImGui::SameLine(); + if (plandoLogData[index].iceTrapModel.GetRandomizerGet() != RG_NONE && + plandoLogData[index].iceTrapModel.GetRandomizerGet() != RG_SOLD_OUT) { + if (ImGui::Button(randomizeButton.c_str())) { + plandoLogData[index].iceTrapName = + GetIceTrapName(plandoLogData[index].iceTrapModel.GetRandomizerGet()).GetForLanguage(CVarGetInteger(CVAR_SETTING("Languages"), 0)).c_str(); + } + ImGui::SameLine(); + } + if (UIWidgets::InputString("##TrapName", &trapTextInput)) { + plandoLogData[index].iceTrapName = trapTextInput.c_str(); + } + + if (plandoLogData[index].shopPrice > -1) { + PlandomizerDrawShopSlider(index); + } + ImGui::EndTable(); + + ImGui::PopID(); +} + +void PlandomizerDrawOptions() { + ImGui::BeginTable("LoadSpoiler", 2); + ImGui::TableNextColumn(); + ImGui::SeparatorText("Load/Save Spoiler Log"); + PlandomizerPopulateSeedList(); + static int32_t selectedList = 0; + if (existingSeedList.size() != 0) { + if (ImGui::BeginCombo("##JsonFiles", existingSeedList[selectedList].c_str())) { + for (size_t i = 0; i < existingSeedList.size(); i++) { + bool isSelected = (selectedList == i); + if (ImGui::Selectable(existingSeedList[i].c_str(), isSelected)) { + selectedList = i; + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + } else { + ImGui::Text("No Spoiler Logs found."); + } + + if (ImGui::Button("Load")) { + logTemp = existingSeedList[selectedList].c_str(); + PlandomizerLoadSpoilerLog(logTemp.c_str()); + } + ImGui::SameLine(); + if (ImGui::Button("Save")) { + PlandomizerSaveSpoilerLog(); + } + + ImGui::TableNextColumn(); + ImGui::SeparatorText("Current Seed Hash"); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (ImGui::GetContentRegionAvail().x * 0.5f) - (34.0f * 5.0f)); + if (spoilerLogData.size() > 0) { + ImGui::BeginTable("HashIcons", 5); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); + for (int i = 0; i < 5; i++) { + ImGui::TableSetupColumn("Icon", ImGuiTableColumnFlags_WidthFixed, 34.0f); + } + ImGui::TableNextColumn(); + + int32_t index = 0; + for (auto& hash : plandoHash) { + ImGui::PushID(index); + textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gSeedTextures[hash].tex); + if (ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("HASH_ARROW_UP"), + ImVec2(35.0f, 18.0f), ImVec2(1, 1), ImVec2(0, 0), 2.0f, ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1))) { + if (hash + 1 >= gSeedTextures.size()) { + hash = 0; + } else { + hash++; + } + } + ImGui::Image(textureID, ImVec2(35.0f, 35.0f)); + if (ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("HASH_ARROW_DWN"), + ImVec2(35.0f, 18.0f), ImVec2(0, 0), ImVec2(1, 1), 2.0f, ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1))) { + if (hash == 0) { + hash = gSeedTextures.size() - 1; + } else { + hash--; + } + } + if (index != spoilerHash.size() - 1) { + ImGui::TableNextColumn(); + } + ImGui::PopID(); + index++; + } + ImGui::PopStyleColor(3); + ImGui::EndTable(); + } else { + ImGui::Text("No Spoiler Log Loaded"); + } + ImGui::EndTable(); + + ImGui::SeparatorText("Options"); + if (plandoLogData.size() == 0) { + ImGui::Text("Please Load Spoiler Data..."); + return; + } + + if (getTabID == TAB_HINTS) { + if (ImGui::Button("Clear All Hints")) { + PlandomizerRemoveAllHints(); + } + ImGui::SameLine(); + if (ImGui::Button("Randomize All Hints")) { + PlandomizerRandomizeHint(HINT_ALL, 0); + } + } + if (getTabID == TAB_LOCATIONS) { + if (plandoLogData.size() > 0) { + const char* comboLabel = rcAreaNames[selectedArea].c_str(); + if (selectedArea == RCAREA_INVALID) { + comboLabel = "All"; + } + ImGui::Text("Filter by Area:"); + ImGui::SameLine(); + ImGui::PushItemWidth(300.0f); + if (ImGui::BeginCombo("##AreaFilter", comboLabel)) { + for (const auto& [area, name] : rcAreaNames) { + bool isSelected = (selectedArea == area); + + const char* displayName = name.c_str(); + if (area == RCAREA_INVALID) { + displayName = "All"; + } + if (ImGui::Selectable(displayName, isSelected)) { + selectedArea = area; + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + ImGui::SameLine(); + if (ImGui::Button("Empty All Rewards")) { + PlandomizerRemoveAllItems(); + } + } + } +} + +void PlandomizerDrawHintsWindow() { + uint32_t index = 0; + std::string hintInputText; + + ImGui::BeginChild("Hints"); + ImGui::BeginTable("Hints Window", 1, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY); + ImGui::TableSetupColumn("Hint Entries"); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); + + for (auto& hintData : spoilerHintData) { + ImGui::PushID(index); + ImGui::TableNextColumn(); + ImGui::SeparatorText(hintData.hintName.c_str()); + ImGui::Text("Current Hint: "); + ImGui::SameLine(); + ImGui::TextWrapped(hintData.hintText.c_str()); + + if (spoilerHintData.size() > 0) { + hintInputText = plandoHintData[index].hintText.c_str(); + } + ImGui::Text("New Hint: "); + ImGui::SameLine(); + if (ImGui::Button(randomizeButton.c_str())) { + PlandomizerRandomizeHint(HINT_SINGLE, index); + } + UIWidgets::Tooltip("Randomize Hint"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 10); + if (UIWidgets::InputString("##HintMessage", &hintInputText)) { + plandoHintData[index].hintText = hintInputText.c_str(); + } + UIWidgets::Tooltip(plandomizerHintsTooltip().c_str()); + index++; + ImGui::PopID(); + } + + ImGui::EndTable(); + ImGui::EndChild(); +} + +void PlandomizerDrawLocationsWindow(RandomizerCheckArea rcArea) { + uint32_t index = 0; + ImGui::BeginChild("Locations"); + ImGui::BeginTable("Locations Window", 4, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY); + ImGui::TableSetupColumn("Spoiler Log Check Name", ImGuiTableColumnFlags_WidthFixed, 250.0f); + ImGui::TableSetupColumn("Spoiler Log Reward", ImGuiTableColumnFlags_WidthFixed, 190.0f); + ImGui::TableSetupColumn("New Reward", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHeaderLabel, 34.0f); + ImGui::TableSetupColumn("Additional Options"); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); + + for (auto& spoilerData : spoilerLogData) { + auto checkID = Rando::StaticData::locationNameToEnum[spoilerData.checkName]; + auto randoArea = Rando::StaticData::GetLocation(checkID)->GetArea(); + if (rcArea == RCAREA_INVALID || rcArea == randoArea) { + ImGui::TableNextColumn(); + ImGui::TextWrapped(spoilerData.checkName.c_str()); + ImGui::TableNextColumn(); + ImGui::TextWrapped(spoilerData.checkRewardItem.GetName().english.c_str()); + ImGui::TableNextColumn(); + PlandomizerDrawItemSlots(index); + if (plandoLogData[index].checkRewardItem.GetRandomizerGet() == RG_ICE_TRAP) { + ImGui::TableNextColumn(); + PlandomizerDrawIceTrapSetup(index); + } else if (spoilerData.shopPrice != -1) { + ImGui::TableNextColumn(); + ImGui::BeginTable("Shops", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInner); + ImGui::TableSetupColumn("Shop Price"); + ImGui::TableHeadersRow(); + ImGui::TableNextColumn(); + PlandomizerDrawShopSlider(index); + ImGui::EndTable(); + } else { + ImGui::TableNextColumn(); + } + } + index++; + } + ImGui::PopStyleColor(3); + ImGui::EndTable(); + ImGui::EndChild(); +} + +void PlandomizerDrawSpoilerTable() { + ImGui::BeginChild("Main"); + if (ImGui::BeginTabBar("Check Tabs")) { + if (ImGui::BeginTabItem("Gossip Stones")) { + getTabID = TAB_HINTS; + PlandomizerDrawHintsWindow(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Locations")) { + getTabID = TAB_LOCATIONS; + PlandomizerDrawLocationsWindow(selectedArea); + ImGui::EndTabItem(); + } + } + ImGui::EndTabBar(); + ImGui::EndChild(); +} + +void PlandomizerWindow::DrawElement() { + PlandomizerDrawOptions(); + UIWidgets::PaddedSeparator(); + PlandomizerDrawSpoilerTable(); +} + +void PlandomizerWindow::InitElement() { + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_RUPEE_GRAYSCALE", gRupeeCounterIconTex, ImVec4(1, 1, 1, 1)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_HEART_GRAYSCALE", gHeartFullTex, ImVec4(0.87f, 0.10f, 0.10f, 1)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_SEEDS", gItemIconDekuSeedsTex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_ARROWS_SMALL", gDropArrows1Tex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_ARROWS_MEDIUM", gDropArrows2Tex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_ARROWS_LARGE", gDropArrows3Tex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ITEM_ICE_TRAP", gMagicArrowEquipEffectTex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("HASH_ARROW_UP", gEmptyCDownArrowTex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("HASH_ARROW_DWN", gEmptyCDownArrowTex, ImVec4( 1, 1, 1, 1 )); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("BOSS_SOUL", gBossSoulTex, ImVec4(1, 1, 1, 1)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("TRIFORCE_PIECE", gTriforcePieceTex, ImVec4(1, 1, 1, 1)); +} diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.h b/soh/soh/Enhancements/randomizer/Plandomizer.h new file mode 100644 index 000000000..d337e4da6 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/Plandomizer.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef PLANDOMIZER_H +#define PLANDOMIZER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // PLANDOMIZER_H + +#include +#include "soh/Enhancements/randomizer/item.h" + +#ifdef __cplusplus +class PlandomizerWindow : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override{}; +}; + +typedef struct { + std::string checkName; + Rando::Item checkRewardItem; + int32_t shopPrice; + Rando::Item iceTrapModel; + std::string iceTrapName; +} SpoilerCheckObject; + +typedef struct { + std::string hintName; + std::string hintType; + std::string hintText; +} SpoilerHintObject; + +typedef enum { + TAB_HINTS, + TAB_LOCATIONS +}; + +typedef enum { + HINT_SINGLE, + HINT_ALL, +}; + +#endif \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/rando_hash.h b/soh/soh/Enhancements/randomizer/rando_hash.h index 76c3ce0a3..3fdcd205e 100644 --- a/soh/soh/Enhancements/randomizer/rando_hash.h +++ b/soh/soh/Enhancements/randomizer/rando_hash.h @@ -10,7 +10,7 @@ #include #include -std::array gSeedTextures = { { +inline std::array gSeedTextures = { { { dgItemIconDekuNutTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 }, { dgItemIconDekuStickTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 1 }, { dgItemIconBombTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 2 }, diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 4cda80c37..fe8910c6d 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -2627,6 +2627,35 @@ typedef enum { RHT_JUNK_SG_6, RHT_JUNK_SG_7, RHT_JUNK_SG_8, + RHT_JUNK_CREW_1, + RHT_JUNK_CREW_2, + RHT_JUNK_CREW_3, + RHT_JUNK_CREW_4, + RHT_JUNK_CREW_5, + RHT_JUNK_CREW_6, + RHT_JUNK_CREW_7, + RHT_JUNK_CREW_8, + RHT_JUNK_CREW_9, + RHT_JUNK_CREW_10, + RHT_JUNK_CREW_11, + RHT_JUNK_CREW_12, + RHT_JUNK_CREW_13, + RHT_JUNK_CREW_14, + RHT_JUNK_CREW_15, + RHT_JUNK_CREW_16, + RHT_JUNK_CREW_17, + RHT_JUNK_CREW_18, + RHT_JUNK_CREW_19, + RHT_JUNK_CREW_20, + RHT_JUNK_CREW_21, + RHT_JUNK_CREW_22, + RHT_JUNK_CREW_23, + RHT_JUNK_CREW_24, + RHT_JUNK_CREW_25, + RHT_JUNK_CREW_26, + RHT_JUNK_CREW_27, + RHT_JUNK_CREW_28, + RHT_JUNK_CREW_29, // Locations RHT_LINKS_POCKET, RHT_QUEEN_GOHMA, diff --git a/soh/soh/ImGuiUtils.cpp b/soh/soh/ImGuiUtils.cpp index 11ad464e8..bfe1293d5 100644 --- a/soh/soh/ImGuiUtils.cpp +++ b/soh/soh/ImGuiUtils.cpp @@ -1,6 +1,7 @@ #include "ImGuiUtils.h" #include #include "assets/soh_assets.h" +#include "soh/Enhancements/randomizer/rando_hash.h" std::map itemMapping = { ITEM_MAP_ENTRY(ITEM_STICK), @@ -150,7 +151,7 @@ std::map questMapping = { QUEST_MAP_ENTRY(QUEST_SKULL_TOKEN, dgQuestIconGoldSkulltulaTex), }; -std::array songMapping = { { +std::map songMapping = { SONG_MAP_ENTRY(QUEST_SONG_LULLABY, 224, 107, 255), SONG_MAP_ENTRY(QUEST_SONG_EPONA, 255, 195, 60), SONG_MAP_ENTRY(QUEST_SONG_SARIA, 127, 255, 137), @@ -163,7 +164,7 @@ std::array songMapping = { { SONG_MAP_ENTRY(QUEST_SONG_REQUIEM, 255, 160, 0), SONG_MAP_ENTRY(QUEST_SONG_NOCTURNE, 255, 100, 255), SONG_MAP_ENTRY(QUEST_SONG_PRELUDE, 255, 240, 100), -} }; +}; std::array vanillaSongMapping = { { VANILLA_SONG_MAP_ENTRY(QUEST_SONG_LULLABY, 255, 255, 255), @@ -212,7 +213,7 @@ void RegisterImGuiItemIcons() { Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.nameFaded, entry.second.texturePath, ImVec4(1, 1, 1, 0.3f)); } - for (const auto& entry : songMapping) { + for (const auto& [quest, entry] : songMapping) { Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.name, gSongNoteTex, entry.color); ImVec4 fadedCol = entry.color; fadedCol.w = 0.3f; @@ -225,4 +226,8 @@ void RegisterImGuiItemIcons() { fadedCol.w = 0.3f; Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.nameFaded, gSongNoteTex, fadedCol); } + + for (const auto& entry : gSeedTextures) { + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.tex, entry.tex, ImVec4(1, 1, 1, 1)); + } } \ No newline at end of file diff --git a/soh/soh/ImGuiUtils.h b/soh/soh/ImGuiUtils.h index c5545ba74..ed735713a 100644 --- a/soh/soh/ImGuiUtils.h +++ b/soh/soh/ImGuiUtils.h @@ -67,11 +67,11 @@ typedef struct { #define SONG_MAP_ENTRY(id, r, g, b) \ { \ - id, #id, #id "_Faded", ImVec4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f) \ + id, { id, #id, #id "_Faded", ImVec4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f) } \ } // Maps song ids to info for use in ImGui -extern std::array songMapping; +extern std::map songMapping; #define VANILLA_SONG_MAP_ENTRY(id, r, g, b) \ { \ diff --git a/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp index c70f544b0..19b8639de 100644 --- a/soh/soh/SohGui.cpp +++ b/soh/soh/SohGui.cpp @@ -131,6 +131,7 @@ namespace SohGui { std::shared_ptr mItemTrackerSettingsWindow; std::shared_ptr mItemTrackerWindow; std::shared_ptr mTimeSplitWindow; + std::shared_ptr mPlandomizerWindow; std::shared_ptr mRandomizerSettingsWindow; std::shared_ptr mAdvancedResolutionSettingsWindow; std::shared_ptr mModalWindow; @@ -210,6 +211,8 @@ namespace SohGui { gui->AddGuiWindow(mRandomizerSettingsWindow); mTimeSplitWindow = std::make_shared(CVAR_WINDOW("TimeSplitEnabled"), "Time Splits", ImVec2(450, 660)); gui->AddGuiWindow(mTimeSplitWindow); + mPlandomizerWindow = std::make_shared(CVAR_WINDOW("PlandomizerWindow"), "Plandomizer Editor", ImVec2(850, 760)); + gui->AddGuiWindow(mPlandomizerWindow); mAdvancedResolutionSettingsWindow = std::make_shared(CVAR_WINDOW("AdvancedResolutionEditor"), "Advanced Resolution Settings", ImVec2(497, 599)); gui->AddGuiWindow(mAdvancedResolutionSettingsWindow); mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); @@ -252,6 +255,7 @@ namespace SohGui { mInputViewer = nullptr; mInputViewerSettings = nullptr; mTimeSplitWindow = nullptr; + mPlandomizerWindow = nullptr; } void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function button1callback, std::function button2callback) { diff --git a/soh/soh/SohGui.hpp b/soh/soh/SohGui.hpp index 1c8eecb79..5502b6757 100644 --- a/soh/soh/SohGui.hpp +++ b/soh/soh/SohGui.hpp @@ -25,6 +25,7 @@ #include "Enhancements/randomizer/randomizer_item_tracker.h" #include "Enhancements/randomizer/randomizer_settings_window.h" #include "Enhancements/timesplits/TimeSplits.h" +#include "Enhancements/randomizer/Plandomizer.h" #include "SohModals.h" #ifdef __cplusplus diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 71336671b..beadebfae 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -41,6 +41,7 @@ #include "Enhancements/resolution-editor/ResolutionEditor.h" #include "Enhancements/enemyrandomizer.h" #include "Enhancements/timesplits/TimeSplits.h" +#include "Enhancements/randomizer/Plandomizer.h" // FA icons are kind of wonky, if they worked how I expected them to the "+ 2.0f" wouldn't be needed, but // they don't work how I expect them to so I added that because it looked good when I eyeballed it @@ -2046,6 +2047,7 @@ extern std::shared_ptr mEntranceTrackerWindow; extern std::shared_ptr mEntranceTrackerSettingsWindow; extern std::shared_ptr mCheckTrackerWindow; extern std::shared_ptr mCheckTrackerSettingsWindow; +extern std::shared_ptr mPlandomizerWindow; extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); void DrawRandomizerMenu() { @@ -2075,6 +2077,14 @@ void DrawRandomizerMenu() { static float separationToOptionsButton = 5.0f; #endif + if (mPlandomizerWindow) { + if (ImGui::Button(GetWindowButtonText("Plandomizer Editor", CVarGetInteger(CVAR_WINDOW("PlandomizerWindow"), 0)).c_str(), buttonSize)) { + mPlandomizerWindow->ToggleVisibility(); + } + } + + UIWidgets::Spacer(0); + if (mRandomizerSettingsWindow) { if (ImGui::Button(GetWindowButtonText("Randomizer Settings", CVarGetInteger(CVAR_WINDOW("RandomizerSettings"), 0)).c_str(), buttonSize)) { mRandomizerSettingsWindow->ToggleVisibility();