From f1357f09ed95f69acf49445deec42eb208c75be3 Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Tue, 2 Jun 2026 22:15:34 -0700 Subject: [PATCH] add starting inventory management to UI --- .../data/text/languages/english.yaml | 92 +++++ .../generator/data/text/languages/french.yaml | 92 +++++ .../generator/data/text/languages/german.yaml | 92 +++++ .../data/text/languages/italian.yaml | 92 +++++ .../data/text/languages/spanish.yaml | 92 +++++ src/dusk/randomizer/generator/logic/hints.cpp | 2 +- .../randomizer/generator/seedgen/config.hpp | 1 + .../randomizer/generator/seedgen/settings.hpp | 1 + .../randomizer/generator/utility/text.cpp | 6 +- .../randomizer/generator/utility/text.hpp | 4 +- src/dusk/ui/rando_config.cpp | 315 +++++++++++++++++- src/dusk/ui/select_button.cpp | 1 + 12 files changed, 786 insertions(+), 4 deletions(-) diff --git a/src/dusk/randomizer/generator/data/text/languages/english.yaml b/src/dusk/randomizer/generator/data/text/languages/english.yaml index aa0eaac028..ef572b9826 100644 --- a/src/dusk/randomizer/generator/data/text/languages/english.yaml +++ b/src/dusk/randomizer/generator/data/text/languages/english.yaml @@ -1240,6 +1240,98 @@ Lanayru Twilight Tear: Cryptic: Text: a {tear of a lake spirit} +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Fishing Rod + Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) # HINT REGION NAMES Ordon: diff --git a/src/dusk/randomizer/generator/data/text/languages/french.yaml b/src/dusk/randomizer/generator/data/text/languages/french.yaml index 765a177e98..9d37c0f0a3 100644 --- a/src/dusk/randomizer/generator/data/text/languages/french.yaml +++ b/src/dusk/randomizer/generator/data/text/languages/french.yaml @@ -1240,6 +1240,98 @@ Lanayru Twilight Tear: Cryptic: Text: a {tear of a lake spirit} +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) # HINT REGION NAMES Ordon: diff --git a/src/dusk/randomizer/generator/data/text/languages/german.yaml b/src/dusk/randomizer/generator/data/text/languages/german.yaml index 42970a7af2..eb8b45cf21 100644 --- a/src/dusk/randomizer/generator/data/text/languages/german.yaml +++ b/src/dusk/randomizer/generator/data/text/languages/german.yaml @@ -1240,6 +1240,98 @@ Lanayru Twilight Tear: Cryptic: Text: a {tear of a lake spirit} +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) # HINT REGION NAMES Ordon: diff --git a/src/dusk/randomizer/generator/data/text/languages/italian.yaml b/src/dusk/randomizer/generator/data/text/languages/italian.yaml index dd4c995507..ba88386d7b 100644 --- a/src/dusk/randomizer/generator/data/text/languages/italian.yaml +++ b/src/dusk/randomizer/generator/data/text/languages/italian.yaml @@ -1240,6 +1240,98 @@ Lanayru Twilight Tear: Cryptic: Text: a {tear of a lake spirit} +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) # HINT REGION NAMES Ordon: diff --git a/src/dusk/randomizer/generator/data/text/languages/spanish.yaml b/src/dusk/randomizer/generator/data/text/languages/spanish.yaml index 0854999b04..2791e1a759 100644 --- a/src/dusk/randomizer/generator/data/text/languages/spanish.yaml +++ b/src/dusk/randomizer/generator/data/text/languages/spanish.yaml @@ -1240,6 +1240,98 @@ Lanayru Twilight Tear: Cryptic: Text: a {tear of a lake spirit} +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) # HINT REGION NAMES Ordon: diff --git a/src/dusk/randomizer/generator/logic/hints.cpp b/src/dusk/randomizer/generator/logic/hints.cpp index 0f638efa8c..753e617363 100644 --- a/src/dusk/randomizer/generator/logic/hints.cpp +++ b/src/dusk/randomizer/generator/logic/hints.cpp @@ -27,7 +27,7 @@ namespace randomizer::logic::hints { } } - if (requiredDungeonText.empty()) { + if (requiredDungeonText.Empty()) { requiredDungeonText += getTextObject("No Required Dungeons Text"); } } diff --git a/src/dusk/randomizer/generator/seedgen/config.hpp b/src/dusk/randomizer/generator/seedgen/config.hpp index 78f66a1517..61a892c2b5 100644 --- a/src/dusk/randomizer/generator/seedgen/config.hpp +++ b/src/dusk/randomizer/generator/seedgen/config.hpp @@ -52,6 +52,7 @@ namespace randomizer::seedgen::config void SetSeed(const std::string& newSeed) { this->_seed = newSeed; } std::string GetSeed() const { return this->_seed; } auto& GetSettingsList() { return this->_settingsList; } + auto& GetSettings() { return this->_settingsList.front();} bool IsUsingPlandomizer() const { return this->_isUsingPlandomizer; } bool IsGeneratingSpoilerLog() const { return this->_isGeneratingSpoilerLog; } diff --git a/src/dusk/randomizer/generator/seedgen/settings.hpp b/src/dusk/randomizer/generator/seedgen/settings.hpp index c1f6cc4ef2..1b538bb94e 100644 --- a/src/dusk/randomizer/generator/seedgen/settings.hpp +++ b/src/dusk/randomizer/generator/seedgen/settings.hpp @@ -200,6 +200,7 @@ namespace randomizer::seedgen::settings const std::map& GetStartingInventory() const { return this->_startingInventory; } const std::set& GetExcludedLocations() const { return this->_excludedLocations; } const std::list>& GetMixedEntrancePools() const { return this->_mixedEntrancePools; } + std::map& GetModifiableStartingInventory() { return this->_startingInventory; } private: std::map _map = {}; diff --git a/src/dusk/randomizer/generator/utility/text.cpp b/src/dusk/randomizer/generator/utility/text.cpp index 83c59f89b2..b8ca9d67cd 100644 --- a/src/dusk/randomizer/generator/utility/text.cpp +++ b/src/dusk/randomizer/generator/utility/text.cpp @@ -185,7 +185,7 @@ namespace randomizer { } } - bool Text::empty() const { + bool Text::Empty() const { for (auto& text : mText) { if (!text.empty()) { return false; @@ -383,6 +383,10 @@ namespace randomizer { return tb; } + bool textObjectExists(const std::string& name) { + return getTextDatabase().contains(name); + } + const Text& getTextObject(const std::string& name, Text::Type type /*= Text::STANDARD*/) { const auto& tb = getTextDatabase(); diff --git a/src/dusk/randomizer/generator/utility/text.hpp b/src/dusk/randomizer/generator/utility/text.hpp index 9f40794f5b..ad4b410c88 100644 --- a/src/dusk/randomizer/generator/utility/text.hpp +++ b/src/dusk/randomizer/generator/utility/text.hpp @@ -72,7 +72,7 @@ namespace randomizer { */ void Replace(const std::string& oldStr, const Text& replacementText, int count = 1); void Replace(const std::string& oldStr, const std::string& replacementText, int count = 1); - bool empty() const; + bool Empty() const; Text& operator+=(const Text& rhs); Text& operator+=(const std::string& rhs); friend Text operator+(Text lhs, Text& rhs); @@ -103,9 +103,11 @@ namespace randomizer { const TextDatabase& getTextDatabase(); + bool textObjectExists(const std::string& name); const Text& getTextObject(const std::string& name, Text::Type type = Text::STANDARD); const std::string& getTextStr(const std::string& name, Text::Type type = Text::STANDARD, Text::Language language = Text::ENGLISH); + Text addColor(const Text& text, Text::Color color, int count = 1, bool forceAround = false); // Replaces the message codes in the string with the ingame hex equivalents diff --git a/src/dusk/ui/rando_config.cpp b/src/dusk/ui/rando_config.cpp index 3cd7e6c7a1..c70301da4d 100644 --- a/src/dusk/ui/rando_config.cpp +++ b/src/dusk/ui/rando_config.cpp @@ -38,7 +38,7 @@ randomizer::seedgen::settings::Setting* FindSetting(const std::string& key) { } // TODO: handle multi-world selection - auto& settings = GetRandomizerConfig().GetSettingsList().front(); + auto& settings = GetRandomizerConfig().GetSettings(); try { return &settings.GetMap().at(key); } catch (std::exception e) { @@ -200,6 +200,211 @@ NumberButton* rando_add_optional_setting(std::string optionValue, std::string op }); } + +const std::vector>& GetStartingInventoryLayoutOrder() { + static const std::vector> layoutOrder = { + // { display name , logic item name } + {"Shadow Crystal", "Shadow Crystal"}, + {"Horse Call", "Horse Call"}, + {"Fishing Rod", "Progressive Fishing Rod"}, + {"Slingshot", "Slingshot"}, + {"Lantern", "Lantern"}, + {"Gale Boomerang", "Gale Boomerang"}, + {"Iron Boots", "Iron Boots"}, + {"Bow", "Progressive Bow"}, + {"Hawkeye", "Hawkeye"}, + {"Bomb Bags", "Bomb Bag"}, + {"Giant Bomb Bags", "Giant Bomb Bag"}, + {"Clawshot", "Progressive Clawshot"}, + {"Spinner", "Spinner"}, + {"Ball and Chain", "Ball and Chain"}, + {"Dominion Rod", "Progressive Dominion Rod"}, + {"Empty Bottle", "Empty Bottle"}, + {"Auru's Memo", "Aurus Memo"}, + {"Ashei's Sketch", "Asheis Sketch"}, + {"Sky Book", "Progressive Sky Book"}, + {"Sword", "Progressive Sword"}, + {"Ordon Shield", "Ordon Shield"}, + {"Hylian Shield", "Hylian Shield"}, + {"Zora Armor", "Zora Armor"}, + {"Magic Armor", "Magic Armor"}, + {"Wallet", "Progressive Wallet"}, + {"Hidden Skills", "Progressive Hidden Skill"}, + {"Poe Souls", "Poe Soul"}, + {"Fused Shadows", "Progressive Fused Shadow"}, + {"Mirror Shards", "Progressive Mirror Shard"}, + {"Gate Keys", "Gate Keys"}, + {"Gerudo Desert Bulblin Camp Key", "Gerudo Desert Bulblin Camp Key"}, + {"Forest Temple Small Keys", "Forest Temple Small Key"}, + {"Goron Mines Small Keys", "Goron Mines Small Key"}, + {"Lakebed Temple Small Keys", "Lakebed Temple Small Key"}, + {"Arbiter's Grounds Small Keys", "Arbiters Grounds Small Key"}, + {"Snowpeak Ruins Small Keys", "Snowpeak Ruins Small Key"}, + {"Ordon Pumpkin", "Ordon Pumpkin"}, + {"Ordon Cheese", "Ordon Cheese"}, + {"Temple of Time Small Keys", "Temple of Time Small Key"}, + {"City in the Sky Small Keys", "City in the Sky Small Key"}, + {"Palace of Twilight Small Keys", "Palace of Twilight Small Key"}, + {"Hyrule Castle Small Keys", "Hyrule Castle Small Key"}, + {"Forest Temple Big Key", "Forest Temple Big Key"}, + {"Goron Mines Key Shards", "Goron Mines Key Shard"}, + {"Lakebed Temple Big Key", "Lakebed Temple Big Key"}, + {"Arbiter's Grounds Big Key", "Arbiters Grounds Big Key"}, + {"Snowpeak Ruins Bedroom Key", "Snowpeak Ruins Bedroom Key"}, + {"Temple of Time Big Key", "Temple of Time Big Key"}, + {"City in the Sky Big Key", "City in the Sky Big Key"}, + {"Palace of Twilight Big Key", "Palace of Twilight Big Key"}, + {"Hyrule Castle Big Key", "Hyrule Castle Big Key"}, + {"Gerudo Desert Portal", "Gerudo Desert Portal"}, + {"Mirror Chamber Portal", "Mirror Chamber Portal"}, + {"Snowpeak Portal", "Snowpeak Portal"}, + {"Sacred Grove Portal", "Sacred Grove Portal"}, + {"Bridge of Eldin Portal", "Bridge of Eldin Portal"}, + {"Upper Zora's River Portal", "Upper Zoras River Portal"} + }; + return layoutOrder; +} + +void rando_starting_inventory_update_right_pane(Pane& rightPane) { + rightPane.clear(); + rightPane.add_section("Selected Starting Items"); + + const auto& inventory = GetRandomizerConfig().GetSettings().GetStartingInventory(); + const auto& layoutOrder = GetStartingInventoryLayoutOrder(); + + for (const auto& [itemText, itemName] : layoutOrder) { + if (!inventory.contains(itemName)) { + continue; + } + + int count = inventory.at(itemName); + if (count <= 0) { + continue; + } + + // If we have a prettier name for the item, prioritize that + std::string prettyItemName = fmt::format("{} x{}", itemName, count); + if (randomizer::textObjectExists(prettyItemName)) { + rightPane.add_text(fmt::format("• {}", randomizer::getTextStr(prettyItemName))); + } + // Display the count before the itemname for these items + else if (itemName.find("Small Key") != std::string::npos || + itemName.find("Shard") != std::string::npos || + itemName.find("Fused Shadow") != std::string::npos || + itemName.find("Hidden Skill") != std::string::npos || + itemName == "Poe Soul" || + itemName == "Bomb Bag") + { + rightPane.add_text(fmt::format("• {} {}", count, itemText)); + } else { + rightPane.add_text(fmt::format("• {}", itemText)); + } + } +} + +void rando_starting_item_toggle(Pane& leftPane, Pane& rightPane, std::string itemText, std::string itemName = "", int max = 1) { + if (itemName.empty()) { + itemName = itemText; + } + + // Helper function for increasing a starting item count by 1 + auto increaseCount = [itemName, max]() { + auto& inventory = GetRandomizerConfig().GetSettings().GetModifiableStartingInventory(); + int newCount = inventory[itemName] + 1; + if (newCount > max) { + inventory.erase(itemName); + } else { + inventory.at(itemName) = newCount; + } + SaveConfig(); + }; + + // Helper function for decreasing a starting item count by 1 + auto decreaseCount = [itemName, max]() { + auto& inventory = GetRandomizerConfig().GetSettings().GetModifiableStartingInventory(); + int newCount = inventory[itemName] - 1; + if (newCount < 0) { + newCount = max; + } + + if (newCount == 0) { + inventory.erase(itemName); + } else { + inventory.at(itemName) = newCount; + } + SaveConfig(); + }; + + leftPane.add_select_button({ + .key = itemText, + .getValue = + [itemName, itemText] { + auto& inventory = GetRandomizerConfig().GetSettings().GetModifiableStartingInventory(); + int curCount = inventory.contains(itemName) ? inventory[itemName] : 0; + std::string prettyItemName = fmt::format("{} x{}", itemName, curCount); + if (randomizer::textObjectExists(prettyItemName)) { + return Rml::String{randomizer::getTextStr(prettyItemName)}; + } + if (curCount > 0) { + return Rml::String{itemText}; + } + + return Rml::String{"None"}; + }, + }) + // Allow pressing the button to advance through the item choices + .on_pressed([increaseCount]() { + increaseCount(); + mDoAud_seStartMenu(kSoundItemChange); + }) + // Also allow pressing left/right to go back/advance through them + .on_nav_command([increaseCount, decreaseCount](Rml::Event&, NavCommand cmd) { + if (cmd == NavCommand::Right) { + increaseCount(); + mDoAud_seStartMenu(kSoundItemChange); + return true; + } + if (cmd == NavCommand::Left) { + decreaseCount(); + mDoAud_seStartMenu(kSoundItemChange); + return true; + } + return false; + }) + // Listen for the change event so that we can update the list on the right pane + .listen(Rml::EventId::Change, [&rightPane](Rml::Event&) { + rando_starting_inventory_update_right_pane(rightPane); + }); +} + +void rando_starting_item_number_toggle(Pane& leftPane, Pane& rightPane, std::string itemText, std::string itemName = "", int max = 1) { + if (itemName.empty()) { + itemName = itemText; + } + leftPane.add_child(NumberButton::Props{ + .key = itemText, + .getValue = + [itemName] { + auto& inventory = GetRandomizerConfig().GetSettings().GetModifiableStartingInventory(); + return inventory.contains(itemName) ? inventory[itemName] : 0; + }, + .setValue = [itemName](int value) { + auto& inventory = GetRandomizerConfig().GetSettings().GetModifiableStartingInventory(); + if (value == 0) { + inventory.erase(itemName); + } else { + inventory[itemName] = value; + } + SaveConfig(); + }, + .min = 0, + .max = max, + }) + .listen(Rml::EventId::Change, [&rightPane](Rml::Event&) { + rando_starting_inventory_update_right_pane(rightPane); + }); +} + Document* show_seed_gen_modal(std::string_view message) { auto* modal = &push_document(std::make_unique(Modal::Props{ .title = "Randomizer", @@ -454,6 +659,114 @@ RandomizerWindow::RandomizerWindow() { rando_config_toggle(leftPane, rightPane, "Logic Transform Anywhere"); }); + add_tab("Starting Inventory", [this](Rml::Element* content) { + + auto& leftPane = add_child(content, Pane::Type::Controlled); + auto& rightPane = add_child(content, Pane::Type::Controlled); + + // Hijack the Next Nav Command to let controller users interact with the right pane's scrollbar + leftPane.listen(Rml::EventId::Keydown, [&rightPane](Rml::Event& event) { + const auto cmd = map_nav_event(event); + if (cmd == NavCommand::Next) { + rightPane.root()->Focus(); + event.StopPropagation(); + } + }); + + // Map up and down to use the scrollbar on the right pane for controllers + rightPane.listen(Rml::EventId::Keydown, [&rightPane, &leftPane](Rml::Event& event) { + const auto cmd = map_nav_event(event); + if (cmd == NavCommand::Up) { + rightPane.root()->SetScrollTop(rightPane.root()->GetScrollTop() - 40.0f); + event.StopPropagation(); + } else if (cmd == NavCommand::Down) { + rightPane.root()->SetScrollTop(rightPane.root()->GetScrollTop() + 40.0f); + } else if (cmd == NavCommand::Previous) { + leftPane.root()->Focus(); + event.StopPropagation(); + } + }); + + leftPane.add_button({ + .text = "Clear Selected Starting Items", + .isSelected = []{ return false;}, + }) + .on_pressed([&rightPane]() { + auto& inventory = GetRandomizerConfig().GetSettings().GetModifiableStartingInventory(); + inventory.clear(); + SaveConfig(); + rando_starting_inventory_update_right_pane(rightPane); + }); + + leftPane.add_section("Main Items"); + rando_starting_item_toggle(leftPane, rightPane, "Shadow Crystal"); + rando_starting_item_toggle(leftPane, rightPane, "Horse Call"); + rando_starting_item_toggle(leftPane, rightPane, "Fishing Rod", "Progressive Fishing Rod", 2); + rando_starting_item_toggle(leftPane, rightPane, "Slingshot"); + rando_starting_item_toggle(leftPane, rightPane, "Lantern"); + rando_starting_item_toggle(leftPane, rightPane, "Gale Boomerang"); + rando_starting_item_toggle(leftPane, rightPane, "Iron Boots"); + rando_starting_item_toggle(leftPane, rightPane, "Bow", "Progressive Bow", 3); + rando_starting_item_toggle(leftPane, rightPane, "Hawkeye"); + rando_starting_item_number_toggle(leftPane, rightPane, "Bomb Bags", "Bomb Bag", 3); + rando_starting_item_toggle(leftPane, rightPane, "Giant Bomb Bags", "Giant Bomb Bag"); + rando_starting_item_toggle(leftPane, rightPane, "Clawshot", "Progressive Clawshot", 2); + rando_starting_item_toggle(leftPane, rightPane, "Spinner"); + rando_starting_item_toggle(leftPane, rightPane, "Ball and Chain"); + rando_starting_item_toggle(leftPane, rightPane, "Dominion Rod", "Progressive Dominion Rod", 2); + rando_starting_item_toggle(leftPane, rightPane, "Empty Bottle"); + rando_starting_item_toggle(leftPane, rightPane, "Auru's Memo", "Aurus Memo"); + rando_starting_item_toggle(leftPane, rightPane, "Ashei's Sketch", "Asheis Sketch"); + rando_starting_item_toggle(leftPane, rightPane, "Sky Book", "Progressive Sky Book", 7); + + leftPane.add_section("Gear Screen"); + rando_starting_item_toggle(leftPane, rightPane, "Sword", "Progressive Sword", 4); + rando_starting_item_toggle(leftPane, rightPane, "Ordon Shield"); + rando_starting_item_toggle(leftPane, rightPane, "Hylian Shield"); + rando_starting_item_toggle(leftPane, rightPane, "Zora Armor"); + rando_starting_item_toggle(leftPane, rightPane, "Magic Armor"); + rando_starting_item_toggle(leftPane, rightPane, "Wallet", "Progressive Wallet", 2); + rando_starting_item_number_toggle(leftPane, rightPane, "Hidden Skills", "Progressive Hidden Skill", 7); + rando_starting_item_number_toggle(leftPane, rightPane, "Poe Souls", "Poe Soul", 60); + rando_starting_item_number_toggle(leftPane, rightPane, "Fused Shadows", "Progressive Fused Shadow", 3); + rando_starting_item_number_toggle(leftPane, rightPane, "Mirror Shards", "Progressive Mirror Shard", 4); + + leftPane.add_section("Overworld Keys"); + rando_starting_item_toggle(leftPane, rightPane, "Gate Keys"); + rando_starting_item_toggle(leftPane, rightPane, "Gerudo Desert Bulblin Camp Key"); + + leftPane.add_section("Dungeon Items"); + rando_starting_item_number_toggle(leftPane, rightPane, "Forest Temple Small Keys", "Forest Temple Small Key", 4); + rando_starting_item_number_toggle(leftPane, rightPane, "Goron Mines Small Keys", "Goron Mines Small Key", 3); + rando_starting_item_number_toggle(leftPane, rightPane, "Lakebed Temple Small Keys", "Lakebed Temple Small Key", 3); + rando_starting_item_number_toggle(leftPane, rightPane, "Arbiter's Grounds Small Keys", "Arbiters Grounds Small Key", 5); + rando_starting_item_number_toggle(leftPane, rightPane, "Snowpeak Ruins Small Keys", "Snowpeak Ruins Small Key", 4); + rando_starting_item_toggle(leftPane, rightPane, "Ordon Pumpkin"); + rando_starting_item_toggle(leftPane, rightPane, "Ordon Cheese"); + rando_starting_item_number_toggle(leftPane, rightPane, "Temple of Time Small Keys", "Temple of Time Small Key", 4); + rando_starting_item_number_toggle(leftPane, rightPane, "City in the Sky Small Keys", "City in the Sky Small Key", 1); + rando_starting_item_number_toggle(leftPane, rightPane, "Palace of Twilight Small Keys", "Palace of Twilight Small Key", 7); + rando_starting_item_number_toggle(leftPane, rightPane, "Hyrule Castle Small Keys", "Hyrule Castle Small Key", 3); + + rando_starting_item_toggle(leftPane, rightPane, "Forest Temple Big Key"); + rando_starting_item_number_toggle(leftPane, rightPane, "Goron Mines Key Shards", "Goron Mines Key Shard", 3); + rando_starting_item_toggle(leftPane, rightPane, "Lakebed Temple Big Key"); + rando_starting_item_toggle(leftPane, rightPane, "Arbiter's Grounds Big Key", "Arbiters Grounds Big Key"); + rando_starting_item_toggle(leftPane, rightPane, "Snowpeak Ruins Bedroom Key"); + rando_starting_item_toggle(leftPane, rightPane, "Temple of Time Big Key"); + rando_starting_item_toggle(leftPane, rightPane, "City in the Sky Big Key"); + rando_starting_item_toggle(leftPane, rightPane, "Palace of Twilight Big Key"); + rando_starting_item_toggle(leftPane, rightPane, "Hyrule Castle Big Key"); + + leftPane.add_section("Warp Portals"); + rando_starting_item_toggle(leftPane, rightPane, "Gerudo Desert Portal"); + rando_starting_item_toggle(leftPane, rightPane, "Mirror Chamber Portal"); + rando_starting_item_toggle(leftPane, rightPane, "Snowpeak Portal"); + rando_starting_item_toggle(leftPane, rightPane, "Sacred Grove Portal"); + rando_starting_item_toggle(leftPane, rightPane, "Bridge of Eldin Portal"); + rando_starting_item_toggle(leftPane, rightPane, "Upper Zora's River Portal", "Upper Zoras River Portal"); + }); + if (randomizer_IsActive()) { add_tab("In-Game", [this](Rml::Element* content) { auto& leftPane = add_child(content, Pane::Type::Controlled); diff --git a/src/dusk/ui/select_button.cpp b/src/dusk/ui/select_button.cpp index 313594836e..83224ca5ea 100644 --- a/src/dusk/ui/select_button.cpp +++ b/src/dusk/ui/select_button.cpp @@ -49,6 +49,7 @@ void SelectButton::set_value_label(const Rml::String& value) { mValueElem->SetInnerRML(escape(value)); } mProps.value = value; + mRoot->DispatchEvent(Rml::EventId::Change, {}); } }