From 57265903d1546fe5d8b98768b9f964b8f6e612e1 Mon Sep 17 00:00:00 2001 From: SuperDude88 <82904174+SuperDude88@users.noreply.github.com> Date: Sun, 15 Mar 2026 14:35:47 -0400 Subject: [PATCH] CTMC Tweaks - When setting key chest type, check for race mode in the world the key is for (instead of the world it is placed in) - Reduce the amount of duplicated information when setting up CTMC - Always use chain locations instead of flattening a playthrough --- command/WriteLocations.cpp | 51 ++++++++++++++------------------------ command/WriteLocations.hpp | 5 +--- logic/GameItem.cpp | 2 +- logic/GameItem.hpp | 2 +- 4 files changed, 22 insertions(+), 38 deletions(-) diff --git a/command/WriteLocations.cpp b/command/WriteLocations.cpp index dd1eb719..f53d2a98 100644 --- a/command/WriteLocations.cpp +++ b/command/WriteLocations.cpp @@ -141,35 +141,32 @@ ModificationError ModifyChest::writeLocation(const Item& item) { } ModificationError ModifyChest::setCTMCType(ACTR& chest, const Item& item) { - // If any of this item's chain locations are progression, then put the item - // into a spiky chest - if(std::ranges::any_of(item.getChainLocations(), [](auto location){return location->progression;}) && - !gameItemToName(item.getGameItemId()).ends_with("Key")) { - LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(2))) // Metal chests for progress items (excluding keys) - return ModificationError::NONE; - } - - if(gameItemToName(item.getGameItemId()).ends_with("Key")) { - // In race mode, only put the dungeon keys for required dungeons in dark wood chests. - // The other keys go into light wood chests. - - // In some extreme entrance rando situations, traversing through unrequired dungeons may still - // be required, so put small/big keys which appear in the playthrough in dark wood chests also - if(raceMode) { - if(std::any_of(dungeons.begin(), dungeons.end(), [&item](auto& dungeon){return dungeon.second.isRequiredDungeon && (dungeon.second.smallKey == item || dungeon.second.bigKey == item);}) || - (std::any_of(playthroughLocations.begin(), playthroughLocations.end(), [&item](Location* loc){return loc->currentItem.getGameItemId() == item.getGameItemId();}))){ + if(item.isSmallKey() || item.isBigKey()) { + if(item.getWorld()->getSettings().progression_dungeons == ProgressionDungeons::RaceMode) { + // In race mode, only put keys for required dungeons in dark wood chests (the other keys go in light wood chests). + // In some extreme entrance rando situations, traversing through unrequired dungeons may still + // be required, so put small/big keys that unlock progression locations in dark wood chests also + // TODO: put all keys for an unrequired dungeon in dark wood chests if you need to traverse through it, instead of + // only the required ones. The current setup should match the old behavior, but it might make more sense + // if all the keys are in the same type of chest. + if(std::ranges::any_of(item.getWorld()->dungeons, [&item](const auto& dungeon){ return dungeon.second.isRequiredDungeon && (dungeon.second.smallKey == item || dungeon.second.bigKey == item); }) || + std::ranges::any_of(item.getChainLocations(), [](const auto& location){ return location->progression; })){ LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(1))) // Dark wood chest for Small and Big Keys return ModificationError::NONE; } - else { - LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(0))) // Light wood chests for keys for empty dungeons (in race mode) - return ModificationError::NONE; - } + + LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(0))) // Light wood chests for unrequired keys for empty dungeons + return ModificationError::NONE; } LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(1))) // Dark wood chest for Small and Big Keys return ModificationError::NONE; } + else if(std::ranges::any_of(item.getChainLocations(), [](const auto& location){ return location->progression; })) { + // If any of this item's chain locations are progression, then put the item in a spiky chest + LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(2))) // Metal chests for progress items (excluding keys) + return ModificationError::NONE; + } LOG_AND_RETURN_IF_ERR(setParam(chest, 0x00F00000, uint8_t(0))) // Light wood chests for non-progress items and consumables return ModificationError::NONE; @@ -494,17 +491,7 @@ bool writeLocations(WorldPool& worlds) { UPDATE_DIALOG_VALUE(40); UPDATE_DIALOG_LABEL("Saving items..."); - // Flatten the playthrough into a single list - // so that chests can check it for CTMC - std::list playthroughLocations = {}; - for (const auto& sphere : worlds[0].playthroughSpheres) { - for (auto loc : sphere) { - playthroughLocations.push_back(loc); - } - } - - const Settings& settings = worlds[0].getSettings(); - ModifyChest::setCTMC(settings.chest_type_matches_contents, settings.progression_dungeons == ProgressionDungeons::RaceMode, worlds[0].dungeons, playthroughLocations); + ModifyChest::setCTMC(worlds[0].getSettings().chest_type_matches_contents); for (auto& [name, location] : worlds[0].locationTable) { if (const ModificationError err = location->method->writeLocation(location->currentItem); err != ModificationError::NONE) { diff --git a/command/WriteLocations.hpp b/command/WriteLocations.hpp index aa7f907f..8064bca2 100644 --- a/command/WriteLocations.hpp +++ b/command/WriteLocations.hpp @@ -45,9 +45,6 @@ class LocationModification class ModifyChest final : public LocationModification { private: inline static bool isCTMC = false; - inline static bool raceMode = false; - inline static std::map dungeons = {}; - inline static std::list playthroughLocations = {}; fspath filePath; std::vector offsets; @@ -65,7 +62,7 @@ class ModifyChest final : public LocationModification { std::unique_ptr duplicate() const override { return std::make_unique(*this); } ModificationError parseArgs(const YAML::Node& locationObject) override; ModificationError writeLocation(const Item& item) override; - static void setCTMC(const bool& isCTMC_, const bool& raceMode_, const std::map& dungeons_, const std::list& playthroughLocations_) { isCTMC = isCTMC_; raceMode = raceMode_; dungeons = dungeons_; playthroughLocations = playthroughLocations_;} + static void setCTMC(const bool& isCTMC_) { isCTMC = isCTMC_; } }; class ModifyActor final : public LocationModification { diff --git a/logic/GameItem.cpp b/logic/GameItem.cpp index dd4fc95b..5e5bd2b2 100644 --- a/logic/GameItem.cpp +++ b/logic/GameItem.cpp @@ -797,7 +797,7 @@ Item::Item(std::string itemName_, World* world_) : } -World* Item::getWorld() +World* Item::getWorld() const { return world; } diff --git a/logic/GameItem.hpp b/logic/GameItem.hpp index 3e6da90c..71154133 100644 --- a/logic/GameItem.hpp +++ b/logic/GameItem.hpp @@ -420,7 +420,7 @@ class Item Item(GameItem gameItemId_, World* world_); Item(std::string itemName_, World* world_); - World* getWorld(); + World* getWorld() const; int getWorldId() const; void setGameItemId(GameItem newGameItemId); GameItem getGameItemId() const;