summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaladius <Caladius@users.noreply.github.com>2025-10-18 19:57:30 -0400
committerGitHub <noreply@github.com>2025-10-18 19:57:30 -0400
commit43fd6756030e24f49127aa8835d073038007378a (patch)
treebcc007634a7c2aaabec64493741c1ee27fe9eb6f
parent5ef25f993fd15bd08e529651a2d55f75db392ea4 (diff)
[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>
-rw-r--r--mm/2s2h/BenJsonConversions.hpp6
-rw-r--r--mm/2s2h/Rando/Menu.cpp242
-rw-r--r--mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp23
-rw-r--r--mm/2s2h/Rando/Rando.h4
-rw-r--r--mm/2s2h/Rando/Spoiler/Apply.cpp23
-rw-r--r--mm/2s2h/Rando/Spoiler/Generate.cpp2
-rw-r--r--mm/2s2h/Rando/StaticData/Items.cpp119
-rw-r--r--mm/2s2h/Rando/StaticData/Options.cpp3
-rw-r--r--mm/2s2h/Rando/StaticData/StaticData.h2
-rw-r--r--mm/2s2h/Rando/Types.h11
-rw-r--r--mm/2s2h/ShipUtils.cpp33
-rw-r--r--mm/2s2h/ShipUtils.h4
-rw-r--r--mm/include/z64save.h1
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<RandoItemId> 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<RandoItemId> 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<RandoItemId> 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<RandoItemId> 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<uint32_t>();
}
+ std::string startingItemsSave = spoiler["startingItems"].get<std::string>();
+ 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<RandoItemId> 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<RandoItemId> startingItems = convertStartingItemsToRandoItemId(RANDO_STARTING_ITEMS, ",");
if (RANDO_SAVE_OPTIONS[RO_STARTING_MAPS_AND_COMPASSES]) {
std::vector<RandoItemId> 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<RandoItemId, RandoStaticItem> 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<RandoItemId> 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<StartingItemCategory, std::vector<RandoItemId>> 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<RandoOptionId, RandoStaticOption> 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<RandoItemId, RandoStaticItem> Items;
-extern std::vector<RandoItemId> StartingItemsMap;
+extern std::unordered_map<StartingItemCategory, std::vector<RandoItemId>> 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<RandoItemId> startingItemList) {
+ std::string startingItemsStr = "";
+ for (auto& item : startingItemList) {
+ if (startingItemsStr != "") {
+ startingItemsStr += ",";
+ }
+ startingItemsStr += std::to_string(item).c_str();
+ }
+
+ return startingItemsStr;
+}
+
+std::vector<RandoItemId> convertStartingItemsToRandoItemId(const std::string& input, const std::string& delimiter) {
+ std::vector<RandoItemId> 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<RandoItemId>(std::stoul(item)));
+ }
+ start = end + delimiter.length();
+ end = input.find(delimiter, start);
+ }
+
+ if (!input.substr(start).empty()) {
+ result.push_back(static_cast<RandoItemId>(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 <string>
#include <array>
#include <map>
+#include <vector>
#include <imgui.h>
+#include "Rando/Rando.h"
void LoadGuiTextures();
std::string convertEnumToReadableName(const std::string& input);
+std::vector<RandoItemId> convertStartingItemsToRandoItemId(const std::string& input, const std::string& delimiter);
+std::string CreateStartingItemsToCvar(std::vector<RandoItemId> startingItemList);
std::string Ship_RemoveSpecialCharacters(const std::string& str);
extern std::array<const char*, 11> 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;