diff options
| author | Garrett Cox <garrettjcox@gmail.com> | 2024-11-03 14:26:39 -0600 |
|---|---|---|
| committer | Garrett Cox <garrettjcox@gmail.com> | 2024-11-03 14:26:39 -0600 |
| commit | 7fa63244e4d2f1dbfab9babeb9ddd670f57fbc60 (patch) | |
| tree | 7bb4b4be25b8c8a12e7b74a9e68487fcec87a16c | |
| parent | 0e4a2e9fc8ca15a046c81a460b9716028f6dece6 (diff) | |
PoC glitchless playthrough implementation
| -rw-r--r-- | mm/2s2h/BenGui/BenGui.cpp | 13 | ||||
| -rw-r--r-- | mm/2s2h/DeveloperTools/EventLog.cpp | 52 | ||||
| -rw-r--r-- | mm/2s2h/DeveloperTools/SaveEditor.cpp | 6 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnOsn.cpp | 9 | ||||
| -rw-r--r-- | mm/2s2h/Rando/CheckTracker/CheckTracker.cpp | 59 | ||||
| -rw-r--r-- | mm/2s2h/Rando/CheckTracker/CheckTracker.h | 33 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Menu.cpp | 15 | ||||
| -rw-r--r-- | mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp | 173 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Rando.h | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/RemoveItem.cpp | 129 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Checks.cpp | 10 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Regions.cpp | 147 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/StaticData.h | 10 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Types.h | 30 | ||||
| -rw-r--r-- | mm/2s2h/ShipUtils.cpp | 22 | ||||
| -rw-r--r-- | mm/2s2h/ShipUtils.h | 1 | ||||
| -rw-r--r-- | mm/include/functions.h | 1 | ||||
| -rw-r--r-- | mm/src/code/z_actor.c | 8 | ||||
| -rw-r--r-- | mm/src/code/z_parameter.c | 7 |
19 files changed, 677 insertions, 49 deletions
diff --git a/mm/2s2h/BenGui/BenGui.cpp b/mm/2s2h/BenGui/BenGui.cpp index 4a9fa2967..eed6d0b09 100644 --- a/mm/2s2h/BenGui/BenGui.cpp +++ b/mm/2s2h/BenGui/BenGui.cpp @@ -9,6 +9,7 @@ #include "UIWidgets.hpp" #include "HudEditor.h" #include "Notification.h" +#include "2s2h/Rando/CheckTracker/CheckTracker.h" #ifdef __APPLE__ #include "graphic/Fast3D/gfx_metal.h" @@ -38,6 +39,8 @@ std::shared_ptr<EventLogWindow> mEventLogWindow; std::shared_ptr<BenMenu> mBenMenu; std::shared_ptr<BenInputEditorWindow> mBenInputEditorWindow; std::shared_ptr<Notification::Window> mNotificationWindow; +std::shared_ptr<Rando::CheckTracker::Window> mRandoCheckTrackerWindow; +std::shared_ptr<Rando::CheckTracker::SettingsWindow> mRandoCheckTrackerSettingsWindow; void SetupGuiElements() { auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); @@ -99,6 +102,14 @@ void SetupGuiElements() { mNotificationWindow = std::make_shared<Notification::Window>("gWindows.Notifications", "Notifications Window"); gui->AddGuiWindow(mNotificationWindow); mNotificationWindow->Show(); + + mRandoCheckTrackerWindow = + std::make_shared<Rando::CheckTracker::Window>("gWindows.CheckTracker", "Check Tracker", ImVec2(375, 460)); + gui->AddGuiWindow(mRandoCheckTrackerWindow); + + mRandoCheckTrackerSettingsWindow = std::make_shared<Rando::CheckTracker::SettingsWindow>( + "gWindows.CheckTrackerSettings", "Check Tracker Settings"); + gui->AddGuiWindow(mRandoCheckTrackerSettingsWindow); } void Destroy() { @@ -111,6 +122,8 @@ void Destroy() { mCollisionViewerWindow = nullptr; mEventLogWindow = nullptr; mNotificationWindow = nullptr; + mRandoCheckTrackerWindow = nullptr; + mRandoCheckTrackerSettingsWindow = nullptr; mSaveEditorWindow = nullptr; mHudEditorWindow = nullptr; diff --git a/mm/2s2h/DeveloperTools/EventLog.cpp b/mm/2s2h/DeveloperTools/EventLog.cpp index e73857e75..72de4286b 100644 --- a/mm/2s2h/DeveloperTools/EventLog.cpp +++ b/mm/2s2h/DeveloperTools/EventLog.cpp @@ -7,6 +7,7 @@ #include "2s2h/BenGui/UIWidgets.hpp" #include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/ShipUtils.h" extern "C" { #include <z64.h> @@ -56,19 +57,6 @@ std::unordered_map<s16, const char*> actorNames = { #undef DEFINE_ACTOR_INTERNAL #undef DEFINE_ACTOR_UNSET -// 2S2H Added columns to scene table: entranceSceneId, betterMapSelectIndex, humanName -#define DEFINE_SCENE(_name, enumValue, _textId, _drawConfig, _restrictionFlags, _persistentCycleFlags, \ - _entranceSceneId, _betterMapSelectIndex, humanName) \ - { enumValue, humanName }, -#define DEFINE_SCENE_UNSET(_enumValue) - -std::unordered_map<s16, const char*> sceneNames = { -#include "tables/scene_table.h" -}; - -#undef DEFINE_SCENE -#undef DEFINE_SCENE_UNSET - static HOOK_ID onFlagSetHookId = 0; static HOOK_ID onFlagUnsetHookId = 0; static HOOK_ID onSceneFlagSetHookId = 0; @@ -219,25 +207,27 @@ void RegisterEventLogHooks() { TrimEventLog(); }); - onSceneInitHookId = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](s16 sceneId, - s8 spawnNum) { - eventLogEntries.insert(eventLogEntries.begin(), { - .timestamp = CurrentTime(), - .type = EVENT_LOG_ENTRY_TYPE_SCENE_INIT, - .meta = fmt::format("{} {}", sceneNames[sceneId], spawnNum), - }); - TrimEventLog(); - }); + onSceneInitHookId = + GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](s16 sceneId, s8 spawnNum) { + eventLogEntries.insert(eventLogEntries.begin(), + { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_SCENE_INIT, + .meta = fmt::format("{} {}", Ship_GetSceneName(sceneId), spawnNum), + }); + TrimEventLog(); + }); - onRoomInitHookId = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnRoomInit>([](s16 sceneId, - s8 roomId) { - eventLogEntries.insert(eventLogEntries.begin(), { - .timestamp = CurrentTime(), - .type = EVENT_LOG_ENTRY_TYPE_ROOM_INIT, - .meta = fmt::format("{} {}", sceneNames[sceneId], roomId), - }); - TrimEventLog(); - }); + onRoomInitHookId = + GameInteractor::Instance->RegisterGameHook<GameInteractor::OnRoomInit>([](s16 sceneId, s8 roomId) { + eventLogEntries.insert(eventLogEntries.begin(), + { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_ROOM_INIT, + .meta = fmt::format("{} {}", Ship_GetSceneName(sceneId), roomId), + }); + TrimEventLog(); + }); onOpenTextHookId = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOpenText>( [](u16* textId, bool* loadFromMessageTable) { diff --git a/mm/2s2h/DeveloperTools/SaveEditor.cpp b/mm/2s2h/DeveloperTools/SaveEditor.cpp index 7cc3fed22..18c5b0fc8 100644 --- a/mm/2s2h/DeveloperTools/SaveEditor.cpp +++ b/mm/2s2h/DeveloperTools/SaveEditor.cpp @@ -1187,9 +1187,13 @@ void DrawQuestStatusTab() { ImGui::SeparatorText("Randomizer Check Status"); for (auto& [_, randoStaticCheck] : Rando::StaticData::Checks) { + RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId]; + if (!randoSaveCheck.shuffled) { + continue; + } + std::string hiddenName = "##"; hiddenName += randoStaticCheck.name; - RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId]; UIWidgets::Checkbox((hiddenName + "eligible").c_str(), &randoSaveCheck.eligible); ImGui::SetItemTooltip("Eligible"); ImGui::SameLine(); diff --git a/mm/2s2h/Rando/ActorBehavior/EnOsn.cpp b/mm/2s2h/Rando/ActorBehavior/EnOsn.cpp index 972c2dd6b..d918453c0 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnOsn.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnOsn.cpp @@ -8,10 +8,15 @@ extern "C" { // This interaction is skipped by the SkipLearningSongOfHealing and forced on for rando for now, this file simply // handles queuing up the checks to be given. void Rando::ActorBehavior::InitEnOsnBehavior() { + COND_VB_SHOULD(VB_OSN_CONSIDER_ELIGIBLE_FOR_SONG_OF_HEALING, IS_RANDO, { + *should = INV_CONTENT(ITEM_OCARINA_OF_TIME) == ITEM_OCARINA_OF_TIME && + !RANDO_SAVE_CHECKS[RC_CLOCK_TOWER_INTERIOR_SONG_OF_HEALING].obtained; + }); + COND_VB_SHOULD(VB_GIVE_ITEM_FROM_OSN, IS_RANDO, { *should = false; - RANDO_SAVE_CHECKS[RC_CLOCK_TOWN_DEKU_MASK].eligible = true; - RANDO_SAVE_CHECKS[RC_CLOCK_TOWN_SONG_OF_HEALING].eligible = true; + RANDO_SAVE_CHECKS[RC_CLOCK_TOWER_INTERIOR_DEKU_MASK].eligible = true; + RANDO_SAVE_CHECKS[RC_CLOCK_TOWER_INTERIOR_SONG_OF_HEALING].eligible = true; }); } diff --git a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp new file mode 100644 index 000000000..fcfcc318d --- /dev/null +++ b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp @@ -0,0 +1,59 @@ + +#include "CheckTracker.h" +#include "2s2h/ShipUtils.h" + +namespace Rando { + +namespace CheckTracker { + +void FindReachableRegions(RandoRegionId currentRegion, std::set<RandoRegionId>& reachableRegions) { + auto& randoStaticRegion = Rando::StaticData::Regions[currentRegion]; + + for (auto& [neighborRegionId, accessLogicFunc] : randoStaticRegion.regions) { + // Check if the region is accessible and hasn’t been visited yet + if (reachableRegions.count(neighborRegionId) == 0 && accessLogicFunc()) { + reachableRegions.insert(neighborRegionId); // Mark region as visited + FindReachableRegions(neighborRegionId, reachableRegions); // Recursively visit neighbors + } + } +} + +void Window::DrawElement() { + std::set<RandoRegionId> reachableRegions = { RR_CLOCK_TOWN_SOUTH }; + FindReachableRegions(RR_CLOCK_TOWN_SOUTH, reachableRegions); + + for (RandoRegionId regionId : reachableRegions) { + auto& randoStaticRegion = Rando::StaticData::Regions[regionId]; + std::vector<RandoCheckId> availableChecks; + + for (auto& [randoCheckId, accessLogicFunc] : randoStaticRegion.checks) { + auto& randoStaticCheck = Rando::StaticData::Checks[randoCheckId]; + auto& randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId]; + if (randoSaveCheck.shuffled && !randoSaveCheck.obtained && accessLogicFunc()) { + availableChecks.push_back(randoCheckId); + } + } + + if (availableChecks.size() > 0) { + std::string regionName = Ship_GetSceneName(randoStaticRegion.sceneId); + if (randoStaticRegion.name != "") { + regionName += " - "; + regionName += randoStaticRegion.name; + } + ImGui::SeparatorText(regionName.c_str()); + + for (RandoCheckId checkId : availableChecks) { + auto& randoStaticCheck = Rando::StaticData::Checks[checkId]; + ImGui::Text("%s", randoStaticCheck.name); + } + } + } +} + +void SettingsWindow::DrawElement() { + ImGui::Text("Check Tracker Settings"); +} + +} // namespace CheckTracker + +} // namespace Rando diff --git a/mm/2s2h/Rando/CheckTracker/CheckTracker.h b/mm/2s2h/Rando/CheckTracker/CheckTracker.h new file mode 100644 index 000000000..84a7ff956 --- /dev/null +++ b/mm/2s2h/Rando/CheckTracker/CheckTracker.h @@ -0,0 +1,33 @@ +#ifndef RANDO_CHECK_TRACKER_H +#define RANDO_CHECK_TRACKER_H + +#include "Rando/Rando.h" +#include <libultraship/libultraship.h> + +namespace Rando { + +namespace CheckTracker { + +class Window : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override{}; + void DrawElement() override; + void UpdateElement() override{}; +}; + +class SettingsWindow : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override{}; + void DrawElement() override; + void UpdateElement() override{}; +}; + +} // namespace CheckTracker + +} // namespace Rando + +#endif // RANDO_CHECK_TRACKER_H diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index b96406b1c..2ab3af991 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -2,10 +2,16 @@ #include "Rando/Spoiler/Spoiler.h" #include <libultraship/libultraship.h> #include "2s2h/BenGui/UIWidgets.hpp" +#include "Rando/CheckTracker/CheckTracker.h" #include "BenPort.h" // TODO: This block should come from elsewhere, tied to data in Rando::StaticData::Options -std::vector<std::string> logicOptions = { "No Logic", "Vanilla" }; +std::vector<std::string> logicOptions = { "No Logic", "Vanilla", "Glitchless" }; + +namespace BenGui { +extern std::shared_ptr<Rando::CheckTracker::Window> mRandoCheckTrackerWindow; +extern std::shared_ptr<Rando::CheckTracker::SettingsWindow> mRandoCheckTrackerSettingsWindow; +} // namespace BenGui void Rando::DrawMenu() { if (UIWidgets::BeginMenu("Rando", UIWidgets::Colors::Green)) { @@ -48,6 +54,13 @@ void Rando::DrawMenu() { } } + UIWidgets::WindowButton("Check Tracker", "gWindows.CheckTracker", BenGui::mRandoCheckTrackerWindow); + // TODO: Implement Check Tracker Settings + // ImGui::SameLine(); + // UIWidgets::WindowButton(ICON_FA_COG, "gWindows.CheckTrackerSettings", + // BenGui::mRandoCheckTrackerSettingsWindow, + // { .size = UIWidgets::Sizes::Inline }); + ImGui::EndMenu(); } } diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp index 9d7c95b60..09e4daa1f 100644 --- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp +++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp @@ -10,6 +10,27 @@ extern "C" { #include "ShipUtils.h" } +void FindReachableRegions(RandoRegionId currentRegion, std::set<RandoRegionId>& reachableRegions) { + auto& randoStaticRegion = Rando::StaticData::Regions[currentRegion]; + + for (auto& [neighborRegionId, accessLogicFunc] : randoStaticRegion.regions) { + // Check if the region is accessible and hasn’t been visited yet + if (reachableRegions.count(neighborRegionId) == 0 && accessLogicFunc()) { + reachableRegions.insert(neighborRegionId); // Mark region as visited + FindReachableRegions(neighborRegionId, reachableRegions); // Recursively visit neighbors + } + } +} + +struct RandoPoolEntry { + bool shuffled; + RandoItemId vanillaItemId; + RandoItemId placedItemId; + bool itemPlaced; + bool checkFilled; + bool inPool; +}; + // Very primitive randomizer implementation, when a save is created, if rando is enabled // we set the save type to rando and shuffle all checks and persist the results to the save void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { @@ -81,6 +102,158 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { RANDO_SAVE_CHECKS[checkPool[i]].shuffled = true; RANDO_SAVE_CHECKS[checkPool[i]].randoItemId = itemPool[i]; } + } else if (RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_GLITCHLESS) { + std::unordered_map<RandoCheckId, RandoPoolEntry> randoCheckPool; + SaveContext copiedSaveContext; + memcpy(&copiedSaveContext, &gSaveContext, sizeof(SaveContext)); + + // As far as logic is concerned, we start in Deku form + gSaveContext.save.playerForm = PLAYER_FORM_DEKU; + + // First loop through all regions and add checks/items to the pool + for (auto& [randoRegionId, randoStaticRegion] : Rando::StaticData::Regions) { + for (auto& [randoCheckId, _] : randoStaticRegion.checks) { + auto& randoStaticCheck = Rando::StaticData::Checks[randoCheckId]; + + if (randoStaticCheck.randoCheckType == RCTYPE_SKULL_TOKEN && + RANDO_SAVE_OPTIONS[RO_SHUFFLE_GOLD_SKULLTULAS] == RO_GENERIC_NO) { + continue; + } + + if (randoStaticCheck.randoCheckType == RCTYPE_POT && + RANDO_SAVE_OPTIONS[RO_SHUFFLE_POTS] == RO_GENERIC_NO) { + continue; + } + + if (randoStaticCheck.randoCheckType == RCTYPE_SHOP) { + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_SHOPS] == RO_GENERIC_NO) { + continue; + } else { + RANDO_SAVE_CHECKS[randoCheckId].price = Ship_Random(0, 200); + } + } + + randoCheckPool[randoCheckId] = { true, randoStaticCheck.randoItemId, + RI_UNKNOWN, false, + false, false }; + } + } + + // Recursive lambda for backtracking placement + std::function<bool(std::set<RandoRegionId>)> PlaceItems = + [&](std::set<RandoRegionId> reachableRegions) -> bool { + // Crawl through all reachable regions and add any new reachable regions + std::set<RandoRegionId> currentReachableRegions = reachableRegions; + for (RandoRegionId regionId : reachableRegions) { + FindReachableRegions(regionId, currentReachableRegions); + } + + // Mark newly accessible checks + std::vector<RandoCheckId> newChecksInPool; + for (RandoRegionId regionId : currentReachableRegions) { + auto& randoStaticRegion = Rando::StaticData::Regions[regionId]; + for (auto& [randoCheckId, accessLogicFunc] : randoStaticRegion.checks) { + if ( + // Check is shuffled + randoCheckPool[randoCheckId].shuffled && + // Check is not already in the pool + randoCheckPool[randoCheckId].inPool == false) { + // Check is accessible + if (accessLogicFunc()) { + randoCheckPool[randoCheckId].inPool = true; + newChecksInPool.push_back(randoCheckId); + } else { + SPDLOG_INFO("Check {} is not accessible", + Rando::StaticData::Checks[randoCheckId].name); + } + } + } + } + + int checksLeft = 0; + std::vector<RandoCheckId> currentCheckPool; + std::vector<std::pair<RandoItemId, RandoCheckId>> currentItemPool; + for (auto& [randoCheckId, randoPoolEntry] : randoCheckPool) { + if (randoPoolEntry.inPool) { + if (!randoPoolEntry.itemPlaced) { + currentItemPool.push_back({ randoPoolEntry.vanillaItemId, randoCheckId }); + } + if (!randoPoolEntry.checkFilled) { + currentCheckPool.push_back(randoCheckId); + } + } + if (randoPoolEntry.shuffled && !randoPoolEntry.checkFilled) { + checksLeft++; + } + } + if (checksLeft == 0) { + return true; // All items placed + } + + if (currentItemPool.size() == 0) { + for (RandoCheckId randoCheckId : newChecksInPool) { + randoCheckPool[randoCheckId].inPool = false; + } + return false; // No more items to place + } + + // Shuffle the pool + if (currentItemPool.size() > 1) { + for (size_t i = 0; i < currentItemPool.size(); i++) { + std::swap(currentItemPool[i], + currentItemPool[Ship_Random(0, currentItemPool.size() - 1)]); + } + } + + for (size_t i = 0; i < currentItemPool.size(); i++) { + // Place the item in the check + RandoCheckId randoCheckId = currentCheckPool[i]; + auto [randoItemId, randoCheckIdFromItem] = currentItemPool[i]; + SPDLOG_INFO("Placing item {} in check {}", + Rando::StaticData::Items[randoItemId].spoilerName, + Rando::StaticData::Checks[randoCheckId].name); + randoCheckPool[randoCheckId].checkFilled = true; + randoCheckPool[randoCheckId].placedItemId = randoItemId; + randoCheckPool[randoCheckIdFromItem].itemPlaced = true; + RandoItemId convertedItemId = ConvertItem(randoItemId); + GiveItem(convertedItemId); + + // Recurse to place the next item + if (PlaceItems(currentReachableRegions)) { + return true; // Found a solution + } + + SPDLOG_INFO("Failed to place item {} in check {}", + Rando::StaticData::Items[randoItemId].spoilerName, + Rando::StaticData::Checks[randoCheckId].name); + // Backtrack: remove the item and try another check + randoCheckPool[randoCheckId].checkFilled = false; + randoCheckPool[randoCheckId].placedItemId = RI_UNKNOWN; + randoCheckPool[randoCheckIdFromItem].itemPlaced = false; + RemoveItem(convertedItemId); + } + + for (RandoCheckId randoCheckId : newChecksInPool) { + randoCheckPool[randoCheckId].inPool = false; + } + + return false; // No valid placements for this item + }; + + if (!PlaceItems({ RR_CLOCK_TOWN_SOUTH })) { + memcpy(&gSaveContext, &copiedSaveContext, sizeof(SaveContext)); + throw std::runtime_error("Failed to place all items in glitchless logic, input seed: " + + inputSeed); + } else { + memcpy(&gSaveContext, &copiedSaveContext, sizeof(SaveContext)); + + for (auto& [randoCheckId, randoPoolEntry] : randoCheckPool) { + RANDO_SAVE_CHECKS[randoCheckId].randoItemId = randoPoolEntry.placedItemId; + RANDO_SAVE_CHECKS[randoCheckId].shuffled = true; + } + + SPDLOG_INFO("Successfully placed all items in glitchless logic, input seed: {}", inputSeed); + } } if (CVarGetInteger("gRando.GenerateSpoiler", 0)) { diff --git a/mm/2s2h/Rando/Rando.h b/mm/2s2h/Rando/Rando.h index 8733237fc..f6b0b3b4f 100644 --- a/mm/2s2h/Rando/Rando.h +++ b/mm/2s2h/Rando/Rando.h @@ -13,6 +13,7 @@ namespace Rando { void Init(); void DrawItem(RandoItemId randoItemId); void GiveItem(RandoItemId randoItemId); +void RemoveItem(RandoItemId randoItemId); RandoItemId ConvertItem(RandoItemId randoItemId, RandoCheckId randoCheckId = RC_UNKNOWN); void DrawMenu(); diff --git a/mm/2s2h/Rando/RemoveItem.cpp b/mm/2s2h/Rando/RemoveItem.cpp new file mode 100644 index 000000000..1de95e2ad --- /dev/null +++ b/mm/2s2h/Rando/RemoveItem.cpp @@ -0,0 +1,129 @@ +#include "Rando/Rando.h" +#include <libultraship/libultraship.h> + +extern "C" { +#include "variables.h" +#include "functions.h" +} + +void Rando::RemoveItem(RandoItemId randoItemId) { + switch (randoItemId) { + case RI_CLOCK_TOWN_STRAY_FAIRY: + CLEAR_WEEKEVENTREG(WEEKEVENTREG_08_80); + break; + case RI_WOODFALL_STRAY_FAIRY: + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_INDEX_WOODFALL_TEMPLE]--; + break; + case RI_SNOWHEAD_STRAY_FAIRY: + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_INDEX_SNOWHEAD_TEMPLE]--; + break; + case RI_GREAT_BAY_STRAY_FAIRY: + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_INDEX_GREAT_BAY_TEMPLE]--; + break; + case RI_STONE_TOWER_STRAY_FAIRY: + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_INDEX_STONE_TOWER_TEMPLE]--; + break; + case RI_GREAT_SPIN_ATTACK: + CLEAR_WEEKEVENTREG(WEEKEVENTREG_OBTAINED_GREAT_SPIN_ATTACK); + break; + case RI_DOUBLE_DEFENSE: + gSaveContext.save.saveInfo.playerData.doubleDefense = false; + gSaveContext.save.saveInfo.inventory.defenseHearts = 0; + break; + case RI_SINGLE_MAGIC: + gSaveContext.save.saveInfo.playerData.isMagicAcquired = false; + break; + case RI_DOUBLE_MAGIC: + gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = false; + break; + case RI_WOODFALL_BOSS_KEY: + case RI_WOODFALL_MAP: + case RI_WOODFALL_COMPASS: + REMOVE_DUNGEON_ITEM(Rando::StaticData::Items[randoItemId].itemId - ITEM_KEY_BOSS, + DUNGEON_INDEX_WOODFALL_TEMPLE); + break; + case RI_WOODFALL_SMALL_KEY: + DUNGEON_KEY_COUNT(DUNGEON_INDEX_WOODFALL_TEMPLE)--; + gSaveContext.save.shipSaveInfo.rando.foundDungeonKeys[DUNGEON_INDEX_WOODFALL_TEMPLE]--; + break; + case RI_SNOWHEAD_BOSS_KEY: + case RI_SNOWHEAD_MAP: + case RI_SNOWHEAD_COMPASS: + REMOVE_DUNGEON_ITEM(Rando::StaticData::Items[randoItemId].itemId - ITEM_KEY_BOSS, + DUNGEON_INDEX_SNOWHEAD_TEMPLE); + break; + case RI_SNOWHEAD_SMALL_KEY: + DUNGEON_KEY_COUNT(DUNGEON_INDEX_SNOWHEAD_TEMPLE)--; + gSaveContext.save.shipSaveInfo.rando.foundDungeonKeys[DUNGEON_INDEX_WOODFALL_TEMPLE]--; + break; + case RI_GREAT_BAY_BOSS_KEY: + case RI_GREAT_BAY_MAP: + case RI_GREAT_BAY_COMPASS: + REMOVE_DUNGEON_ITEM(Rando::StaticData::Items[randoItemId].itemId - ITEM_KEY_BOSS, + DUNGEON_INDEX_GREAT_BAY_TEMPLE); + break; + case RI_GREAT_BAY_SMALL_KEY: + DUNGEON_KEY_COUNT(DUNGEON_INDEX_GREAT_BAY_TEMPLE)--; + gSaveContext.save.shipSaveInfo.rando.foundDungeonKeys[DUNGEON_INDEX_WOODFALL_TEMPLE]--; + break; + case RI_STONE_TOWER_BOSS_KEY: + case RI_STONE_TOWER_MAP: + case RI_STONE_TOWER_COMPASS: + REMOVE_DUNGEON_ITEM(Rando::StaticData::Items[randoItemId].itemId - ITEM_KEY_BOSS, + DUNGEON_INDEX_STONE_TOWER_TEMPLE); + break; + case RI_STONE_TOWER_SMALL_KEY: + DUNGEON_KEY_COUNT(DUNGEON_INDEX_STONE_TOWER_TEMPLE)--; + gSaveContext.save.shipSaveInfo.rando.foundDungeonKeys[DUNGEON_INDEX_WOODFALL_TEMPLE]--; + break; + case RI_PROGRESSIVE_MAGIC: + case RI_PROGRESSIVE_BOW: + case RI_PROGRESSIVE_BOMB_BAG: + // TODO + break; + case RI_GS_TOKEN_SWAMP: { + int skullTokenCount = Inventory_GetSkullTokenCount(SCENE_KINSTA1); + gSaveContext.save.saveInfo.skullTokenCount = + ((int)(skullTokenCount & 0xFFFF) << 0x10) | (gSaveContext.save.saveInfo.skullTokenCount & 0xFFFF); + break; + } + case RI_GS_TOKEN_OCEAN: { + int skullTokenCount = Inventory_GetSkullTokenCount(SCENE_KINDAN2); + gSaveContext.save.saveInfo.skullTokenCount = + (gSaveContext.save.saveInfo.skullTokenCount & 0xFFFF0000) | (skullTokenCount & 0xFFFF); + break; + } + case RI_MOONS_TEAR: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_MOONS_TEAR); + break; + case RI_DEED_LAND: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_DEED_LAND); + break; + case RI_DEED_SWAMP: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_DEED_SWAMP); + break; + case RI_DEED_MOUNTAIN: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_DEED_MOUNTAIN); + break; + case RI_DEED_OCEAN: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_DEED_OCEAN); + break; + case RI_ROOM_KEY: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_ROOM_KEY); + break; + case RI_LETTER_TO_MAMA: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_LETTER_TO_MAMA); + break; + case RI_LETTER_TO_KAFEI: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_LETTER_TO_KAFEI); + break; + case RI_PENDANT_OF_MEMORIES: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_PENDANT_OF_MEMORIES); + break; + default: + // TODO: Handle equipment + Inventory_DeleteItem(Rando::StaticData::Items[randoItemId].itemId, + SLOT(Rando::StaticData::Items[randoItemId].itemId)); + break; + } +} diff --git a/mm/2s2h/Rando/StaticData/Checks.cpp b/mm/2s2h/Rando/StaticData/Checks.cpp index 25cc40997..41eb50024 100644 --- a/mm/2s2h/Rando/StaticData/Checks.cpp +++ b/mm/2s2h/Rando/StaticData/Checks.cpp @@ -29,17 +29,17 @@ std::map<RandoCheckId, RandoStaticCheck> Checks = { RC(RC_BENEATH_THE_WELL_KEESE_CHEST, RCTYPE_CHEST, SCENE_REDEAD, FLAG_CYCL_SCENE_CHEST, 0x02, RI_RUPEE_PURPLE), RC(RC_BENEATH_THE_WELL_MIRROR_SHIELD, RCTYPE_CHEST, SCENE_REDEAD, FLAG_CYCL_SCENE_CHEST, 0x1b, RI_SHIELD_MIRROR), RC(RC_BENEATH_THE_WELL_SKULLTULLA_CHEST, RCTYPE_CHEST, SCENE_REDEAD, FLAG_CYCL_SCENE_CHEST, 0x01, RI_RUPEE_PURPLE), + RC(RC_CLOCK_TOWER_INTERIOR_DEKU_MASK, RCTYPE_NPC, SCENE_INSIDETOWER, FLAG_NONE, 0x00, RI_MASK_DEKU), + RC(RC_CLOCK_TOWER_INTERIOR_SONG_OF_HEALING, RCTYPE_NPC, SCENE_INSIDETOWER, FLAG_NONE, 0x00, RI_SONG_OF_HEALING), RC(RC_CLOCK_TOWER_ROOF_OCARINA, RCTYPE_NPC, SCENE_OKUJOU, FLAG_NONE, 0x00, RI_OCARINA), RC(RC_CLOCK_TOWER_ROOF_SONG_OF_TIME, RCTYPE_SONG, SCENE_OKUJOU, FLAG_NONE, 0x00, RI_SONG_OF_TIME), - RC(RC_CLOCK_TOWN_DEKU_MASK, RCTYPE_NPC, SCENE_INSIDETOWER, FLAG_NONE, 0x00, RI_MASK_DEKU), RC(RC_CLOCK_TOWN_EAST_UPPER_CHEST, RCTYPE_CHEST, SCENE_TOWN, FLAG_CYCL_SCENE_CHEST, 0x0A, RI_RUPEE_SILVER), RC(RC_CLOCK_TOWN_GREAT_FAIRY_ALT, RCTYPE_NPC, SCENE_YOUSEI_IZUMI, FLAG_NONE, 0x00, RI_MASK_GREAT_FAIRY), RC(RC_CLOCK_TOWN_GREAT_FAIRY, RCTYPE_NPC, SCENE_YOUSEI_IZUMI, FLAG_NONE, 0x00, RI_PROGRESSIVE_MAGIC), RC(RC_CLOCK_TOWN_LAUNDRY_GURU_GURU, RCTYPE_NPC, SCENE_ALLEY, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_38_40, RI_MASK_BREMEN), - RC(RC_CLOCK_TOWN_NORTH_TREE_HP, RCTYPE_FREESTANDING, SCENE_BACKTOWN, FLAG_CYCL_SCENE_COLLECTIBLE, 0x0A, RI_HEART_PIECE), RC(RC_CLOCK_TOWN_NORTH_BOMB_LADY, RCTYPE_NPC, SCENE_BACKTOWN, FLAG_CYCL_SCENE_COLLECTIBLE, WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_BLAST_MASK, RI_MASK_BLAST), + RC(RC_CLOCK_TOWN_NORTH_TREE_HP, RCTYPE_FREESTANDING, SCENE_BACKTOWN, FLAG_CYCL_SCENE_COLLECTIBLE, 0x0A, RI_HEART_PIECE), RC(RC_CLOCK_TOWN_SCRUB_DEED, RCTYPE_NPC, SCENE_CLOCKTOWER, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_17_80, RI_DEED_LAND), - RC(RC_CLOCK_TOWN_SONG_OF_HEALING, RCTYPE_NPC, SCENE_INSIDETOWER, FLAG_NONE, 0x00, RI_SONG_OF_HEALING), RC(RC_CLOCK_TOWN_SOUTH_CHEST_LOWER, RCTYPE_CHEST, SCENE_CLOCKTOWER, FLAG_CYCL_SCENE_CHEST, 0x00, RI_RUPEE_RED), RC(RC_CLOCK_TOWN_SOUTH_CHEST_UPPER, RCTYPE_CHEST, SCENE_CLOCKTOWER, FLAG_CYCL_SCENE_CHEST, 0x01, RI_RUPEE_PURPLE), RC(RC_CLOCK_TOWN_SOUTH_PLATFORM_HP, RCTYPE_FREESTANDING, SCENE_CLOCKTOWER, FLAG_CYCL_SCENE_COLLECTIBLE, 0x0A, RI_HEART_PIECE), @@ -86,8 +86,8 @@ std::map<RandoCheckId, RandoStaticCheck> Checks = { RC(RC_LONE_PEAK_SHRINE_BOULDER_CHEST, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x06, RI_RUPEE_RED), RC(RC_LONE_PEAK_SHRINE_INVISIBLE_CHEST, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x03, RI_RUPEE_PURPLE), RC(RC_LONE_PEAK_SHRINE_LENS_CHEST, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x01, RI_LENS), - RC(RC_MAYORS_OFFICE_KAFEIS_MASK, RCTYPE_NPC, SCENE_SONCHONOIE, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KAFEIS_MASK, RI_MASK_KAFEIS_MASK), RC(RC_MAYORS_OFFICE_HP, RCTYPE_NPC, SCENE_SONCHONOIE, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_MAYOR_HP, RI_HEART_PIECE), + RC(RC_MAYORS_OFFICE_KAFEIS_MASK, RCTYPE_NPC, SCENE_SONCHONOIE, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KAFEIS_MASK, RI_MASK_KAFEIS_MASK), RC(RC_MOON_TRIAL_DEKU_HP, RCTYPE_FREESTANDING, SCENE_LAST_DEKU, FLAG_CYCL_SCENE_COLLECTIBLE, 0x01, RI_HEART_PIECE), RC(RC_MOON_TRIAL_GORON_HP, RCTYPE_FREESTANDING, SCENE_LAST_GORON, FLAG_CYCL_SCENE_COLLECTIBLE, 0x01, RI_HEART_PIECE), RC(RC_MOON_TRIAL_LINK_GARO_MASTER_CHEST, RCTYPE_CHEST, SCENE_LAST_LINK, FLAG_CYCL_SCENE_CHEST, 0x01, RI_ARROWS_30), @@ -252,13 +252,13 @@ std::map<RandoCheckId, RandoStaticCheck> Checks = { RC(RC_SWORDSMAN_SCHOOL_HP, RCTYPE_NPC, SCENE_DOUJOU, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_63_20, RI_HEART_PIECE), RC(RC_TERMINA_FIELD_BIO_BABA_GROTTO, RCTYPE_FREESTANDING, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_COLLECTIBLE, 0x02, RI_HEART_PIECE), RC(RC_TERMINA_FIELD_DODONGO_GROTTO, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x00, RI_HEART_PIECE), + RC(RC_TERMINA_FIELD_KAMARO, RCTYPE_NPC, SCENE_00KEIKOKU, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KAMAROS_MASK, RI_MASK_KAMARO), RC(RC_TERMINA_FIELD_PEAHAT_GROTTO, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x04, RI_HEART_PIECE), RC(RC_TERMINA_FIELD_PILLAR_GROTTO, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x1a, RI_BOMBCHU), RC(RC_TERMINA_FIELD_TALL_GRASS_CHEST, RCTYPE_CHEST, SCENE_00KEIKOKU, FLAG_CYCL_SCENE_CHEST, 0x01, RI_RUPEE_RED), RC(RC_TERMINA_FIELD_TALL_GRASS_GROTTO, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x1f, RI_RUPEE_RED), RC(RC_TERMINA_FIELD_TREE_STUMP_CHEST, RCTYPE_CHEST, SCENE_00KEIKOKU, FLAG_CYCL_SCENE_CHEST, 0x02, RI_RUPEE_RED), RC(RC_TERMINA_FIELD_WATER_CHEST, RCTYPE_CHEST, SCENE_00KEIKOKU, FLAG_CYCL_SCENE_CHEST, 0x00, RI_RUPEE_RED), - RC(RC_TERMINA_FIELD_KAMARO, RCTYPE_NPC, SCENE_00KEIKOKU, FLAG_WEEK_EVENT_REG, WEEKEVENTREG_BOMBERS_NOTEBOOK_EVENT_RECEIVED_KAMAROS_MASK, RI_MASK_KAMARO), RC(RC_TWIN_ISLANDS_FROZEN_GROTTO_CHEST, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x02, RI_RUPEE_RED), RC(RC_TWIN_ISLANDS_RAMP_GROTTO_CHEST, RCTYPE_CHEST, SCENE_KAKUSIANA, FLAG_CYCL_SCENE_CHEST, 0x19, RI_BOMBCHU), RC(RC_TWIN_ISLANDS_UNDERWATER_CHEST_1, RCTYPE_CHEST, SCENE_17SETUGEN, FLAG_CYCL_SCENE_CHEST, 0x06, RI_HEART_PIECE), diff --git a/mm/2s2h/Rando/StaticData/Regions.cpp b/mm/2s2h/Rando/StaticData/Regions.cpp new file mode 100644 index 000000000..06980f921 --- /dev/null +++ b/mm/2s2h/Rando/StaticData/Regions.cpp @@ -0,0 +1,147 @@ +#include "StaticData.h" + +extern "C" { +s32 Flags_GetRandoInf(s32 flag); +} + +namespace Rando { + +namespace StaticData { + +#define IS_DEKU (GET_PLAYER_FORM == PLAYER_FORM_DEKU) +#define IS_ZORA (GET_PLAYER_FORM == PLAYER_FORM_ZORA) +#define IS_DEITY (GET_PLAYER_FORM == PLAYER_FORM_FIERCE_DEITY) +#define IS_GORON (GET_PLAYER_FORM == PLAYER_FORM_GORON) +#define IS_HUMAN (GET_PLAYER_FORM == PLAYER_FORM_HUMAN) +#define HAS_ITEM(item) (INV_CONTENT(item) == item) +#define CAN_BE_DEKU (IS_DEKU || HAS_ITEM(ITEM_MASK_DEKU)) +#define CAN_BE_ZORA (IS_ZORA || HAS_ITEM(ITEM_MASK_ZORA)) +#define CAN_BE_DIETY (IS_DEITY || HAS_ITEM(ITEM_MASK_FIERCE_DEITY)) +#define CAN_BE_GORON (IS_GORON || HAS_ITEM(ITEM_MASK_GORON)) +#define CAN_BE_HUMAN \ + (IS_HUMAN || (IS_DEITY && HAS_ITEM(ITEM_MASK_FIERCE_DEITY)) || (IS_ZORA && HAS_ITEM(ITEM_MASK_ZORA)) || \ + (IS_DEKU && HAS_ITEM(ITEM_MASK_DEKU)) || (IS_GORON && HAS_ITEM(ITEM_MASK_GORON))) +#define HAS_MAGIC (gSaveContext.save.saveInfo.playerData.isMagicAcquired) +#define CAN_USE_EXPLOSIVE (HAS_ITEM(ITEM_BOMB) || HAS_ITEM(ITEM_BOMBCHU) || (HAS_ITEM(ITEM_MASK_BLAST) && CAN_BE_HUMAN)) + +// clang-format off +std::map<RandoRegionId, RandoStaticRegion> Regions = { + { RR_CLOCK_TOWN_SOUTH, { RR_CLOCK_TOWN_SOUTH, "", SCENE_CLOCKTOWER, + { // Checks + { RC_CLOCK_TOWN_SCRUB_DEED, []() { return Flags_GetRandoInf(RANDO_INF_OBTAINED_MOONS_TEAR); } }, + { RC_CLOCK_TOWN_SOUTH_CHEST_UPPER, []() { return CAN_BE_DEKU && Flags_GetRandoInf(RANDO_INF_OBTAINED_MOONS_TEAR); } }, + { RC_CLOCK_TOWN_SOUTH_CHEST_LOWER, []() { return CAN_BE_DEKU && Flags_GetRandoInf(RANDO_INF_OBTAINED_MOONS_TEAR) && (CAN_BE_HUMAN || CAN_BE_ZORA || CAN_BE_DIETY); } }, + }, + { // Regions + { RR_CLOCK_TOWN_SOUTH_PLATFORM, []() { return CAN_BE_HUMAN || CAN_BE_ZORA || CAN_BE_DIETY || (CAN_BE_DEKU && Flags_GetRandoInf(RANDO_INF_OBTAINED_MOONS_TEAR)); } }, + { RR_CLOCK_TOWN_LAUNDRY, []() { return true; } }, + { RR_CLOCK_TOWN_NORTH, []() { return true; } }, + { RR_CLOCK_TOWN_EAST, []() { return true; } }, + { RR_CLOCK_TOWER_INTERIOR, []() { return true; } }, + { RR_CATCH_ALL, []() { return true; } }, // TODO: We'll remove this eventually + }, + } }, + { RR_CLOCK_TOWER_INTERIOR, { RR_CLOCK_TOWER_INTERIOR, "", SCENE_INSIDETOWER, + { // Checks + { RC_CLOCK_TOWER_INTERIOR_SONG_OF_HEALING, []() { return HAS_ITEM(ITEM_OCARINA_OF_TIME); } }, + { RC_CLOCK_TOWER_INTERIOR_DEKU_MASK, []() { return HAS_ITEM(ITEM_OCARINA_OF_TIME); } }, + }, + { // Regions + { RR_CLOCK_TOWN_SOUTH, []() { return true; } }, + }, + } }, + { RR_CLOCK_TOWN_SOUTH_PLATFORM, { RR_CLOCK_TOWN_SOUTH_PLATFORM, "Clock Tower Platform", SCENE_CLOCKTOWER, + { // Checks + { RC_CLOCK_TOWN_SOUTH_PLATFORM_HP, []() { return true; } }, + }, + { // Regions + { RR_CLOCK_TOWN_SOUTH, []() { return true; } }, + { RR_CLOCK_TOWER_ROOF, []() { return true; } }, + }, + } }, + { RR_CLOCK_TOWER_ROOF, { RR_CLOCK_TOWER_ROOF, "", SCENE_OKUJOU, + { // Checks + { RC_CLOCK_TOWER_ROOF_SONG_OF_TIME, []() { return HAS_MAGIC && CAN_BE_DEKU; } }, + { RC_CLOCK_TOWER_ROOF_OCARINA, []() { return HAS_MAGIC && CAN_BE_DEKU; } }, + }, + { // Regions + // TODO: Moon? + }, + } }, + { RR_CLOCK_TOWN_LAUNDRY, { RR_CLOCK_TOWN_LAUNDRY, "", SCENE_ALLEY, + { // Checks + { RC_CLOCK_TOWN_STRAY_FAIRY, []() { return true; } }, + { RC_CLOCK_TOWN_LAUNDRY_GURU_GURU, []() { return CAN_BE_DIETY || CAN_BE_HUMAN || CAN_BE_ZORA || CAN_BE_GORON; } }, + }, + { // Regions + { RR_CLOCK_TOWN_SOUTH, []() { return true; } }, + }, + } }, + { RR_CLOCK_TOWN_NORTH, { RR_CLOCK_TOWN_NORTH, "", SCENE_BACKTOWN, + { // Checks + { RC_CLOCK_TOWN_NORTH_TREE_HP, []() { return CAN_BE_DIETY || CAN_BE_HUMAN || CAN_BE_ZORA; } }, + { RC_CLOCK_TOWN_NORTH_BOMB_LADY, []() { return CAN_BE_DIETY || CAN_BE_HUMAN || CAN_BE_ZORA || CAN_BE_GORON; } }, + }, + { // Regions + { RR_CLOCK_TOWN_SOUTH, []() { return true; } }, + { RR_CLOCK_TOWN_EAST, []() { return true; } }, + { RR_CLOCK_TOWN_GREAT_FAIRY_FOUNTAIN, []() { return true; } }, + }, + } }, + { RR_CLOCK_TOWN_GREAT_FAIRY_FOUNTAIN, { RR_CLOCK_TOWN_GREAT_FAIRY_FOUNTAIN, "Clock Town", SCENE_YOUSEI_IZUMI, + { // Checks + { RC_CLOCK_TOWN_GREAT_FAIRY, []() { return CHECK_WEEKEVENTREG(WEEKEVENTREG_08_80); } }, + { RC_CLOCK_TOWN_GREAT_FAIRY_ALT, []() { return CHECK_WEEKEVENTREG(WEEKEVENTREG_08_80) && CAN_BE_HUMAN; } }, + }, + { // Regions + { RR_CLOCK_TOWN_NORTH, []() { return true; } }, + }, + } }, + { RR_CLOCK_TOWN_EAST, { RR_CLOCK_TOWN_EAST, "", SCENE_TOWN, + { // Checks + { RC_CLOCK_TOWN_STRAY_FAIRY, []() { return CAN_BE_DEKU; } }, // Same check in two places, is this okay? + }, + { // Regions + { RR_CLOCK_TOWN_NORTH, []() { return true; } }, + { RR_ASTRAL_OBSERVATORY_PASSAGE, []() { return true; } }, // TODO: Bombers Code req + }, + } }, + { RR_ASTRAL_OBSERVATORY_PASSAGE, { RR_ASTRAL_OBSERVATORY_PASSAGE, "Passage", SCENE_TENMON_DAI, + { // Checks + { RC_ASTRAL_OBSERVATORY_PASSAGE_CHEST, []() { return (CAN_BE_HUMAN || CAN_BE_DIETY || CAN_BE_ZORA) && CAN_USE_EXPLOSIVE; } }, + }, + { // Regions + { RR_CLOCK_TOWN_EAST, []() { return true; } }, + { RR_ASTRAL_OBSERVATORY, []() { return CAN_BE_DEKU && HAS_MAGIC; } }, + }, + } }, + { RR_ASTRAL_OBSERVATORY, { RR_ASTRAL_OBSERVATORY, "", SCENE_TENMON_DAI, + { // Checks + }, + { // Regions + { RR_ASTRAL_OBSERVATORY_PASSAGE, []() { return CAN_BE_DEKU && HAS_MAGIC; } }, // TODO: Trick to backflip over balloon + { RR_ASTRAL_OBSERVATORY_OUTSIDE, []() { return true; } }, + }, + } }, + { RR_ASTRAL_OBSERVATORY_OUTSIDE, { RR_ASTRAL_OBSERVATORY_OUTSIDE, "Outside Astral Observatory", SCENE_00KEIKOKU, + { // Checks + { RC_ASTRAL_OBSERVATORY_MOON_TEAR, []() { return true; } }, + }, + { // Regions + { RR_ASTRAL_OBSERVATORY, []() { return true; } }, + }, + } }, + // TODO: We'll remove this eventually + { RR_CATCH_ALL, { RR_CATCH_ALL, "", SCENE_MAX, + { // Checks + // { RC_CLOCK_TOWER_INTERIOR_DEKU_MASK, []() { return true; } }, + }, + { // Regions + }, + } }, +}; +// clang-format on + +} // namespace StaticData + +} // namespace Rando diff --git a/mm/2s2h/Rando/StaticData/StaticData.h b/mm/2s2h/Rando/StaticData/StaticData.h index 8611847c4..720f56a2d 100644 --- a/mm/2s2h/Rando/StaticData/StaticData.h +++ b/mm/2s2h/Rando/StaticData/StaticData.h @@ -52,6 +52,16 @@ extern std::map<RandoOptionId, RandoStaticOption> Options; RandoOptionId GetOptionIdFromName(const char* name); +struct RandoStaticRegion { + RandoRegionId randoRegionId; + const char* name; + SceneId sceneId; + std::unordered_map<RandoCheckId, std::function<bool()>> checks; + std::unordered_map<RandoRegionId, std::function<bool()>> regions; +}; + +extern std::map<RandoRegionId, RandoStaticRegion> Regions; + } // namespace StaticData } // namespace Rando diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index 6d64db90c..ee6d76dc1 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -27,17 +27,17 @@ typedef enum { RC_BENEATH_THE_WELL_KEESE_CHEST, RC_BENEATH_THE_WELL_MIRROR_SHIELD, RC_BENEATH_THE_WELL_SKULLTULLA_CHEST, + RC_CLOCK_TOWER_INTERIOR_DEKU_MASK, + RC_CLOCK_TOWER_INTERIOR_SONG_OF_HEALING, RC_CLOCK_TOWER_ROOF_OCARINA, RC_CLOCK_TOWER_ROOF_SONG_OF_TIME, - RC_CLOCK_TOWN_DEKU_MASK, RC_CLOCK_TOWN_EAST_UPPER_CHEST, RC_CLOCK_TOWN_GREAT_FAIRY_ALT, RC_CLOCK_TOWN_GREAT_FAIRY, RC_CLOCK_TOWN_LAUNDRY_GURU_GURU, - RC_CLOCK_TOWN_NORTH_TREE_HP, RC_CLOCK_TOWN_NORTH_BOMB_LADY, + RC_CLOCK_TOWN_NORTH_TREE_HP, RC_CLOCK_TOWN_SCRUB_DEED, - RC_CLOCK_TOWN_SONG_OF_HEALING, RC_CLOCK_TOWN_SOUTH_CHEST_LOWER, RC_CLOCK_TOWN_SOUTH_CHEST_UPPER, RC_CLOCK_TOWN_SOUTH_PLATFORM_HP, @@ -84,8 +84,8 @@ typedef enum { RC_LONE_PEAK_SHRINE_BOULDER_CHEST, RC_LONE_PEAK_SHRINE_INVISIBLE_CHEST, RC_LONE_PEAK_SHRINE_LENS_CHEST, - RC_MAYORS_OFFICE_KAFEIS_MASK, RC_MAYORS_OFFICE_HP, + RC_MAYORS_OFFICE_KAFEIS_MASK, RC_MOON_TRIAL_DEKU_HP, RC_MOON_TRIAL_GORON_HP, RC_MOON_TRIAL_LINK_GARO_MASTER_CHEST, @@ -250,13 +250,13 @@ typedef enum { RC_SWORDSMAN_SCHOOL_HP, RC_TERMINA_FIELD_BIO_BABA_GROTTO, RC_TERMINA_FIELD_DODONGO_GROTTO, + RC_TERMINA_FIELD_KAMARO, RC_TERMINA_FIELD_PEAHAT_GROTTO, RC_TERMINA_FIELD_PILLAR_GROTTO, RC_TERMINA_FIELD_TALL_GRASS_CHEST, RC_TERMINA_FIELD_TALL_GRASS_GROTTO, RC_TERMINA_FIELD_TREE_STUMP_CHEST, RC_TERMINA_FIELD_WATER_CHEST, - RC_TERMINA_FIELD_KAMARO, RC_TWIN_ISLANDS_FROZEN_GROTTO_CHEST, RC_TWIN_ISLANDS_RAMP_GROTTO_CHEST, RC_TWIN_ISLANDS_UNDERWATER_CHEST_1, @@ -895,6 +895,7 @@ typedef enum { RI_MAGIC_BEAN, RI_MAGIC_JAR_BIG, RI_MAGIC_JAR_SMALL, + RI_MASK_BLAST, RI_MASK_BREMEN, RI_MASK_BUNNY, RI_MASK_CAPTAIN, @@ -902,9 +903,8 @@ typedef enum { RI_MASK_GIANT, RI_MASK_GREAT_FAIRY, RI_MASK_KAFEIS_MASK, - RI_MASK_ROMANI, - RI_MASK_BLAST, RI_MASK_KAMARO, + RI_MASK_ROMANI, RI_MILK_REFILL, RI_MOONS_TEAR, RI_NONE, @@ -949,6 +949,22 @@ typedef enum { } RandoItemId; typedef enum { + RR_CLOCK_TOWN_SOUTH, + RR_CLOCK_TOWN_SOUTH_PLATFORM, + RR_CLOCK_TOWER_ROOF, + RR_CLOCK_TOWN_LAUNDRY, + RR_CLOCK_TOWN_NORTH, + RR_CLOCK_TOWN_GREAT_FAIRY_FOUNTAIN, + RR_CLOCK_TOWN_EAST, + RR_CLOCK_TOWER_INTERIOR, + RR_ASTRAL_OBSERVATORY, + RR_ASTRAL_OBSERVATORY_PASSAGE, + RR_ASTRAL_OBSERVATORY_OUTSIDE, + RR_CATCH_ALL, + RR_MAX, +} RandoRegionId; + +typedef enum { RO_LOGIC, RO_SHUFFLE_GOLD_SKULLTULAS, RO_SHUFFLE_POTS, diff --git a/mm/2s2h/ShipUtils.cpp b/mm/2s2h/ShipUtils.cpp index e605de725..8ab330921 100644 --- a/mm/2s2h/ShipUtils.cpp +++ b/mm/2s2h/ShipUtils.cpp @@ -10,6 +10,7 @@ extern "C" { #include "macros.h" +#include "z64.h" extern f32 sNESFontWidths[160]; extern const char* fontTbl[156]; @@ -18,6 +19,27 @@ extern TexturePtr gQuestIcons[14]; extern TexturePtr gBombersNotebookPhotos[24]; } +// 2S2H Added columns to scene table: entranceSceneId, betterMapSelectIndex, humanName +#define DEFINE_SCENE(_name, enumValue, _textId, _drawConfig, _restrictionFlags, _persistentCycleFlags, \ + _entranceSceneId, _betterMapSelectIndex, humanName) \ + { enumValue, humanName }, +#define DEFINE_SCENE_UNSET(_enumValue) + +std::unordered_map<s16, const char*> sceneNames = { +#include "tables/scene_table.h" +}; + +#undef DEFINE_SCENE +#undef DEFINE_SCENE_UNSET + +extern "C" const char* Ship_GetSceneName(s16 sceneId) { + if (sceneNames.contains(sceneId)) { + return sceneNames[sceneId]; + } + + return "Unknown"; +} + // Build vertex coordinates for a quad command // In order of top left, top right, bottom left, then bottom right // Supports flipping the texture horizontally diff --git a/mm/2s2h/ShipUtils.h b/mm/2s2h/ShipUtils.h index 61582dffe..b2ffe54ab 100644 --- a/mm/2s2h/ShipUtils.h +++ b/mm/2s2h/ShipUtils.h @@ -11,6 +11,7 @@ void LoadGuiTextures(); extern "C" { #endif +const char* Ship_GetSceneName(s16 sceneId); void Ship_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH); f32 Ship_GetCharFontWidthNES(u8 character); TexturePtr Ship_GetCharFontTextureNES(u8 character); diff --git a/mm/include/functions.h b/mm/include/functions.h index 8ce6ede41..358eb15e6 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1346,6 +1346,7 @@ void Flags_ClearEventInf(s32 flag); // #region 2S2H [Rando] s32 Flags_GetRandoInf(s32 flag); void Flags_SetRandoInf(s32 flag); +void Flags_ClearRandoInf(s32 flag); // #endregion // #region 2S2H [Port] Stubbed methods void osSetThreadPri(OSThread* thread, OSPri p); diff --git a/mm/src/code/z_actor.c b/mm/src/code/z_actor.c index 7e1bfb38e..7e7281822 100644 --- a/mm/src/code/z_actor.c +++ b/mm/src/code/z_actor.c @@ -927,6 +927,14 @@ void Flags_SetRandoInf(s32 flag) { GameInteractor_ExecuteOnFlagSet(FLAG_RANDO_INF, flag); } } + +void Flags_ClearRandoInf(s32 flag) { + u8 previouslyOn = Flags_GetRandoInf(flag); + gSaveContext.save.shipSaveInfo.rando.randoInf[(flag) >> 4] &= (u8) ~(1 << ((flag)&0xF)); + if (previouslyOn) { + GameInteractor_ExecuteOnFlagUnset(FLAG_RANDO_INF, flag); + } +} // #endregion /* End of Flags section */ diff --git a/mm/src/code/z_parameter.c b/mm/src/code/z_parameter.c index 13c5e2901..dc6a46866 100644 --- a/mm/src/code/z_parameter.c +++ b/mm/src/code/z_parameter.c @@ -3551,7 +3551,6 @@ s16 sRupeeRefillCounts[] = { 1, 5, 10, 20, 50, 100, 200 }; // 2S2H [Enhancements] This was originally Item_Give, we wrapped it for hooking purposes u8 Item_GiveImpl(PlayState* play, u8 item) { - Player* player = GET_PLAYER(play); u8 i; u8 temp; u8 slot; @@ -3605,7 +3604,11 @@ u8 Item_GiveImpl(PlayState* play, u8 item) { } else if ((item >= ITEM_SHIELD_HERO) && (item <= ITEM_SHIELD_MIRROR)) { if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) != (u16)(item - ITEM_SHIELD_HERO + EQUIP_VALUE_SHIELD_HERO)) { SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, item - ITEM_SHIELD_HERO + EQUIP_VALUE_SHIELD_HERO); - Player_SetEquipmentData(play, player); + // 2S2H [Randomizer] Added a nullptr check so that we can call this function outside of gameplay for logic + if (gPlayState != NULL) { + Player* player = GET_PLAYER(play); + Player_SetEquipmentData(play, player); + } return ITEM_NONE; } return item; |
