From 17aa82a2cd1ca5a478a21373e48ade73064a1db9 Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Mon, 22 Aug 2022 13:23:27 -0500 Subject: [PATCH] Initial work towards shopsanity, not fully working but some of it is --- soh/include/z64save.h | 1 + .../randomizer/3drando/settings.cpp | 1 + .../randomizer/3drando/spoiler_log.cpp | 2 +- .../Enhancements/randomizer/randomizer.cpp | 459 +++++++++++++++++- soh/soh/Enhancements/randomizer/randomizer.h | 3 + .../Enhancements/randomizer/randomizerTypes.h | 11 +- soh/soh/OTRGlobals.cpp | 14 + soh/soh/OTRGlobals.h | 2 + soh/soh/SaveManager.cpp | 12 + soh/src/code/z_sram.c | 6 + .../overlays/actors/ovl_En_GirlA/z_en_girla.c | 87 +++- .../overlays/actors/ovl_En_Ossan/z_en_ossan.c | 12 +- .../ovl_file_choose/z_file_choose.c | 2 + 13 files changed, 601 insertions(+), 11 deletions(-) diff --git a/soh/include/z64save.h b/soh/include/z64save.h index d219fd17f..566f39d99 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -186,6 +186,7 @@ typedef struct { u8 trialsDone[6]; u8 cowsMilked[10]; u8 scrubsPurchased[35]; + u8 shopItemsPurchased[48]; u8 temporaryWeapon; u16 adultTradeItems; } SaveContext; // size = 0x1428 diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index c7e06b812..c8c4d70ed 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -2532,6 +2532,7 @@ namespace Settings { ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]); ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]); Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]); + Shopsanity.SetSelectedIndex(cvarSettings[RSK_SHOPSANITY]); Scrubsanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SCRUBS]); ShuffleCows.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_COWS]); ShuffleKokiriSword.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_KOKIRI_SWORD]); diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 562671fd1..23a8649d8 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -678,7 +678,7 @@ static void WriteAllLocations(int language) { std::string placedItemName = language == 2 ? location->GetPlacedItemName().french : location->GetPlacedItemName().english; // Eventually check for other things here like fake name - if (location->HasScrubsanityPrice() || location->HasShopsanityPrice()) { + if (location->HasScrubsanityPrice() || location->HasShopsanityPrice() || location->GetPrice() > 0) { jsonData["locations"][location->GetName()]["item"] = placedItemName; jsonData["locations"][location->GetName()]["price"] = location->GetPrice();; } else { diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 299d418c8..009088fe1 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -32,6 +32,7 @@ u8 generated; const std::string Randomizer::getItemMessageTableID = "Randomizer"; const std::string Randomizer::hintMessageTableID = "RandomizerHints"; +const std::string Randomizer::shopMessageTableID = "RandomizerShops"; const std::string Randomizer::scrubMessageTableID = "RandomizerScrubs"; Randomizer::Randomizer() { @@ -524,6 +525,7 @@ std::unordered_map SpoilerfileSettingNameToEn { "Open Settings:Random Ganon's Trials", RSK_RANDOM_TRIALS }, { "Open Settings:Trial Count", RSK_TRIAL_COUNT }, { "Shuffle Settings:Shuffle Gerudo Card", RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD }, + { "Shuffle Settings:Shopsanity", RSK_SHOPSANITY }, { "Shuffle Settings:Scrub Shuffle", RSK_SHUFFLE_SCRUBS }, { "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS }, { "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS }, @@ -637,6 +639,93 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { } } +std::vector shopItemRandomizerChecks = { + RC_KF_SHOP_ITEM_1, + RC_KF_SHOP_ITEM_2, + RC_KF_SHOP_ITEM_3, + RC_KF_SHOP_ITEM_4, + RC_KF_SHOP_ITEM_5, + RC_KF_SHOP_ITEM_6, + RC_KF_SHOP_ITEM_7, + RC_KF_SHOP_ITEM_8, + RC_GC_SHOP_ITEM_1, + RC_GC_SHOP_ITEM_2, + RC_GC_SHOP_ITEM_3, + RC_GC_SHOP_ITEM_4, + RC_GC_SHOP_ITEM_5, + RC_GC_SHOP_ITEM_6, + RC_GC_SHOP_ITEM_7, + RC_GC_SHOP_ITEM_8, + RC_ZD_SHOP_ITEM_1, + RC_ZD_SHOP_ITEM_2, + RC_ZD_SHOP_ITEM_3, + RC_ZD_SHOP_ITEM_4, + RC_ZD_SHOP_ITEM_5, + RC_ZD_SHOP_ITEM_6, + RC_ZD_SHOP_ITEM_7, + RC_ZD_SHOP_ITEM_8, + RC_KAK_POTION_SHOP_ITEM_1, + RC_KAK_POTION_SHOP_ITEM_2, + RC_KAK_POTION_SHOP_ITEM_3, + RC_KAK_POTION_SHOP_ITEM_4, + RC_KAK_POTION_SHOP_ITEM_5, + RC_KAK_POTION_SHOP_ITEM_6, + RC_KAK_POTION_SHOP_ITEM_7, + RC_KAK_POTION_SHOP_ITEM_8, + RC_MARKET_POTION_SHOP_ITEM_1, + RC_MARKET_POTION_SHOP_ITEM_2, + RC_MARKET_POTION_SHOP_ITEM_3, + RC_MARKET_POTION_SHOP_ITEM_4, + RC_MARKET_POTION_SHOP_ITEM_5, + RC_MARKET_POTION_SHOP_ITEM_6, + RC_MARKET_POTION_SHOP_ITEM_7, + RC_MARKET_POTION_SHOP_ITEM_8, + RC_MARKET_BOMBCHU_SHOP_ITEM_1, + RC_MARKET_BOMBCHU_SHOP_ITEM_2, + RC_MARKET_BOMBCHU_SHOP_ITEM_3, + RC_MARKET_BOMBCHU_SHOP_ITEM_4, + RC_MARKET_BOMBCHU_SHOP_ITEM_5, + RC_MARKET_BOMBCHU_SHOP_ITEM_6, + RC_MARKET_BOMBCHU_SHOP_ITEM_7, + RC_MARKET_BOMBCHU_SHOP_ITEM_8, +}; + +void Randomizer::LoadShopMessages(const char* spoilerFileName) { + if (strcmp(spoilerFileName, "") != 0) { + ParseHintLocationsFile(spoilerFileName); + } + + CustomMessageManager::Instance->ClearMessageTable(Randomizer::shopMessageTableID); + CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::shopMessageTableID); + + // Make an inverse of std::unordered_map SpoilerfileGetNameToEnum + // so that we can get the name of the item from the RandomizerCheck + // TODO: We should probably just make the GetItemName in util.cpp more robust and use that instead. Currently + // it's not localized, doesn't have rando items, and quest items are separate, so we're doing this for now + std::unordered_map GetEnumToSpoilerfileName; + for (auto& [name, get] : SpoilerfileGetNameToEnum) { + GetEnumToSpoilerfileName[get] = name; + } + + for (int index = 0; index < shopItemRandomizerChecks.size(); ++index) { + RandomizerCheck shopItemCheck = shopItemRandomizerChecks[index]; + std::string shopItemName = GetEnumToSpoilerfileName[this->itemLocations[shopItemCheck]]; + u16 shopItemPrice = scrubPrices[shopItemCheck]; + CustomMessageManager::Instance->CreateMessage( + Randomizer::shopMessageTableID, index, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, + "\x08%r" + shopItemName + ": " + std::to_string(shopItemPrice) + " Rupees&%wSpecial deal! ONE LEFT!&Get it while it lasts!\x0A\x02", + "\x08%r" + shopItemName + ": " + std::to_string(shopItemPrice) + " Rubis&%wOffre spéciale! DERNIER EN STOCK!&Faites vite!\x0A\x02", + "\x08%r" + shopItemName + ": " + std::to_string(shopItemPrice) + " Rupees&%wSpecial deal! ONE LEFT!&Get it while it lasts!\x0A\x02", + }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::shopMessageTableID, index + shopItemRandomizerChecks.size(), { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, + "\x08" + shopItemName + ": " + std::to_string(shopItemPrice) + " Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02", + "\x08" + shopItemName + ": " + std::to_string(shopItemPrice) + " Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w\x09\x02", + "\x08" + shopItemName + ": " + std::to_string(shopItemPrice) + " Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02", + }); + } +} + void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) { if (strcmp(spoilerFileName, "") != 0) { ParseItemLocationsFile(spoilerFileName, silent); @@ -751,6 +840,23 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { numericValueString = it.value(); gSaveContext.randoSettings[index].value = std::stoi(numericValueString); break; + case RSK_SHOPSANITY: + if(it.value() == "Off") { + gSaveContext.randoSettings[index].value = 0; + } else if(it.value() == "0") { + gSaveContext.randoSettings[index].value = 1; + } else if(it.value() == "1") { + gSaveContext.randoSettings[index].value = 2; + } else if(it.value() == "2") { + gSaveContext.randoSettings[index].value = 3; + } else if(it.value() == "3") { + gSaveContext.randoSettings[index].value = 4; + } else if(it.value() == "4") { + gSaveContext.randoSettings[index].value = 5; + } else if(it.value() == "Random") { + gSaveContext.randoSettings[index].value = 6; + } + break; case RSK_SHUFFLE_SCRUBS: if(it.value() == "Off") { gSaveContext.randoSettings[index].value = 0; @@ -1126,6 +1232,9 @@ s16 Randomizer::GetItemFromActor(s16 actorId, s16 actorParams, s16 sceneNum, Get return GetItemFromGet(this->itemLocations[GetCheckFromActor(sceneNum, actorId, actorParams)], ogItemId); } +// TODO: This is being used for shops, but we kinda don't want it to be. We +// need to split this into two methods, one that determines whether or not you can get an item +// and another than determines what you get s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) { switch (randoGet) { case RG_NONE: @@ -1139,15 +1248,19 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) { return !CHECK_OWNED_EQUIP(EQUIP_SWORD, 2) ? GI_SWORD_BGS : GI_RUPEE_BLUE; case RG_DEKU_SHIELD: + case RG_BUY_DEKU_SHIELD: return GI_SHIELD_DEKU; + case RG_BUY_HYLIAN_SHIELD: case RG_HYLIAN_SHIELD: return GI_SHIELD_HYLIAN; case RG_MIRROR_SHIELD: return !CHECK_OWNED_EQUIP(EQUIP_SHIELD, 2) ? GI_SHIELD_MIRROR : GI_RUPEE_BLUE; case RG_GORON_TUNIC: + case RG_BUY_GORON_TUNIC: return GI_TUNIC_GORON; case RG_ZORA_TUNIC: + case RG_BUY_ZORA_TUNIC: return GI_TUNIC_ZORA; case RG_IRON_BOOTS: @@ -1361,6 +1474,7 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) { return GI_RUPEE_BLUE; case RG_RECOVERY_HEART: + case RG_BUY_HEART: return GI_HEART; case RG_GREEN_RUPEE: @@ -1379,21 +1493,52 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) { case RG_HEART_CONTAINER: // todo figure out what GI_HEART_CONTAINER_2 is return GI_HEART_CONTAINER; + + // TODO: Logic around needing a bottle? case RG_MILK: - return GI_MILK; //todo logic around needing a bottle? + return GI_MILK; + case RG_BUY_FISH: + return GI_FISH; + case RG_BUY_BLUE_FIRE: + return GI_BLUE_FIRE; + case RG_BUY_BOTTLE_BUG: + return GI_BUGS; + case RG_BUY_POE: + return GI_POE; + case RG_BUY_FAIRYS_SPIRIT: + return GI_FAIRY; + case RG_RED_POTION_REFILL: + case RG_BUY_RED_POTION_30: + case RG_BUY_RED_POTION_40: + case RG_BUY_RED_POTION_50: + return GI_POTION_RED; + case RG_GREEN_POTION_REFILL: + case RG_BUY_GREEN_POTION: + return GI_POTION_GREEN; + case RG_BLUE_POTION_REFILL: + case RG_BUY_BLUE_POTION: + return GI_POTION_BLUE; case RG_BOMBS_5: + case RG_BUY_BOMBS_525: + case RG_BUY_BOMBS_535: return CUR_UPG_VALUE(UPG_BOMB_BAG) ? GI_BOMBS_5 : GI_RUPEE_BLUE; case RG_BOMBS_10: + case RG_BUY_BOMBS_10: return CUR_UPG_VALUE(UPG_BOMB_BAG) ? GI_BOMBS_10 : GI_RUPEE_BLUE; case RG_BOMBS_20: + case RG_BUY_BOMBS_20: + case RG_BUY_BOMBS_30: // TODO: Is there not a 30 bomb item? return CUR_UPG_VALUE(UPG_BOMB_BAG) ? GI_BOMBS_20 : GI_RUPEE_BLUE; case RG_BOMBCHU_5: + case RG_BUY_BOMBCHU_5: return GI_BOMBCHUS_5; case RG_BOMBCHU_10: + case RG_BUY_BOMBCHU_10: return GI_BOMBCHUS_10; case RG_BOMBCHU_20: + case RG_BUY_BOMBCHU_20: return GI_BOMBCHUS_20; case RG_BOMBCHU_DROP: return GI_BOMBCHUS_5; //todo figure out what we want to do for chu drops @@ -1401,27 +1546,28 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) { case RG_ARROWS_5: return CUR_UPG_VALUE(UPG_QUIVER) ? GI_ARROWS_SMALL : GI_RUPEE_BLUE; case RG_ARROWS_10: + case RG_BUY_ARROWS_10: return CUR_UPG_VALUE(UPG_QUIVER) ? GI_ARROWS_MEDIUM : GI_RUPEE_BLUE; case RG_ARROWS_30: + case RG_BUY_ARROWS_30: + case RG_BUY_ARROWS_50: // TODO: Is there not a 50 arrow item? return CUR_UPG_VALUE(UPG_QUIVER) ? GI_ARROWS_LARGE : GI_RUPEE_BLUE; case RG_DEKU_NUTS_5: + case RG_BUY_DEKU_NUT_5: return GI_NUTS_5; case RG_DEKU_NUTS_10: + case RG_BUY_DEKU_NUT_10: return GI_NUTS_10; case RG_DEKU_SEEDS_30: + case RG_BUY_DEKU_SEEDS_30: return CUR_UPG_VALUE(UPG_BULLET_BAG) ? GI_SEEDS_30 : GI_RUPEE_BLUE; case RG_DEKU_STICK_1: + case RG_BUY_DEKU_STICK_1: return GI_STICKS_1; - // RANDOTODO these won't be used until we implement shopsanity/scrub shuffle - case RG_RED_POTION_REFILL: - case RG_GREEN_POTION_REFILL: - case RG_BLUE_POTION_REFILL: - return GI_NONE; - case RG_TREASURE_GAME_HEART: return GI_HEART_PIECE_WIN; case RG_TREASURE_GAME_GREEN_RUPEE: @@ -1855,6 +2001,290 @@ ScrubIdentity Randomizer::IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respa return scrubIdentity; } +ShopItemIdentity Randomizer::IdentifyShopItem(s32 sceneNum, s32 actorParams) { + struct ShopItemIdentity shopItemIdentity; + + shopItemIdentity.shopItemId = -1; + shopItemIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + shopItemIdentity.getItemId = GI_NONE; + shopItemIdentity.itemPrice = -1; + shopItemIdentity.isShuffled = GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) > 0; + + switch (sceneNum) { + case SCENE_KOKIRI_SHOP: + switch (actorParams) { + case 0x0D: + shopItemIdentity.shopItemId = 0x00; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_1; + shopItemIdentity.getItemId = GI_SHIELD_DEKU; + break; + case 0x00: + shopItemIdentity.shopItemId = 0x01; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_2; + shopItemIdentity.getItemId = GI_NUTS_5_2; + break; + case 0x04: + shopItemIdentity.shopItemId = 0x02; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_3; + shopItemIdentity.getItemId = GI_NUTS_10; + break; + case 0x05: + shopItemIdentity.shopItemId = 0x03; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_4; + shopItemIdentity.getItemId = GI_STICKS_1; + break; + case 0x1D: + shopItemIdentity.shopItemId = 0x04; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_5; + shopItemIdentity.getItemId = GI_SEEDS_30; + break; + case 0x2C: + shopItemIdentity.shopItemId = 0x05; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_6; + shopItemIdentity.getItemId = GI_ARROWS_SMALL; + break; + case 0x01: + shopItemIdentity.shopItemId = 0x06; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_7; + shopItemIdentity.getItemId = GI_ARROWS_MEDIUM; + break; + case 0x10: + shopItemIdentity.shopItemId = 0x07; + shopItemIdentity.randomizerCheck = RC_KF_SHOP_ITEM_8; + shopItemIdentity.getItemId = GI_HEART; + break; + } + break; + case SCENE_GOLON: + switch (actorParams) { + case 0x03: + shopItemIdentity.shopItemId = 0x08; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_1; + shopItemIdentity.getItemId = GI_BOMBS_5; + break; + case 0x06: + shopItemIdentity.shopItemId = 0x09; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_2; + shopItemIdentity.getItemId = GI_BOMBS_10; + break; + case 0x2D: + shopItemIdentity.shopItemId = 0x0A; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_3; + shopItemIdentity.getItemId = GI_BOMBS_20; + break; + case 0x2E: + shopItemIdentity.shopItemId = 0x0B; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_4; + shopItemIdentity.getItemId = GI_BOMBS_30; + break; + case 0x0E: + shopItemIdentity.shopItemId = 0x0C; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_5; + shopItemIdentity.getItemId = GI_TUNIC_GORON; + break; + case 0x10: + shopItemIdentity.shopItemId = 0x0D; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_6; + shopItemIdentity.getItemId = GI_HEART; + break; + case 0x30: + shopItemIdentity.shopItemId = 0x0E; + shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_7; + shopItemIdentity.getItemId = GI_POTION_RED; + break; + // TODO: Not a huge issue because shopsanity won't use this slot, but it has the same actorParam as another + // case 0x10: + // shopItemIdentity.shopItemId = 0x0F; + // shopItemIdentity.randomizerCheck = RC_GC_SHOP_ITEM_8; + // shopItemIdentity.getItemId = GI_HEART; + // break; + } + break; + case SCENE_ZOORA: + switch (actorParams) { + case 0x0F: + shopItemIdentity.shopItemId = 0x10; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_1; + shopItemIdentity.getItemId = GI_TUNIC_ZORA; + break; + case 0x2C: + shopItemIdentity.shopItemId = 0x11; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_2; + shopItemIdentity.getItemId = GI_ARROWS_SMALL; + break; + case 0x10: + shopItemIdentity.shopItemId = 0x12; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_3; + shopItemIdentity.getItemId = GI_HEART; + break; + case 0x01: + shopItemIdentity.shopItemId = 0x13; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_4; + shopItemIdentity.getItemId = GI_ARROWS_MEDIUM; + break; + case 0x00: + shopItemIdentity.shopItemId = 0x14; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_5; + shopItemIdentity.getItemId = GI_NUTS_5_2; + break; + case 0x02: + shopItemIdentity.shopItemId = 0x15; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_6; + shopItemIdentity.getItemId = GI_ARROWS_LARGE; + break; + case 0x07: + shopItemIdentity.shopItemId = 0x16; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_7; + shopItemIdentity.getItemId = GI_FISH; + break; + case 0x31: + shopItemIdentity.shopItemId = 0x17; + shopItemIdentity.randomizerCheck = RC_ZD_SHOP_ITEM_8; + shopItemIdentity.getItemId = GI_POTION_RED; + break; + } + break; + case SCENE_DRAG: + switch (actorParams) { + case 0x09: + shopItemIdentity.shopItemId = 0x18; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_1; + shopItemIdentity.getItemId = GI_POTION_GREEN; + break; + case 0x27: + shopItemIdentity.shopItemId = 0x19; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_2; + shopItemIdentity.getItemId = GI_BLUE_FIRE; + break; + case 0x08: + shopItemIdentity.shopItemId = 0x1A; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_3; + shopItemIdentity.getItemId = GI_POTION_RED; + break; + case 0x2B: + shopItemIdentity.shopItemId = 0x1B; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_4; + shopItemIdentity.getItemId = GI_FAIRY; + break; + case 0x00: + shopItemIdentity.shopItemId = 0x1C; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_5; + shopItemIdentity.getItemId = GI_NUTS_5_2; + break; + case 0x28: + shopItemIdentity.shopItemId = 0x1D; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_6; + shopItemIdentity.getItemId = GI_BUGS; + break; + case 0x2A: + shopItemIdentity.shopItemId = 0x1E; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_7; + shopItemIdentity.getItemId = GI_POE; + break; + case 0x07: + shopItemIdentity.shopItemId = 0x1F; + shopItemIdentity.randomizerCheck = RC_KAK_POTION_SHOP_ITEM_8; + shopItemIdentity.getItemId = GI_FISH; + break; + } + break; + case SCENE_ALLEY_SHOP: + switch (actorParams) { + case 0x09: + shopItemIdentity.shopItemId = 0x20; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_1; + shopItemIdentity.getItemId = GI_POTION_GREEN; + break; + case 0x27: + shopItemIdentity.shopItemId = 0x21; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_2; + shopItemIdentity.getItemId = GI_BLUE_FIRE; + break; + case 0x08: + shopItemIdentity.shopItemId = 0x22; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_3; + shopItemIdentity.getItemId = GI_POTION_RED; + break; + case 0x2B: + shopItemIdentity.shopItemId = 0x23; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_4; + shopItemIdentity.getItemId = GI_FAIRY; + break; + case 0x00: + shopItemIdentity.shopItemId = 0x24; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_5; + shopItemIdentity.getItemId = GI_NUTS_5_2; + break; + case 0x28: + shopItemIdentity.shopItemId = 0x25; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_6; + shopItemIdentity.getItemId = GI_BUGS; + break; + case 0x2A: + shopItemIdentity.shopItemId = 0x26; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_7; + shopItemIdentity.getItemId = GI_POE; + break; + case 0x07: + shopItemIdentity.shopItemId = 0x27; + shopItemIdentity.randomizerCheck = RC_MARKET_POTION_SHOP_ITEM_8; + shopItemIdentity.getItemId = GI_FISH; + break; + } + break; + case SCENE_NIGHT_SHOP: + switch (actorParams) { + case 0x18: + shopItemIdentity.shopItemId = 0x28; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_1; + shopItemIdentity.getItemId = GI_BOMBCHUS_10; + break; + case 0x1C: + shopItemIdentity.shopItemId = 0x29; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_2; + shopItemIdentity.getItemId = GI_BOMBCHUS_10; + break; + case 0x19: + shopItemIdentity.shopItemId = 0x2A; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_3; + shopItemIdentity.getItemId = GI_BOMBCHUS_10; + break; + case 0x15: + shopItemIdentity.shopItemId = 0x2B; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_4; + shopItemIdentity.getItemId = GI_BOMBCHUS_10; + break; + case 0x1A: + shopItemIdentity.shopItemId = 0x2C; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_5; + shopItemIdentity.getItemId = GI_BOMBCHUS_20; + break; + case 0x16: + shopItemIdentity.shopItemId = 0x2D; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_6; + shopItemIdentity.getItemId = GI_BOMBCHUS_20; + break; + case 0x1B: + shopItemIdentity.shopItemId = 0x2E; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_7; + shopItemIdentity.getItemId = GI_BOMBCHUS_20; + break; + case 0x17: + shopItemIdentity.shopItemId = 0x2F; + shopItemIdentity.randomizerCheck = RC_MARKET_BOMBCHU_SHOP_ITEM_8; + shopItemIdentity.getItemId = GI_BOMBCHUS_20; + break; + } + break; + } + + if (scrubPrices.find(shopItemIdentity.randomizerCheck) != scrubPrices.end()) { + shopItemIdentity.itemPrice = scrubPrices[shopItemIdentity.randomizerCheck]; + } + + return shopItemIdentity; +} + u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) { return this->randoSettings[randoSettingKey]; } @@ -2876,6 +3306,7 @@ void GenerateRandomizerImgui() { cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS] = CVar_GetS32("gRandomizeShuffleDungeonReward", 0); cvarSettings[RSK_SHUFFLE_SONGS] = CVar_GetS32("gRandomizeShuffleSongs", 0); cvarSettings[RSK_SHUFFLE_TOKENS] = CVar_GetS32("gRandomizeShuffleTokens", 0); + cvarSettings[RSK_SHOPSANITY] = CVar_GetS32("gRandomizeShopsanity", 0); cvarSettings[RSK_SHUFFLE_SCRUBS] = CVar_GetS32("gRandomizeShuffleScrubs", 0); cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0); cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0); @@ -3393,6 +3824,20 @@ void DrawRandoEditor(bool& open) { "expected to be collected after getting Sun's Song."); PaddedSeparator(); + // Shopsanity + ImGui::Text(Settings::Shopsanity.GetName().c_str()); + InsertHelpHoverText( + "Off - All shop items will be the same as vanilla.\n" + "\n" + "0 - Vanilla shop items will be shuffled among different shops.\n" + "\n" + "1-4 - Vanilla shop items will be shuffled among different shops, and each shop will contain 1-4 non-vanilla shop items.\n" + "\n" + "Random - Vanilla shop items will be shuffled among different shops, and each shop will contain a random number of non-vanilla shop items.\n" + ); + SohImGui::EnhancementCombobox("gRandomizeShopsanity", randoShopsanity, 7, 0); + PaddedSeparator(); + // Shuffle Scrubs ImGui::Text(Settings::Scrubsanity.GetName().c_str()); InsertHelpHoverText( diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index 98c2efa5f..3ca80d43d 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -31,6 +31,7 @@ class Randomizer { static const std::string getItemMessageTableID; static const std::string hintMessageTableID; + static const std::string shopMessageTableID; static const std::string scrubMessageTableID; static Sprite* GetSeedTexture(uint8_t index); @@ -39,6 +40,7 @@ class Randomizer { bool SpoilerFileExists(const char* spoilerFileName); void LoadRandomizerSettings(const char* spoilerFileName); void LoadHintLocations(const char* spoilerFileName); + void LoadShopMessages(const char* spoilerFileName); void LoadItemLocations(const char* spoilerFileName, bool silent); u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey); RandomizerCheck GetCheckFromActor(s16 sceneNum, s16 actorId, s16 actorParams); @@ -47,6 +49,7 @@ class Randomizer { std::string GetGanonText() const; std::string GetGanonHintText() const; ScrubIdentity IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData); + ShopItemIdentity IdentifyShopItem(s32 sceneNum, s32 actorParams); s16 GetRandomizedItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); s16 GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum); static void CreateCustomMessages(); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 79d0db060..31d0a4bc6 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -988,6 +988,7 @@ typedef enum { RSK_SHUFFLE_DUNGEON_REWARDS, RSK_SHUFFLE_SONGS, RSK_SHUFFLE_TOKENS, + RSK_SHOPSANITY, RSK_SHUFFLE_SCRUBS, RSK_SHUFFLE_COWS, RSK_SHUFFLE_WEIRD_EGG, @@ -1024,4 +1025,12 @@ typedef struct ScrubIdentity { GetItemID getItemId; int32_t itemPrice; bool isShuffled; -} ScrubIdentity; \ No newline at end of file +} ScrubIdentity; + +typedef struct ShopItemIdentity { + int32_t shopItemId; + RandomizerCheck randomizerCheck; + GetItemID getItemId; + int32_t itemPrice; + bool isShuffled; +} ShopItemIdentity; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index af727a812..5d5a983f6 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1552,6 +1552,10 @@ extern "C" void Randomizer_LoadHintLocations(const char* spoilerFileName) { OTRGlobals::Instance->gRandomizer->LoadHintLocations(spoilerFileName); } +extern "C" void Randomizer_LoadShopMessages(const char* spoilerFileName) { + OTRGlobals::Instance->gRandomizer->LoadShopMessages(spoilerFileName); +} + extern "C" void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent) { OTRGlobals::Instance->gRandomizer->LoadItemLocations(spoilerFileName, silent); } @@ -1572,10 +1576,18 @@ extern "C" ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, return OTRGlobals::Instance->gRandomizer->IdentifyScrub(sceneNum, actorParams, respawnData); } +extern "C" ShopItemIdentity Randomizer_IdentifyShopItem(s32 sceneNum, s32 actorParams) { + return OTRGlobals::Instance->gRandomizer->IdentifyShopItem(sceneNum, actorParams); +} + extern "C" CustomMessageEntry Randomizer_GetScrubMessage(s16 itemPrice) { return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, itemPrice); } +extern "C" CustomMessageEntry Randomizer_GetShopMessage(s16 shopItemId) { + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::shopMessageTableID, shopItemId); +} + extern "C" CustomMessageEntry Randomizer_GetAltarMessage() { return (LINK_IS_ADULT) ? CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT) @@ -1699,6 +1711,8 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) { } } else if (textId >= 0x9000 && textId <= 0x905F) { messageEntry = Randomizer_GetScrubMessage((textId & ((1 << 8) - 1))); + } else if (textId >= 0x9100 && textId <= 0x9160) { + messageEntry = Randomizer_GetShopMessage((textId & ((1 << 8) - 1))); } } if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) { diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 75f375fa7..0d24e44f7 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -97,7 +97,9 @@ void Randomizer_LoadSettings(const char* spoilerFileName); u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum); ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData); +ShopItemIdentity Randomizer_IdentifyShopItem(s32 sceneNum, s32 actorParams); void Randomizer_LoadHintLocations(const char* spoilerFileName); +void Randomizer_LoadShopMessages(const char* spoilerFileName); void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent); GetItemEntry Randomizer_GetRandomizedItem(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum); GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 4621c5e93..1b951b56e 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -772,6 +772,10 @@ void SaveManager::LoadBaseVersion1() { SaveManager::Instance->LoadArray("scrubsPurchased", ARRAY_COUNT(gSaveContext.scrubsPurchased), [](size_t i) { SaveManager::Instance->LoadData("", gSaveContext.scrubsPurchased[i]); }); + + SaveManager::Instance->LoadArray("shopItemsPurchased", ARRAY_COUNT(gSaveContext.shopItemsPurchased), [](size_t i) { + SaveManager::Instance->LoadData("", gSaveContext.shopItemsPurchased[i]); + }); } void SaveManager::LoadBaseVersion2() { @@ -940,6 +944,10 @@ void SaveManager::LoadBaseVersion2() { SaveManager::Instance->LoadArray("scrubsPurchased", ARRAY_COUNT(gSaveContext.scrubsPurchased), [](size_t i) { SaveManager::Instance->LoadData("", gSaveContext.scrubsPurchased[i]); }); + + SaveManager::Instance->LoadArray("shopItemsPurchased", ARRAY_COUNT(gSaveContext.shopItemsPurchased), [](size_t i) { + SaveManager::Instance->LoadData("", gSaveContext.shopItemsPurchased[i]); + }); } void SaveManager::SaveBase() { @@ -1104,6 +1112,10 @@ void SaveManager::SaveBase() { SaveManager::Instance->SaveArray("scrubsPurchased", ARRAY_COUNT(gSaveContext.scrubsPurchased), [](size_t i) { SaveManager::Instance->SaveData("", gSaveContext.scrubsPurchased[i]); }); + + SaveManager::Instance->SaveArray("shopItemsPurchased", ARRAY_COUNT(gSaveContext.shopItemsPurchased), [](size_t i) { + SaveManager::Instance->SaveData("", gSaveContext.shopItemsPurchased[i]); + }); } void SaveManager::SaveArray(const std::string& name, const size_t size, SaveArrayFunc func) { diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 4b362f070..c6e17c416 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -8,6 +8,7 @@ #define NUM_TRIALS 6 #define NUM_COWS 10 #define NUM_SCRUBS 35 +#define NUM_SHOP_ITEMS 35 /** * Initialize new save. @@ -723,6 +724,11 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) { gSaveContext.scrubsPurchased[i] = 0; } + // Sets all shop items to not purchased when generating a rando save. + for (u8 i = 0; i < NUM_SHOP_ITEMS; i++) { + gSaveContext.shopItemsPurchased[i] = 0; + } + // Set Cutscene flags to skip them gSaveContext.eventChkInf[0xC] |= 0x10; // returned to tot with medallions gSaveContext.eventChkInf[0xC] |= 0x20; //sheik at tot pedestal diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 5a1a36be7..bc827822b 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -388,7 +388,18 @@ void EnGirlA_InitItem(EnGirlA* this, GlobalContext* globalCtx) { return; } - this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, shopItemEntries[params].objID); + if (!gSaveContext.n64ddFlag) { + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, shopItemEntries[params].objID); + } else { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.getItemId); + + if (Object_IsLoaded(&globalCtx->objectCtx, getItemEntry.objectId)) { + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, getItemEntry.objectId); + } else { + this->objBankIndex = Object_Spawn(&globalCtx->objectCtx, getItemEntry.objectId); + } + } if (this->objBankIndex < 0) { Actor_Kill(&this->actor); @@ -715,6 +726,24 @@ s32 EnGirlA_CanBuy_Fairy(GlobalContext* globalCtx, EnGirlA* this) { return CANBUY_RESULT_SUCCESS; } +s32 EnGirlA_CanBuy_Randomizer(GlobalContext* globalCtx, EnGirlA* this) { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.getItemId); + // TOOD: Call some some sort of Randomizer equivalent Item_CheckObtainability method to determine if they can buy + + if (gSaveContext.rupees < shopItemIdentity.itemPrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + + // TOOD: We should put a sold out sign instead of preventing them from buying again + // TODO: Need to allow repeated buys for some items + if (gSaveContext.shopItemsPurchased[shopItemIdentity.shopItemId] == 1) { + return CANBUY_RESULT_CANT_GET_NOW; + } + + return CANBUY_RESULT_SUCCESS; +} + void EnGirlA_ItemGive_Arrows(GlobalContext* globalCtx, EnGirlA* this) { Inventory_ChangeAmmo(ITEM_BOW, this->itemCount); Rupees_ChangeBy(-this->basePrice); @@ -842,6 +871,17 @@ void EnGirlA_ItemGive_BottledItem(GlobalContext* globalCtx, EnGirlA* this) { Rupees_ChangeBy(-this->basePrice); } +void EnGirlA_ItemGive_Randomizer(GlobalContext* globalCtx, EnGirlA* this) { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.getItemId); + + if (getItemEntry.modIndex == MOD_NONE) { + Item_Give(globalCtx, getItemEntry.itemId); + } else if (getItemEntry.modIndex == MOD_RANDOMIZER) { + Randomizer_Item_Give(globalCtx, getItemEntry); + } +} + void EnGirlA_BuyEvent_ShieldDiscount(GlobalContext* globalCtx, EnGirlA* this) { if (this->actor.params == SI_HYLIAN_SHIELD) { if (gSaveContext.infTable[7] & 0x40) { @@ -890,6 +930,12 @@ void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this) Rupees_ChangeBy(-this->basePrice); } +void EnGirlA_BuyEvent_Randomizer(GlobalContext* globalCtx, EnGirlA* this) { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + gSaveContext.shopItemsPurchased[shopItemIdentity.shopItemId] = 1; + Rupees_ChangeBy(-this->basePrice); +} + void EnGirlA_Noop(EnGirlA* this, GlobalContext* globalCtx) { } @@ -937,6 +983,14 @@ void EnGirlA_SetItemDescription(GlobalContext* globalCtx, EnGirlA* this) { } else { this->actor.textId = tmp->itemDescTextId; } + + if (gSaveContext.n64ddFlag) { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + if (shopItemIdentity.shopItemId != -1) { + this->actor.textId = 0x9100 + shopItemIdentity.shopItemId; + } + } + this->isInvisible = false; this->actor.draw = EnGirlA_Draw; } @@ -955,6 +1009,20 @@ void EnGirlA_UpdateStockedItem(GlobalContext* globalCtx, EnGirlA* this) { if (EnGirlA_TryChangeShopItem(this)) { EnGirlA_InitItem(this, globalCtx); itemEntry = &shopItemEntries[this->actor.params]; + + if (gSaveContext.n64ddFlag) { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + if (shopItemIdentity.shopItemId != -1) { + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.getItemId); + itemEntry->objID = getItemEntry.objectId; + itemEntry->giDrawId = getItemEntry.gid; + itemEntry->getItemId = getItemEntry.getItemId; + itemEntry->count = 1; + itemEntry->price = shopItemIdentity.itemPrice; + itemEntry->itemDescTextId = 0x9100 + shopItemIdentity.shopItemId; + } + } + this->actor.textId = itemEntry->itemDescTextId; } else { this->isInvisible = false; @@ -981,6 +1049,23 @@ void EnGirlA_InitializeItemAction(EnGirlA* this, GlobalContext* globalCtx) { s16 params = this->actor.params; ShopItemEntry* itemEntry = &shopItemEntries[params]; + if (gSaveContext.n64ddFlag) { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->actor.params); + if (shopItemIdentity.shopItemId != -1) { + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.getItemId); + itemEntry->objID = getItemEntry.objectId; + itemEntry->giDrawId = getItemEntry.gid; + itemEntry->getItemId = getItemEntry.getItemId; + itemEntry->count = 1; + itemEntry->price = shopItemIdentity.itemPrice; + itemEntry->itemDescTextId = 0x9100 + shopItemIdentity.shopItemId; + itemEntry->itemBuyPromptTextId = 0x9100 + shopItemIdentity.shopItemId + 48; // Magic number, 48 is the number of shop items in the game + itemEntry->canBuyFunc = EnGirlA_CanBuy_Randomizer; + itemEntry->itemGiveFunc = EnGirlA_ItemGive_Randomizer; + itemEntry->buyEventFunc = EnGirlA_BuyEvent_Randomizer; + } + } + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { this->actor.flags &= ~ACTOR_FLAG_4; this->actor.objBankIndex = this->objBankIndex; diff --git a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c index 79a2d7010..82b9a548b 100644 --- a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c +++ b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c @@ -1330,7 +1330,17 @@ void EnOssan_GiveItemWithFanfare(GlobalContext* globalCtx, EnOssan* this) { Player* player = GET_PLAYER(globalCtx); osSyncPrintf("\n" VT_FGCOL(YELLOW) "初めて手にいれた!!" VT_RST "\n\n"); - func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f); + if (!gSaveContext.n64ddFlag) { + func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f); + } else { + ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->shelfSlots[this->cursorIndex]->actor.params); + if (shopItemIdentity.shopItemId != -1) { + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.getItemId); + GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 120.0f, 120.0f); + } else { + func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f); + } + } globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; globalCtx->msgCtx.stateTimer = 4; player->stateFlags2 &= ~0x20000000; diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 2aef4bc64..897dcce4d 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -434,6 +434,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) { Randomizer_LoadSettings(fileLoc); Randomizer_LoadHintLocations(fileLoc); Randomizer_LoadItemLocations(fileLoc, silent); + Randomizer_LoadShopMessages(fileLoc); fileSelectSpoilerFileLoaded = true; } @@ -1742,6 +1743,7 @@ void FileChoose_LoadGame(GameState* thisx) { Randomizer_LoadSettings(""); Randomizer_LoadHintLocations(""); + Randomizer_LoadShopMessages(""); Randomizer_LoadItemLocations("", true); gSaveContext.respawn[0].entranceIndex = -1;