From 43fd6756030e24f49127aa8835d073038007378a Mon Sep 17 00:00:00 2001 From: Caladius Date: Sat, 18 Oct 2025 19:57:30 -0400 Subject: [Rando] Starting Items Rework (Cleaner Edition) (#1295) * WIP * Reworked Starting Items * Clean up * Clean up 2 * default macro for cvar * Spoiler and Save File handling * Fix spoiler drop --------- Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com> --- mm/2s2h/BenJsonConversions.hpp | 6 + mm/2s2h/Rando/Menu.cpp | 242 ++++++++++++++++------------ mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp | 23 +-- mm/2s2h/Rando/Rando.h | 4 + mm/2s2h/Rando/Spoiler/Apply.cpp | 23 +-- mm/2s2h/Rando/Spoiler/Generate.cpp | 2 + mm/2s2h/Rando/StaticData/Items.cpp | 119 +++++--------- mm/2s2h/Rando/StaticData/Options.cpp | 3 - mm/2s2h/Rando/StaticData/StaticData.h | 2 +- mm/2s2h/Rando/Types.h | 11 +- mm/2s2h/ShipUtils.cpp | 33 ++++ mm/2s2h/ShipUtils.h | 4 + mm/include/z64save.h | 1 + 13 files changed, 255 insertions(+), 218 deletions(-) diff --git a/mm/2s2h/BenJsonConversions.hpp b/mm/2s2h/BenJsonConversions.hpp index 8dc1fa7bf..007c3ae8d 100644 --- a/mm/2s2h/BenJsonConversions.hpp +++ b/mm/2s2h/BenJsonConversions.hpp @@ -55,6 +55,7 @@ void to_json(json& j, const RandoSaveInfo& rando) { { "randoSaveChecks", rando.randoSaveChecks }, { "finalSeed", rando.finalSeed }, { "randoSaveOptions", rando.randoSaveOptions }, + { "randoStartingItems", rando.randoStartingItems }, { "foundDungeonKeys", rando.foundDungeonKeys }, { "foundTriforcePieces", rando.foundTriforcePieces }, }; @@ -66,6 +67,11 @@ void from_json(const json& j, RandoSaveInfo& rando) { j.at("randoSaveChecks").get_to(rando.randoSaveChecks); j.at("finalSeed").get_to(rando.finalSeed); j.at("randoSaveOptions").get_to(rando.randoSaveOptions); + + // The value of Starting Items is a string in code but is stored as a char in the RandoSaveInfo + std::string startingItemsStr = j.value("randoStartingItems", ""); + strncpy(rando.randoStartingItems, startingItemsStr.c_str(), startingItemsStr.size() +1); + j.at("foundDungeonKeys").get_to(rando.foundDungeonKeys); j.at("foundTriforcePieces").get_to(rando.foundTriforcePieces); } diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index bc529d404..02554e8c5 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -293,8 +293,7 @@ static void DrawShufflesTab() { static void DrawItemsTab() { f32 columnWidth = ImGui::GetContentRegionAvail().x / 3 - (ImGui::GetStyle().ItemSpacing.x * 2); - f32 halfHeight = ImGui::GetContentRegionAvail().y / 3 - (ImGui::GetStyle().ItemSpacing.y * 2); - ImGui::BeginChild("randoItemsColumn1", ImVec2(columnWidth, halfHeight)); + ImGui::BeginChild("randoItemsColumn1", ImVec2(columnWidth, ImGui::GetContentRegionAvail().y)); CVarCheckbox("Shuffle Swim", Rando::StaticData::Options[RO_SHUFFLE_SWIM].cvar, CheckboxOptions({ { .tooltip = "Shuffles the ability to Swim, entering the Swim state or submerging\n" "into deep water will respawn Link." } })); @@ -310,7 +309,7 @@ static void DrawItemsTab() { CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); ImGui::EndChild(); ImGui::SameLine(); - ImGui::BeginChild("randoItemsColumn2", ImVec2(columnWidth, halfHeight)); + ImGui::BeginChild("randoItemsColumn2", ImVec2(columnWidth, ImGui::GetContentRegionAvail().y)); CVarCheckbox( "Plentiful Items", Rando::StaticData::Options[RO_PLENTIFUL_ITEMS].cvar, CheckboxOptions({ { .tooltip = "Major items, masks, and keys will have an extra copy added to the item pool. \n" @@ -328,7 +327,7 @@ static void DrawItemsTab() { CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); ImGui::EndChild(); ImGui::SameLine(); - ImGui::BeginChild("randoItemsColumn3", ImVec2(columnWidth, halfHeight)); + ImGui::BeginChild("randoItemsColumn3", ImVec2(columnWidth, ImGui::GetContentRegionAvail().y)); CVarCheckbox("Song of Double Time", "gPlaceholderBool", CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); CVarCheckbox("Inverted Song of Time", "gPlaceholderBool", @@ -338,122 +337,160 @@ static void DrawItemsTab() { CVarCheckbox("Sun's Song", "gPlaceholderBool", CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); ImGui::EndChild(); - ImGui::BeginChild("randoItemsStarting", ImVec2(0, 0)); - ImGui::BeginChild("randoStartingItemsColumn1", ImVec2(columnWidth, 0)); +} + +static void DrawStartingItemsTab() { + f32 columnWidth = ImGui::GetContentRegionAvail().x / 2 - (ImGui::GetStyle().ItemSpacing.x * 2); + f32 quarterHeight = ImGui::GetContentRegionAvail().y / 4 - (ImGui::GetStyle().ItemSpacing.y * 4); + int tableColumns = 0; + ImGui::BeginChild("randoStartingOptions", ImVec2(0, quarterHeight)); ImGui::SeparatorText("Starting Options"); - CVarCheckbox("Wallet Full", Rando::StaticData::Options[RO_STARTING_RUPEES].cvar, - CheckboxOptions({ { - .tooltip = "Start with a full wallet", - } })); - CVarCheckbox("Consumables Full", Rando::StaticData::Options[RO_STARTING_CONSUMABLES].cvar, - CheckboxOptions({ { - .tooltip = "Start with full Deku Sticks and Deku Nuts", - } })); - - CVarCheckbox("Maps and Compasses", Rando::StaticData::Options[RO_STARTING_MAPS_AND_COMPASSES].cvar, - CheckboxOptions({ { - .tooltip = "Enables maps and compasses everywhere", - } })); - CVarSliderInt("Health", Rando::StaticData::Options[RO_STARTING_HEALTH].cvar, - IntSliderOptions() - .Min(1) - .Max(20) - .DefaultValue(3) - .LabelPosition(LabelPosition::None) - .Format("%d Hearts") - .Color(Colors::Red)); + if (ImGui::BeginTable("Starting Options", 3)) { + ImGui::TableNextColumn(); + CVarCheckbox("Wallet Full", Rando::StaticData::Options[RO_STARTING_RUPEES].cvar, + CheckboxOptions({ { + .tooltip = "Start with a full wallet", + } })); + + ImGui::TableNextColumn(); + CVarCheckbox("Consumables Full", Rando::StaticData::Options[RO_STARTING_CONSUMABLES].cvar, + CheckboxOptions({ { + .tooltip = "Start with full Deku Sticks and Deku Nuts", + } })); + + ImGui::TableNextColumn(); + CVarCheckbox("Maps and Compasses", Rando::StaticData::Options[RO_STARTING_MAPS_AND_COMPASSES].cvar, + CheckboxOptions({ { + .tooltip = "Enables maps and compasses everywhere", + } })); + + ImGui::TableNextColumn(); + CVarSliderInt("Health", Rando::StaticData::Options[RO_STARTING_HEALTH].cvar, + IntSliderOptions() + .Min(1) + .Max(20) + .DefaultValue(3) + .LabelPosition(LabelPosition::None) + .Format("%d Hearts") + .Color(Colors::Red)); + + ImGui::EndTable(); + } ImGui::EndChild(); - ImGui::SameLine(); - ImGui::BeginChild("randoStartingItemsColumn2", ImVec2(columnWidth, 0)); + ImGui::BeginChild("randoStartingItems1", ImVec2(0, quarterHeight)); ImGui::SeparatorText("Starting Items"); - int checkedItemIndex = 0; - for (size_t i = 0; i < Rando::StaticData::StartingItemsMap.size(); i++) { - RandoItemId itemId = Rando::StaticData::StartingItemsMap[i]; - std::string itemName = Rando::StaticData::Items[itemId].name; - RandoOptionId optionId; - uint32_t defaults = 0; - if (i < 32) { - optionId = RO_STARTING_ITEMS_1; - } else if (i < 64) { - optionId = RO_STARTING_ITEMS_2; - defaults = -2145385984; - } else { - optionId = RO_STARTING_ITEMS_3; - defaults = 2048; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(15, 15)); + 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)); + + std::vector setStartingItemsList = + convertStartingItemsToRandoItemId(CVarGetString("gRando.StartingItems", RANDO_STARTING_ITEMS_DEFAULT), ","); + uint32_t listIndex = 0; + for (auto& startingItem : setStartingItemsList) { + ImGui::PushID(listIndex); + ImVec2 imageSize = ImVec2(42.0f, 42.0f); + if ((startingItem >= RI_SONG_ELEGY && startingItem <= RI_SONG_TIME) || startingItem == RI_PROGRESSIVE_LULLABY) { + imageSize.x /= 1.5f; } - uint32_t startingItems = CVarGetInteger(Rando::StaticData::Options[optionId].cvar, defaults); - bool checked = (startingItems & (1 << i)) != 0; - if (checked) { - const char* texturePath = Rando::StaticData::GetIconTexturePath(itemId); - ImTextureID textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(texturePath); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - 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)); - if (ImGui::ImageButton(std::to_string(i).c_str(), textureId, ImVec2(columnWidth / 8, columnWidth / 8), - ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1))) { - startingItems &= ~(1 << i); - CVarSetInteger(Rando::StaticData::Options[optionId].cvar, startingItems); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + Rando::StaticData::RandoStaticItem randoStaticItem = Rando::StaticData::Items[startingItem]; + const char* texturePath = Rando::StaticData::GetIconTexturePath(startingItem); + ImTextureID textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(texturePath); + + if (ImGui::ImageButton( + std::to_string(listIndex).c_str(), textureId, imageSize, ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), + Ship_GetItemColorTint(startingItem == RI_PROGRESSIVE_LULLABY ? ITEM_SONG_LULLABY + : randoStaticItem.itemId))) { + for (size_t i = 0; i < setStartingItemsList.size(); i++) { + if (setStartingItemsList[i] == startingItem) { + setStartingItemsList.erase(setStartingItemsList.begin() + i); + break; + } } - ImGui::PopStyleColor(3); - ImGui::PopStyleVar(2); - UIWidgets::Tooltip(itemName.c_str()); - checkedItemIndex++; + CVarSetString("gRando.StartingItems", CreateStartingItemsToCvar(setStartingItemsList).c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } - if (checkedItemIndex % 8 != 0) { - ImGui::SameLine(0, 0); + UIWidgets::Tooltip(randoStaticItem.name); + listIndex++; + + if ((listIndex + 1) % 15 != 0) { + ImGui::SameLine(); } + ImGui::PopID(); } + + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(2); + ImGui::EndChild(); - ImGui::SameLine(); - ImGui::BeginChild("randoStartingItemsColumn3", ImVec2(columnWidth, 0)); + ImGui::BeginChild("randoStartingItems2", ImVec2(0, 0)); ImGui::SeparatorText("Available Items"); - int uncheckedItemIndex = 0; - for (size_t i = 0; i < Rando::StaticData::StartingItemsMap.size(); i++) { - RandoItemId itemId = Rando::StaticData::StartingItemsMap[i]; - std::string itemName = Rando::StaticData::Items[itemId].name; - RandoOptionId optionId; - uint32_t defaults = 0; - if (i < 32) { - optionId = RO_STARTING_ITEMS_1; - } else if (i < 64) { - optionId = RO_STARTING_ITEMS_2; - defaults = -2145385984; - } else { - optionId = RO_STARTING_ITEMS_3; - defaults = 2048; - } - uint32_t startingItems = CVarGetInteger(Rando::StaticData::Options[optionId].cvar, defaults); - bool checked = (startingItems & (1 << i)) != 0; - if (!checked) { - const char* texturePath = Rando::StaticData::GetIconTexturePath(itemId); - ImTextureID textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(texturePath); + for (auto& category : Rando::StaticData::StartingItemsMap) { + tableColumns = 5; + if (category.first == STARTING_ITEMS_MASK) { + tableColumns++; + } + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); + if (ImGui::BeginChild(std::to_string(category.first).c_str(), ImVec2(0, 0), + ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | + ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders)) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); 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)); - if (ImGui::ImageButton(std::to_string(i).c_str(), textureId, ImVec2(columnWidth / 8, columnWidth / 8), - ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1))) { - startingItems |= (1 << i); - CVarSetInteger(Rando::StaticData::Options[optionId].cvar, startingItems); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + + if (ImGui::BeginTable(std::to_string(category.first).c_str(), tableColumns)) { + for (int i = 0; i < tableColumns; i++) { + ImGui::TableSetupColumn("item", ImGuiTableColumnFlags_WidthFixed, 50.0f); + } + for (auto& item : category.second) { + ImVec2 imageSize = ImVec2(42.0f, 42.0f); + if ((item >= RI_SONG_ELEGY && item <= RI_SONG_TIME) || item == RI_PROGRESSIVE_LULLABY) { + imageSize.x /= 1.5f; + } + + Rando::StaticData::RandoStaticItem randoStaticItem = Rando::StaticData::Items[item]; + const char* texturePath = Rando::StaticData::GetIconTexturePath(item); + ImTextureID textureId = + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(texturePath); + + if (item == RI_SONG_TIME) { + ImGui::TableNextRow(); + } + ImGui::TableNextColumn(); + if (ImGui::ImageButton(std::to_string(item).c_str(), textureId, imageSize, ImVec2(0, 0), + ImVec2(1, 1), ImVec4(0, 0, 0, 0), + Ship_GetItemColorTint(item == RI_PROGRESSIVE_LULLABY + ? ITEM_SONG_LULLABY + : randoStaticItem.itemId))) { + std::string currentStartingItems = + CVarGetString("gRando.StartingItems", RANDO_STARTING_ITEMS_DEFAULT); + if (currentStartingItems.length() != 0) { + currentStartingItems += ","; + } + currentStartingItems += std::to_string(item).c_str(); + CVarSetString("gRando.StartingItems", currentStartingItems.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + UIWidgets::Tooltip(randoStaticItem.name); + } + ImGui::EndTable(); } + ImGui::PopStyleColor(3); ImGui::PopStyleVar(2); - UIWidgets::Tooltip(itemName.c_str()); - uncheckedItemIndex++; + ImGui::EndChild(); } - if (uncheckedItemIndex % 8 != 0) { - ImGui::SameLine(0, 0); + ImGui::PopStyleColor(1); + if (category.first != STARTING_ITEMS_QUEST) { + ImGui::SameLine(); } } ImGui::EndChild(); - ImGui::EndChild(); } static void DrawLocationsTab() { @@ -622,13 +659,20 @@ void Rando::RegisterMenu() { }); mBenMenu->AddSidebarEntry("Rando", "Shuffle Options", 1); path.sidebarName = "Shuffle Options"; - mBenMenu->AddWidget(path, "Locations", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawShufflesTab(); }); + mBenMenu->AddWidget(path, "Shuffle Options", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + DrawShufflesTab(); + }); mBenMenu->AddSidebarEntry("Rando", "Locations", 1); + path.sidebarName = "Locations"; + mBenMenu->AddWidget(path, "Locations", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawLocationsTab(); }); mBenMenu->AddSidebarEntry("Rando", "Items", 1); path.sidebarName = "Items"; mBenMenu->AddWidget(path, "Items", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawItemsTab(); }); - path.sidebarName = "Locations"; - mBenMenu->AddWidget(path, "Locations", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawLocationsTab(); }); + mBenMenu->AddSidebarEntry("Rando", "Starting Items", 1); + path.sidebarName = "Starting Items"; + mBenMenu->AddWidget(path, "Starting Items", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + DrawStartingItemsTab(); + }); mBenMenu->AddSidebarEntry("Rando", "Hints", 1); path.sidebarName = "Hints"; mBenMenu->AddWidget(path, "Hints", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { DrawHintsTab(); }); diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp index f9d691a73..f264bf407 100644 --- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp +++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp @@ -59,6 +59,12 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { (uint32_t)CVarGetInteger(randoStaticOption.cvar, randoStaticOption.defaultValue); } + std::vector startingItems = convertStartingItemsToRandoItemId( + CVarGetString("gRando.StartingItems", RANDO_STARTING_ITEMS_DEFAULT), ","); + + std::string startingItemSave = CreateStartingItemsToCvar(startingItems); + strncpy(RANDO_STARTING_ITEMS, startingItemSave.c_str(), startingItemSave.size() + 1); + if (RANDO_SAVE_OPTIONS[RO_STARTING_HEALTH] != 3) { gSaveContext.save.saveInfo.playerData.healthCapacity = gSaveContext.save.saveInfo.playerData.health = RANDO_SAVE_OPTIONS[RO_STARTING_HEALTH] * 0x10; @@ -76,23 +82,6 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { AMMO(ITEM_DEKU_NUT) = CUR_CAPACITY(UPG_DEKU_NUTS); } - std::vector startingItems = {}; - for (size_t i = 0; i < Rando::StaticData::StartingItemsMap.size(); i++) { - RandoItemId itemId = Rando::StaticData::StartingItemsMap[i]; - RandoOptionId optionId; - if (i < 32) { - optionId = RO_STARTING_ITEMS_1; - } else if (i < 64) { - optionId = RO_STARTING_ITEMS_2; - } else { - optionId = RO_STARTING_ITEMS_3; - } - uint32_t startingItemsBits = RANDO_SAVE_OPTIONS[optionId]; - if ((startingItemsBits & (1 << (i % 32))) != 0) { - startingItems.push_back(itemId); - } - } - if (RANDO_SAVE_OPTIONS[RO_STARTING_MAPS_AND_COMPASSES]) { std::vector MapsAndCompasses = { RI_GREAT_BAY_COMPASS, RI_GREAT_BAY_MAP, RI_SNOWHEAD_COMPASS, diff --git a/mm/2s2h/Rando/Rando.h b/mm/2s2h/Rando/Rando.h index eba60dac2..4f592b0c5 100644 --- a/mm/2s2h/Rando/Rando.h +++ b/mm/2s2h/Rando/Rando.h @@ -9,6 +9,10 @@ #define RANDO_SAVE_CHECKS gSaveContext.save.shipSaveInfo.rando.randoSaveChecks #define RANDO_SAVE_OPTIONS gSaveContext.save.shipSaveInfo.rando.randoSaveOptions #define RANDO_EVENTS gSaveContext.save.shipSaveInfo.rando.randoEvents +#define RANDO_STARTING_ITEMS gSaveContext.save.shipSaveInfo.rando.randoStartingItems + +#define RANDO_STARTING_ITEMS_DEFAULT \ + "109,126,91,146" // This includes a Progressive Sword, Hero's Shield, Ocarina of Time, and Song of Time namespace Rando { diff --git a/mm/2s2h/Rando/Spoiler/Apply.cpp b/mm/2s2h/Rando/Spoiler/Apply.cpp index 902b29fcc..2f75bdcd5 100644 --- a/mm/2s2h/Rando/Spoiler/Apply.cpp +++ b/mm/2s2h/Rando/Spoiler/Apply.cpp @@ -1,5 +1,7 @@ #include "Spoiler.h" #include "Rando/Rando.h" +#include "public/bridge/consolevariablebridge.h" +#include "ShipUtils.h" namespace Rando { @@ -12,6 +14,9 @@ void ApplyToSaveContext(nlohmann::json spoiler) { RANDO_SAVE_OPTIONS[randoOptionId] = spoiler["options"][randoStaticOption.name].get(); } + std::string startingItemsSave = spoiler["startingItems"].get(); + strncpy(RANDO_STARTING_ITEMS, startingItemsSave.c_str(), startingItemsSave.size() + 1); + if (RANDO_SAVE_OPTIONS[RO_STARTING_HEALTH] != 3) { gSaveContext.save.saveInfo.playerData.healthCapacity = gSaveContext.save.saveInfo.playerData.health = RANDO_SAVE_OPTIONS[RO_STARTING_HEALTH] * 0x10; @@ -24,23 +29,7 @@ void ApplyToSaveContext(nlohmann::json spoiler) { AMMO(ITEM_DEKU_NUT) = CUR_CAPACITY(UPG_DEKU_NUTS); } - std::vector startingItems = {}; - - for (size_t i = 0; i < Rando::StaticData::StartingItemsMap.size(); i++) { - RandoItemId itemId = Rando::StaticData::StartingItemsMap[i]; - RandoOptionId optionId; - if (i < 32) { - optionId = RO_STARTING_ITEMS_1; - } else if (i < 64) { - optionId = RO_STARTING_ITEMS_2; - } else { - optionId = RO_STARTING_ITEMS_3; - } - uint32_t startingItemsBits = RANDO_SAVE_OPTIONS[optionId]; - if ((startingItemsBits & (1 << (i % 32))) != 0) { - startingItems.push_back(itemId); - } - } + std::vector startingItems = convertStartingItemsToRandoItemId(RANDO_STARTING_ITEMS, ","); if (RANDO_SAVE_OPTIONS[RO_STARTING_MAPS_AND_COMPASSES]) { std::vector MapsAndCompasses = { diff --git a/mm/2s2h/Rando/Spoiler/Generate.cpp b/mm/2s2h/Rando/Spoiler/Generate.cpp index a10c2a9da..23fc19713 100644 --- a/mm/2s2h/Rando/Spoiler/Generate.cpp +++ b/mm/2s2h/Rando/Spoiler/Generate.cpp @@ -16,6 +16,8 @@ nlohmann::json GenerateFromSaveContext() { spoiler["options"][randoStaticOption.name] = RANDO_SAVE_OPTIONS[randoOptionId]; } + spoiler["startingItems"] = RANDO_STARTING_ITEMS; + spoiler["checks"] = nlohmann::json::object(); for (auto& [randoCheckId, randoStaticCheck] : Rando::StaticData::Checks) { if (randoStaticCheck.randoCheckId == RC_UNKNOWN) { diff --git a/mm/2s2h/Rando/StaticData/Items.cpp b/mm/2s2h/Rando/StaticData/Items.cpp index b8a8064c0..3368c9fa9 100644 --- a/mm/2s2h/Rando/StaticData/Items.cpp +++ b/mm/2s2h/Rando/StaticData/Items.cpp @@ -6,6 +6,7 @@ extern "C" { extern s16 D_801CFF94[250]; #include "assets/interface/parameter_static/parameter_static.h" +#include "assets/interface/icon_item_dungeon_static/icon_item_dungeon_static.h" } namespace Rando { @@ -196,86 +197,37 @@ std::map Items = { RI(RI_WOODFALL_SMALL_KEY, "a", "Woodfall Small Key", RITYPE_SMALL_KEY, ITEM_KEY_SMALL, GI_KEY_SMALL, GID_KEY_SMALL), RI(RI_WOODFALL_STRAY_FAIRY, "a", "Woodfall Stray Fairy", RITYPE_STRAY_FAIRY, ITEM_STRAY_FAIRIES, GI_STRAY_FAIRY, GID_NONE), }; -// clang-format on -std::vector StartingItemsMap = { - RI_ARROW_FIRE, - RI_ARROW_ICE, - RI_ARROW_LIGHT, - RI_BOMBERS_NOTEBOOK, - RI_BOTTLE_EMPTY, - RI_DEED_LAND, - RI_DEED_MOUNTAIN, - RI_DEED_OCEAN, - RI_DEED_SWAMP, - RI_DOUBLE_DEFENSE, - RI_GREAT_FAIRY_SWORD, - RI_GREAT_SPIN_ATTACK, - RI_HOOKSHOT, - RI_LENS, - RI_LETTER_TO_KAFEI, - RI_LETTER_TO_MAMA, - RI_MASK_ALL_NIGHT, - RI_MASK_BLAST, - RI_MASK_BREMEN, - RI_MASK_BUNNY, - RI_MASK_CAPTAIN, - RI_MASK_CIRCUS_LEADER, - RI_MASK_COUPLE, - RI_MASK_DEKU, - RI_MASK_DON_GERO, - RI_MASK_FIERCE_DEITY, - RI_MASK_GARO, - RI_MASK_GIANT, - RI_MASK_GIBDO, - RI_MASK_GORON, - RI_MASK_GREAT_FAIRY, - RI_MASK_KAFEIS_MASK, - RI_MASK_KAMARO, - RI_MASK_KEATON, - RI_MASK_POSTMAN, - RI_MASK_ROMANI, - RI_MASK_SCENTS, - RI_MASK_STONE, - RI_MASK_TRUTH, - RI_MASK_ZORA, - RI_MOONS_TEAR, - RI_OCARINA, - RI_PENDANT_OF_MEMORIES, - RI_PICTOGRAPH_BOX, - RI_POWDER_KEG, - RI_PROGRESSIVE_BOMB_BAG, - RI_PROGRESSIVE_BOMB_BAG, - RI_PROGRESSIVE_BOMB_BAG, - RI_PROGRESSIVE_BOW, - RI_PROGRESSIVE_BOW, - RI_PROGRESSIVE_BOW, - RI_PROGRESSIVE_MAGIC, - RI_PROGRESSIVE_MAGIC, - RI_PROGRESSIVE_SWORD, - RI_PROGRESSIVE_SWORD, - RI_PROGRESSIVE_SWORD, - RI_PROGRESSIVE_WALLET, - RI_PROGRESSIVE_WALLET, - RI_REMAINS_GOHT, - RI_REMAINS_GYORG, - RI_REMAINS_ODOLWA, - RI_REMAINS_TWINMOLD, - RI_ROOM_KEY, - RI_SHIELD_HERO, - RI_SHIELD_MIRROR, - RI_SONG_ELEGY, - RI_SONG_EPONA, - RI_SONG_HEALING, - RI_PROGRESSIVE_LULLABY, - RI_PROGRESSIVE_LULLABY, - RI_SONG_NOVA, - RI_SONG_OATH, - RI_SONG_SOARING, - RI_SONG_SONATA, - RI_SONG_STORMS, - RI_SONG_TIME, +std::unordered_map> StartingItemsMap = { + { STARTING_ITEMS_INVENTORY, + { RI_OCARINA, RI_PROGRESSIVE_BOW, RI_ARROW_FIRE, RI_ARROW_ICE, RI_ARROW_LIGHT, + RI_PROGRESSIVE_BOMB_BAG, RI_BOMBCHU, RI_DEKU_STICK, RI_DEKU_NUT, RI_MAGIC_BEAN, + RI_POWDER_KEG, RI_PICTOGRAPH_BOX, RI_LENS, RI_HOOKSHOT, RI_GREAT_FAIRY_SWORD, + RI_BOTTLE_EMPTY + } }, + { STARTING_ITEMS_MASK, + { RI_MASK_POSTMAN, RI_MASK_ALL_NIGHT, RI_MASK_BLAST, RI_MASK_STONE, RI_MASK_GREAT_FAIRY, RI_MASK_DEKU, + RI_MASK_KEATON, RI_MASK_BREMEN, RI_MASK_BUNNY, RI_MASK_DON_GERO, RI_MASK_SCENTS, RI_MASK_GORON, + RI_MASK_ROMANI, RI_MASK_CIRCUS_LEADER, RI_MASK_KAFEIS_MASK, RI_MASK_COUPLE, RI_MASK_TRUTH, RI_MASK_ZORA, + RI_MASK_KAMARO, RI_MASK_GIBDO, RI_MASK_GARO, RI_MASK_CAPTAIN, RI_MASK_GIANT, RI_MASK_FIERCE_DEITY + } }, + { STARTING_ITEMS_QUEST, + { RI_BOMBERS_NOTEBOOK, RI_REMAINS_ODOLWA, RI_REMAINS_GOHT, RI_REMAINS_GYORG, RI_REMAINS_TWINMOLD, + RI_PROGRESSIVE_SWORD, RI_SHIELD_HERO, RI_SHIELD_MIRROR, RI_PROGRESSIVE_MAGIC, RI_DOUBLE_DEFENSE, + RI_PROGRESSIVE_WALLET, + RI_SONG_TIME, RI_SONG_HEALING, RI_SONG_EPONA, RI_SONG_SOARING, RI_SONG_STORMS, + RI_SONG_SONATA, RI_PROGRESSIVE_LULLABY, RI_SONG_NOVA, RI_SONG_ELEGY, RI_SONG_OATH + } }, + { STARTING_ITEMS_TRADE, + { RI_MOONS_TEAR, RI_DEED_LAND, RI_DEED_SWAMP, RI_DEED_MOUNTAIN, RI_DEED_OCEAN, RI_ROOM_KEY, RI_LETTER_TO_MAMA, + RI_LETTER_TO_KAFEI, RI_PENDANT_OF_MEMORIES + } }, + { STARTING_ITEMS_MISC, + { RI_SOUL_GOHT, RI_SOUL_GYORG, RI_SOUL_MAJORA, RI_SOUL_ODOLWA, RI_SOUL_TWINMOLD, + RI_FROG_BLUE, RI_FROG_CYAN, RI_FROG_PINK, RI_FROG_WHITE + } }, }; +// clang-format on RandoItemId GetItemIdFromName(const char* name) { for (auto& [randoItemId, randoStaticItem] : Items) { @@ -369,6 +321,17 @@ const char* GetIconTexturePath(RandoItemId randoItemId) { return (const char*)gItemIcons[ITEM_SONG_LULLABY]; case RI_PROGRESSIVE_MAGIC: return (const char*)gItemIcons[ITEM_MAGIC_JAR_SMALL]; + case RI_SOUL_GOHT: + case RI_SOUL_GYORG: + case RI_SOUL_MAJORA: + case RI_SOUL_ODOLWA: + case RI_SOUL_TWINMOLD: + return (const char*)gDungeonMapSkullTex; + case RI_FROG_BLUE: + case RI_FROG_CYAN: + case RI_FROG_PINK: + case RI_FROG_WHITE: + return (const char*)gItemIcons[ITEM_MASK_DON_GERO]; default: break; } diff --git a/mm/2s2h/Rando/StaticData/Options.cpp b/mm/2s2h/Rando/StaticData/Options.cpp index b8e695653..1f42b8930 100644 --- a/mm/2s2h/Rando/StaticData/Options.cpp +++ b/mm/2s2h/Rando/StaticData/Options.cpp @@ -44,9 +44,6 @@ std::map Options = { RO(RO_SHUFFLE_TRIFORCE_PIECES, RO_GENERIC_OFF), RO(RO_STARTING_CONSUMABLES, RO_GENERIC_OFF), RO(RO_STARTING_HEALTH, 3), - RO(RO_STARTING_ITEMS_1, 0), - RO(RO_STARTING_ITEMS_2, 2149581312), - RO(RO_STARTING_ITEMS_3, 2048), RO(RO_STARTING_MAPS_AND_COMPASSES, RO_GENERIC_OFF), RO(RO_STARTING_RUPEES, RO_GENERIC_OFF), RO(RO_TRIFORCE_PIECES_MAX, DEFAULT_TRIFORCE_PIECES_MAX), diff --git a/mm/2s2h/Rando/StaticData/StaticData.h b/mm/2s2h/Rando/StaticData/StaticData.h index 3c51a0b5b..050d5e1ec 100644 --- a/mm/2s2h/Rando/StaticData/StaticData.h +++ b/mm/2s2h/Rando/StaticData/StaticData.h @@ -46,7 +46,7 @@ struct RandoStaticItem { }; extern std::map Items; -extern std::vector StartingItemsMap; +extern std::unordered_map> StartingItemsMap; RandoItemId GetItemIdFromName(const char* name); u8 GetIconForZMessage(RandoItemId itemId); diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index 8e1f9c064..e236fbe02 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -2767,9 +2767,6 @@ typedef enum { RO_SHUFFLE_TRIFORCE_PIECES, RO_STARTING_CONSUMABLES, RO_STARTING_HEALTH, - RO_STARTING_ITEMS_1, - RO_STARTING_ITEMS_2, - RO_STARTING_ITEMS_3, RO_STARTING_MAPS_AND_COMPASSES, RO_STARTING_RUPEES, RO_TRIFORCE_PIECES_MAX, @@ -2892,4 +2889,12 @@ typedef enum { RE_MAX, } RandoEvent; +typedef enum { + STARTING_ITEMS_INVENTORY, + STARTING_ITEMS_MASK, + STARTING_ITEMS_QUEST, + STARTING_ITEMS_TRADE, + STARTING_ITEMS_MISC, +} StartingItemCategory; + #endif // RANDO_TYPES_H diff --git a/mm/2s2h/ShipUtils.cpp b/mm/2s2h/ShipUtils.cpp index ca4971391..758121cc1 100644 --- a/mm/2s2h/ShipUtils.cpp +++ b/mm/2s2h/ShipUtils.cpp @@ -242,6 +242,39 @@ void LoadGuiTextures() { } } +std::string CreateStartingItemsToCvar(std::vector startingItemList) { + std::string startingItemsStr = ""; + for (auto& item : startingItemList) { + if (startingItemsStr != "") { + startingItemsStr += ","; + } + startingItemsStr += std::to_string(item).c_str(); + } + + return startingItemsStr; +} + +std::vector convertStartingItemsToRandoItemId(const std::string& input, const std::string& delimiter) { + std::vector result; + size_t start = 0; + size_t end = input.find(delimiter); + + while (end != std::string::npos) { + std::string item = input.substr(start, end - start); + if (!item.empty()) { + result.push_back(static_cast(std::stoul(item))); + } + start = end + delimiter.length(); + end = input.find(delimiter, start); + } + + if (!input.substr(start).empty()) { + result.push_back(static_cast(std::stoul(input.substr(start)))); + } + + return result; +} + std::string convertEnumToReadableName(const std::string& input) { std::string result; std::string content = input; diff --git a/mm/2s2h/ShipUtils.h b/mm/2s2h/ShipUtils.h index e684afcfe..db1c13ff0 100644 --- a/mm/2s2h/ShipUtils.h +++ b/mm/2s2h/ShipUtils.h @@ -9,9 +9,13 @@ #include #include #include +#include #include +#include "Rando/Rando.h" void LoadGuiTextures(); std::string convertEnumToReadableName(const std::string& input); +std::vector convertStartingItemsToRandoItemId(const std::string& input, const std::string& delimiter); +std::string CreateStartingItemsToCvar(std::vector startingItemList); std::string Ship_RemoveSpecialCharacters(const std::string& str); extern std::array digitList; extern std::string Ship_FormatTimeDisplay(uint32_t value); diff --git a/mm/include/z64save.h b/mm/include/z64save.h index b2c14f752..3dfb34a14 100644 --- a/mm/include/z64save.h +++ b/mm/include/z64save.h @@ -385,6 +385,7 @@ typedef struct RandoSaveInfo { RandoSaveCheck randoSaveChecks[RC_MAX]; u32 finalSeed; u32 randoSaveOptions[RO_MAX]; // Type here may change in the future + char randoStartingItems[512]; s8 foundDungeonKeys[9]; // Tracks the number of dungeon keys found, opposed to the number of keys in the inventory u8 foundTriforcePieces; } RandoSaveInfo; -- cgit v1.2.3