diff --git a/mods/randomizer/generator/data/locations.yaml b/mods/randomizer/generator/data/locations.yaml index a85d5aece6..31494f850a 100644 --- a/mods/randomizer/generator/data/locations.yaml +++ b/mods/randomizer/generator/data/locations.yaml @@ -56,6 +56,7 @@ Metadata: Shop: - Stage: 65 + Room: 1 Item: 0x4B Name Lookup: - Sera Shop Slingshot @@ -1350,6 +1351,7 @@ Metadata: Shop: - Stage: 68 + Room: 3 Item: 0x3E Name Lookup: - Kakariko Village Malo Mart Hawkeye @@ -1366,6 +1368,7 @@ Metadata: Shop: - Stage: 68 + Room: 3 Item: 0x2C Name Lookup: - Kakariko Village Malo Mart Hylian Shield @@ -1382,6 +1385,7 @@ Metadata: Shop: - Stage: 68 + Room: 3 Item: 0x61 Name Lookup: - Kakariko Village Malo Mart Red Potion @@ -1398,6 +1402,7 @@ Metadata: Shop: - Stage: 68 + Room: 3 Item: 0x2B Name Lookup: - Kakariko Village Malo Mart Wooden Shield @@ -1449,6 +1454,7 @@ Metadata: Shop: - Stage: 68 + Room: 1 Item: 0x50 Event Flag: 0x0908 @@ -2464,6 +2470,7 @@ Metadata: Shop: - Stage: 73 + Room: 4 Item: 0x61 Name Lookup: - Castle Town Goron Shop Red Potion @@ -2479,6 +2486,7 @@ Metadata: Shop: - Stage: 73 + Room: 4 Item: 0x66 Name Lookup: - Castle Town Goron Shop Lantern Oil @@ -2494,6 +2502,7 @@ Metadata: Shop: - Stage: 73 + Room: 4 Item: 0x2C Name Lookup: - Castle Town Goron Shop Hylian Shield @@ -2509,6 +2518,7 @@ Metadata: Shop: - Stage: 53 + Room: 0 Item: 0x10 Name Lookup: - Castle Town Goron Shop Arrow Refill @@ -2523,6 +2533,7 @@ Metadata: Shop: - Stage: 73 + Room: 0 Item: 0x30 Name Lookup: - Castle Town Malo Mart Magic Armor diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 3d4ad9800d..150380d375 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -764,7 +764,7 @@ HookAction hookPreShopSeqDecideYes(ModContext*, void* args, void*, void*) { int item_no = 0; if (i_this->mFlow.getEventId(&item_no) == 1 && playerIsInRoomStage(3, "R_SP109")) { - const u16 key = static_cast((getStageID() << 8) | (item_no & 0xFF)); + const u16 key = static_cast((getStageID() << 16) | (dStage_roomControl_c::getStayNo() << 8) | (item_no & 0xFF)); if (randomizer_GetContext().mShopOverrides.contains(key)) { i_this->setSoldOutFlag(); } diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp index b2ea347695..bf3aba3f69 100644 --- a/mods/randomizer/src/randomizer_context.cpp +++ b/mods/randomizer/src/randomizer_context.cpp @@ -156,8 +156,8 @@ std::optional RandomizerContext::WriteToFile() { const std::unordered_map u16GoldenWolfOverrides(this->mGoldenWolfOverrides.begin(), this->mGoldenWolfOverrides.end()); out["mGoldenWolfOverrides"] = u16GoldenWolfOverrides; - const std::unordered_map u16ShopOverrides(this->mShopOverrides.begin(), this->mShopOverrides.end()); - out["mShopOverrides"] = u16ShopOverrides; + const std::unordered_map u32ShopOverrides(this->mShopOverrides.begin(), this->mShopOverrides.end()); + out["mShopOverrides"] = u32ShopOverrides; out["mTwilitInsectOverrides"] = mTwilitInsectOverrides; @@ -348,7 +348,7 @@ std::optional RandomizerContext::LoadFromHash(const std::string& ha // Shop Items for (const auto& shopNode : in["mShopOverrides"]) { - u16 key = shopNode.first.as(); + u32 key = shopNode.first.as(); u8 itemId = shopNode.second.as(); this->mShopOverrides[key] = itemId; } @@ -1252,12 +1252,13 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { } // Shop Items - // Keyed by u16 of the stage and original shop item + // Keyed by u32 of 0xFF0000 (stage index), 0x00FF00 (room), and 0x0000FF (original shop item) if (location->HasCategories("Shop") && world->Setting("Shop Items") == "On") { for (const auto& shopNode : metaData["Shop"]) { u8 stage = shopNode["Stage"].as(); + u8 room = shopNode["Room"].as(); u8 originalItem = shopNode["Item"].as(); - u16 key = (stage << 8) | originalItem; + u32 key = (stage << 16) | (room << 8) | originalItem; randoData.mShopOverrides[key] = location->GetCurrentItem()->GetID(); } } diff --git a/mods/randomizer/src/randomizer_context.hpp b/mods/randomizer/src/randomizer_context.hpp index 639b15334e..1fa6e948b0 100644 --- a/mods/randomizer/src/randomizer_context.hpp +++ b/mods/randomizer/src/randomizer_context.hpp @@ -51,7 +51,7 @@ public: std::unordered_map mBugRewardOverrides{}; std::unordered_map mSkyCharacterOverrides{}; std::unordered_map mGoldenWolfOverrides{}; - std::unordered_map mShopOverrides{}; + std::unordered_map mShopOverrides{}; std::unordered_map mTwilitInsectOverrides{}; // Just used in tracker for now std::unordered_map mItemLocations{}; diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index 87ae073e84..dc45aa39e1 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -70,6 +70,31 @@ std::optional parse_derived(const char* name, std::string_view prefi return DerivedKey{stage_id, static_cast((stage_id << 8) | (n & 0xFF))}; } +std::optional parse_shop_check(const char* name, std::string_view prefix) { + if (std::strncmp(name, prefix.data(), prefix.size()) != 0) { + return std::nullopt; + } + const char* stage_begin = name + prefix.size(); + const char* stage_end = std::strchr(stage_begin, ':'); + if (stage_end == nullptr) { + return std::nullopt; + } + const std::string stage{stage_begin, stage_end}; + const int stage_id = getStageID(stage.c_str()); + if (stage_id < 0) { + return std::nullopt; + } + const char* room_begin = stage_end + 1; + const char* room_end = std::strchr(room_begin, ':'); + if (room_end == nullptr) { + return std::nullopt; + } + const std::string roomStr{room_begin, room_end}; + u8 roomNo = std::atoi(roomStr.c_str()); + const int itemNo = std::atoi(room_end + 1); + return static_cast((stage_id << 16) | (roomNo << 8) | (itemNo & 0xFF)); +} + std::optional parse_flag_check(const char* name, std::string_view prefix) { if (std::strncmp(name, prefix.data(), prefix.size()) != 0) { return std::nullopt; @@ -92,9 +117,9 @@ bool set_resolution( return true; } -template +template bool lookup_override( - const Map& map, u16 key, const ItemCheckInfo* info, ItemCheckResolution* outResult) { + const Map& map, Key key, const ItemCheckInfo* info, ItemCheckResolution* outResult) { const auto it = map.find(key); if (it == map.end()) { return false; @@ -134,8 +159,8 @@ bool resolve_check(ModContext*, const ItemCheckInfo* info, ItemCheckResolution* const u16 key = static_cast((*stageId << 8) | 0x9F); return lookup_override(ctx.mFreestandingItemOverrides, key, info, outResult); } - if (auto key = parse_derived(info->name, ITEM_CHECK_SHOP_PREFIX)) { - return lookup_override(ctx.mShopOverrides, key->key, info, outResult); + if (auto key = parse_shop_check(info->name, ITEM_CHECK_SHOP_PREFIX)) { + return lookup_override(ctx.mShopOverrides, *key, info, outResult); } if (auto key = parse_derived(info->name, ITEM_CHECK_SKY_PREFIX)) { return lookup_override(ctx.mSkyCharacterOverrides, key->key, info, outResult); diff --git a/mods/randomizer/src/tools.cpp b/mods/randomizer/src/tools.cpp index 46bff87f55..0df5ffaa0c 100644 --- a/mods/randomizer/src/tools.cpp +++ b/mods/randomizer/src/tools.cpp @@ -531,15 +531,15 @@ std::string nameLookupOverride(const std::string& locationName) { {"Iza Raging Rapids Minigame", ITEM_CHECK_IZA_REWARD_2}, {"Jovani 20 Poe Soul Reward", ITEM_CHECK_JOVANI_REWARD_1}, {"Jovani 60 Poe Soul Reward", ITEM_CHECK_JOVANI_REWARD_2}, - {"Kakariko Village Malo Mart Hawkeye", "shop:R_SP109:62"}, - {"Kakariko Village Malo Mart Hylian Shield", "shop:R_SP109:44"}, - {"Kakariko Village Malo Mart Red Potion", "shop:R_SP109:97"}, - {"Kakariko Village Malo Mart Wooden Shield", "shop:R_SP109:43"}, - {"Castle Town Malo Mart Magic Armor", "shop:R_SP160:48"}, - {"Castle Town Goron Shop Arrow Refill", "shop:F_SP116:16"}, - {"Castle Town Goron Shop Hylian Shield", "shop:R_SP160:44"}, - {"Castle Town Goron Shop Lantern Oil", "shop:R_SP160:102"}, - {"Castle Town Goron Shop Red Potion", "shop:R_SP160:97"}, + {"Kakariko Village Malo Mart Hawkeye", "shop:R_SP109:3:62"}, + {"Kakariko Village Malo Mart Hylian Shield", "shop:R_SP109:3:44"}, + {"Kakariko Village Malo Mart Red Potion", "shop:R_SP109:3:97"}, + {"Kakariko Village Malo Mart Wooden Shield", "shop:R_SP109:3:43"}, + {"Castle Town Malo Mart Magic Armor", "shop:R_SP160:0:48"}, + {"Castle Town Goron Shop Arrow Refill", "shop:F_SP116:0:16"}, + {"Castle Town Goron Shop Hylian Shield", "shop:R_SP160:4:44"}, + {"Castle Town Goron Shop Lantern Oil", "shop:R_SP160:4:102"}, + {"Castle Town Goron Shop Red Potion", "shop:R_SP160:4:97"}, {"Lakebed Temple Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_LAKEBED}, {"Ordon Cat Rescue", ITEM_CHECK_SERA_REWARD}, {"Ordon Shield", ITEM_CHECK_ORDON_SHIELD}, @@ -549,7 +549,7 @@ std::string nameLookupOverride(const std::string& locationName) { {"Rutelas Blessing", ITEM_CHECK_ZORA_ARMOR}, {"Sacred Grove Pedestal Master Sword", ITEM_CHECK_MASTER_SWORD}, {"Sacred Grove Pedestal Shadow Crystal", ITEM_CHECK_SHADOW_CRYSTAL}, - {"Sera Shop Slingshot", "shop:R_SP01:75"}, + {"Sera Shop Slingshot", "shop:R_SP01:1:75"}, {"Shad Dominion Rod", ITEM_CHECK_SHAD_DOMINION_ROD}, {"Skybook From Impaz", ITEM_CHECK_SKYBOOK}, {"Snowboard Racing Prize", ITEM_CHECK_SNOWBOARD_REWARD}, @@ -662,8 +662,9 @@ int getLocationItem(randomizer::logic::location::Location* location) { } if (auto& shopNode = locationMeta["Shop"]) { u8 stage = shopNode[0]["Stage"].as(); + u8 room = shopNode[0]["Room"].as(); u8 originalItem = shopNode[0]["Item"].as(); - u16 key = (stage << 8) | originalItem; + u32 key = (stage << 16) | (room << 8) | originalItem; return context.mShopOverrides[key]; } if (auto& twilitInsectNode = locationMeta["Twilit Insect"]) { diff --git a/sdk/include/mods/items.h b/sdk/include/mods/items.h index b1f0a183a6..ceee2a0efc 100644 --- a/sdk/include/mods/items.h +++ b/sdk/include/mods/items.h @@ -10,7 +10,7 @@ #define ITEM_CHECK_FREESTANDING_PREFIX "freestanding:" /* : */ #define ITEM_CHECK_GOLDEN_WOLF_PREFIX "golden_wolf:" /* */ #define ITEM_CHECK_POE_PREFIX "poe:" /* : */ -#define ITEM_CHECK_SHOP_PREFIX "shop:" /* : */ +#define ITEM_CHECK_SHOP_PREFIX "shop:" /* :: */ #define ITEM_CHECK_SKY_PREFIX "sky:" /* : */ #define ITEM_CHECK_DUNGEON_REWARD_PREFIX "dungeon_reward:" /* */ diff --git a/src/dusk/mods/item_checks.cpp b/src/dusk/mods/item_checks.cpp index 0d2e5a4982..dc0a66d209 100644 --- a/src/dusk/mods/item_checks.cpp +++ b/src/dusk/mods/item_checks.cpp @@ -158,7 +158,7 @@ std::string poe_check_name(uint8_t bitNo) { } std::string shop_check_name(uint8_t itemNo) { - return fmt::format("shop:{}:{}", current_stage_name(), itemNo); + return fmt::format("shop:{}:{}:{}", current_stage_name(), dStage_roomControl_c::getStayNo(), itemNo); } std::string bug_check_name(uint8_t insectId) {