From 341bc43daf8b327c515c717e87d2a25fcce7266f Mon Sep 17 00:00:00 2001 From: Ralphie Morell Date: Tue, 8 Nov 2022 13:54:51 -0500 Subject: [PATCH] Rando: Shuffle Merchants (#1720) --- .../custom-message/CustomMessageTypes.h | 3 + .../Enhancements/debugger/debugSaveEditor.h | 3 + .../randomizer/3drando/settings.cpp | 1 + .../Enhancements/randomizer/randomizer.cpp | 82 +++++++++++++++++++ .../Enhancements/randomizer/randomizerTypes.h | 1 + .../Enhancements/randomizer/randomizer_inf.h | 3 + soh/soh/OTRGlobals.cpp | 5 ++ soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c | 28 ++++++- soh/src/overlays/actors/ovl_En_Js/z_en_js.c | 8 ++ 9 files changed, 133 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h index 991a44b19..8856d7e5a 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -21,6 +21,9 @@ typedef enum { TEXT_PURPLE_RUPEE = 0xF1, TEXT_HUGE_RUPEE = 0xF2, TEXT_BEAN_SALESMAN = 0x405E, + TEXT_MEDIGORON = 0x304C, + TEXT_CARPET_SALESMAN_1 = 0x6077, + TEXT_CARPET_SALESMAN_2 = 0x6078, TEXT_SCRUB_RANDOM = 0x9000, TEXT_SCRUB_RANDOM_FREE = 0x9001, TEXT_SHOP_ITEM_RANDOM = 0x9100, diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.h b/soh/soh/Enhancements/debugger/debugSaveEditor.h index 8f2d7b454..97b2a5062 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.h +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.h @@ -485,5 +485,8 @@ const std::vector flagTables = { { RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_6, "SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_6" }, { RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_7, "SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_7" }, { RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_8, "SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_8" }, + + { RAND_INF_MERCHANTS_MEDIGORON, "RAND_INF_MERCHANTS_MEDIGORON" }, + { RAND_INF_MERCHANTS_CARPET_SALESMAN, "RAND_INF_MERCHANTS_CARPET_SALESMAN" }, } }, }; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index 4c199b929..78b2e03d5 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -2579,6 +2579,7 @@ namespace Settings { ShuffleAdultTradeQuest.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_ADULT_TRADE]); ShuffleMagicBeans.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_MAGIC_BEANS]); + ShuffleMerchants.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_MERCHANTS]); // the checkbox works because 0 is "Off" and 1 is "Fairy Ocarina" StartingOcarina.SetSelectedIndex(cvarSettings[RSK_STARTING_OCARINA]); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 8fad01f21..f9dfb1c77 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -203,6 +203,7 @@ std::unordered_map SpoilerfileSettingNameToEn { "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS }, { "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE }, { "Shuffle Settings:Shuffle Magic Beans", RSK_SHUFFLE_MAGIC_BEANS }, + { "Shuffle Settings:Shuffle Merchants", RSK_SHUFFLE_MERCHANTS }, { "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD }, { "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD }, { "Start with Fairy Ocarina", RSK_STARTING_OCARINA }, @@ -453,6 +454,61 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) { "%gobjet mystérieux%w pour 60 Rubis?\x1B&%gOui&Non%w", }); + + //Setup for merchant text boxes + //Medigoron + //RANDOTODO: Implement obscure/ambiguous hints + CustomMessageManager::Instance->CreateMessage( + Randomizer::merchantMessageTableID, TEXT_MEDIGORON, + { + TEXTBOX_TYPE_BLACK, + TEXTBOX_POS_BOTTOM, + "How about buying %r&{{item}}%w for %g200 rupees%w?\x1B&%gYes&No%w", + "Wie wäre es mit %r&{{item}}%w für %g200 Rubine?%w\x1B&%gJa!&Nein!%w", + "Veux-tu acheter %r&{{item}}%w pour %g200 rubis?%w\x1B&%gOui&Non&w" + }); + + //Carpet Salesman + //RANDOTODO: Implement obscure/ambiguous hints + std::vector cgBoxTwoText; + if (Randomizer::GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == 2) { //If merchant hints are clear... + cgBoxTwoText = { + "!%w&It's real, I promise!&A lonely man such as myself&wouldn't %rlie%w to you, hmm?^", + "!%w&Ich kann versichern es ist ein&aufrichtiges Angebot!^Ein einsamer Mann wie ich würde dich&doch nicht %ranlügen%w, oder?^", + "!%w&C'est vrai! J'te jure!&Un gars comme moi ne te %rmentirai%w pas&tu ne crois pas?^" + }; + } else { + cgBoxTwoText = { + "!%w&I won't tell you what it is until I see&the money...^", + "!%w&Erst kommt das Geld, dann die Ware...^", + "!%w&Je ne te dirai pas ce que c'est avant&d'être payé rubis sur l'ongle...^" + }; + } + CustomMessageManager::Instance->CreateMessage( + Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1, + { + TEXTBOX_TYPE_BLACK, + TEXTBOX_POS_BOTTOM, + "Welcome!^I am selling stuff, strange and rare, &from all over the world to everybody.&Today's special is...^%r{{item}}" + cgBoxTwoText[0] + + "How about %g200 Rupees?%w\x1B&&%gYes&No%w", + "Sei gegrüßt!^Ich verkaufe allerlei Kuriorisäten.&Stets sonderliche und seltene Ware&aus aller Welt für jedermann.&Das heutige Angebot bleibt...^%r{{item}}" + + cgBoxTwoText[1] + "Wie wäre es mit %g200 Rubinen?%w\x1B&&%gJa!&Nein!%w", + "Bienvenue!^Je vends des trucs étranges et rares,&de partout dans le monde et à tout le&monde! L'objet du jour est...^%r{{item}}" + + cgBoxTwoText[2] + "Alors, marché conclu pour %g200 rubis?%w\x1B&&%gOui&Non%w" + } + ); + + CustomMessageManager::Instance->CreateMessage( + Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2, + { + TEXTBOX_TYPE_BLACK, + TEXTBOX_POS_TOP, + "Finally! Now I can go back to being &an %rarms dealer!%w", + "Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!", + "Squalala! Je vais enfin pouvoir &%rprendre des vacances!%w" + } + ); + // Each shop item has two messages, one for when the cursor is over it, and one for when you select it and are // prompted buy/don't buy CustomMessageManager::Instance->CreateMessage( @@ -654,6 +710,15 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { gSaveContext.randoSettings[index].value = 1; } break; + case RSK_SHUFFLE_MERCHANTS: + if(it.value() == "Off") { + gSaveContext.randoSettings[index].value = 0; + } else if (it.value() == "On (No Hints)") { + gSaveContext.randoSettings[index].value = 1; + } else if (it.value() == "On (With Hints)") { + gSaveContext.randoSettings[index].value = 2; + } + break; // Uses Ammo Drops option for now. "Off" not yet implemented case RSK_ENABLE_BOMBCHU_DROPS: if (it.value() == "On") { @@ -2145,6 +2210,9 @@ std::map rcToRandomizerInf = { { RC_MARKET_BOMBCHU_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_6 }, { RC_MARKET_BOMBCHU_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_7 }, { RC_MARKET_BOMBCHU_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_8 }, + { RC_GC_MEDIGORON, RAND_INF_MERCHANTS_MEDIGORON }, + { RC_WASTELAND_BOMBCHU_SALESMAN, RAND_INF_MERCHANTS_CARPET_SALESMAN }, + }; RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) { @@ -2422,6 +2490,7 @@ void GenerateRandomizerImgui() { cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0); cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0); cvarSettings[RSK_SHUFFLE_MAGIC_BEANS] = CVar_GetS32("gRandomizeShuffleBeans", 0); + cvarSettings[RSK_SHUFFLE_MERCHANTS] = CVar_GetS32("gRandomizeShuffleMerchants", 0); cvarSettings[RSK_ENABLE_BOMBCHU_DROPS] = CVar_GetS32("gRandomizeEnableBombchuDrops", 0); cvarSettings[RSK_BOMBCHUS_IN_LOGIC] = CVar_GetS32("gRandomizeBombchusInLogic", 0); cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0); @@ -3052,6 +3121,19 @@ void DrawRandoEditor(bool& open) { UIWidgets::PaddedSeparator(); + // Shuffle Merchants + ImGui::Text(Settings::ShuffleMerchants.GetName().c_str()); + UIWidgets::InsertHelpHoverText( + "Enabling this adds a Giant's Knife and a pack of Bombchus to the item pool " + "and changes both Medigoron and the Haunted Wasteland Carpet Salesman to sell " + "a random item once at the price of 200 rupees.\n\n" + "On (no hints) - Salesmen will be included but won't tell you what you'll get.\n" + "On (with hints) - Salesmen will be included and you'll know what you're buying." + ); + UIWidgets::EnhancementCombobox("gRandomizeShuffleMerchants", randoShuffleMerchants, 3, 0); + + UIWidgets::PaddedSeparator(); + // Shuffle Frog Song Rupees UIWidgets::EnhancementCheckbox(Settings::ShuffleFrogSongRupees.GetName().c_str(), "gRandomizeShuffleFrogSongRupees"); UIWidgets::InsertHelpHoverText( diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index c516a92d9..4ffa6313c 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -1029,6 +1029,7 @@ typedef enum { RSK_SKULLS_SUNS_SONG, RSK_SHUFFLE_ADULT_TRADE, RSK_SHUFFLE_MAGIC_BEANS, + RSK_SHUFFLE_MERCHANTS, RSK_BLUE_FIRE_ARROWS, RSK_SUNLIGHT_ARROWS, RSK_ENABLE_BOMBCHU_DROPS, diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index 5cc3304b9..e764f372b 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -140,6 +140,9 @@ typedef enum { RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_8, + RAND_INF_MERCHANTS_CARPET_SALESMAN, + RAND_INF_MERCHANTS_MEDIGORON, + // If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16) RAND_INF_MAX, diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 25ebf9847..352a46f7d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1983,6 +1983,11 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::NaviRandoMessageTableID, naviTextId); } else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS) && textId == TEXT_BEAN_SALESMAN) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN); + } else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) && (textId == TEXT_MEDIGORON || + (textId == TEXT_CARPET_SALESMAN_1 && !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN)) || + (textId == TEXT_CARPET_SALESMAN_2 && !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN)))) { + RandomizerInf randoInf = (RandomizerInf)(textId == TEXT_MEDIGORON ? RAND_INF_MERCHANTS_MEDIGORON : RAND_INF_MERCHANTS_CARPET_SALESMAN); + messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(randoInf, textId, Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != 2); } else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) && (textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); diff --git a/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c index b6b8b9acb..8a63a70f2 100644 --- a/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c +++ b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c @@ -97,6 +97,8 @@ s32 func_80A3D7C8(void) { return 1; } else if (gBitFlags[3] & gSaveContext.inventory.equipment) { return 2; + } else if ((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS)) && (gBitFlags[2] & gSaveContext.inventory.equipment)){ + return 1; } else { return 3; } @@ -209,6 +211,11 @@ void func_80A3DC44(EnGm* this, PlayState* play) { return; case 1: gSaveContext.infTable[11] |= 2; + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) && + !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) { + //Resets "Talked to Medigoron" flag in infTable to restore initial conversation state + gSaveContext.infTable[11] &= ~2; + } case 2: this->actionFunc = EnGm_ProcessChoiceIndex; default: @@ -243,8 +250,17 @@ void EnGm_ProcessChoiceIndex(EnGm* this, PlayState* play) { Message_ContinueTextbox(play, 0xC8); this->actionFunc = func_80A3DD7C; } else { + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) && + !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) { + GiveItemEntryFromActor(&this->actor, play, + Randomizer_GetItemFromKnownCheck(RC_GC_MEDIGORON, GI_SWORD_KNIFE), 415.0f, 10.0f); + Flags_SetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON); + gSaveContext.infTable[11] |= 2; + this->actionFunc = func_80A3DF00; + } else { func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f); this->actionFunc = func_80A3DF00; + } } break; case 1: // no @@ -260,7 +276,17 @@ void func_80A3DF00(EnGm* this, PlayState* play) { this->actor.parent = NULL; this->actionFunc = func_80A3DF60; } else { - func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f); + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) && + !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) { + GiveItemEntryFromActor(&this->actor, play, + Randomizer_GetItemFromKnownCheck(RC_GC_MEDIGORON, GI_SWORD_KNIFE), 415.0f, 10.0f); + Flags_SetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON); + gSaveContext.infTable[11] |= 2; + } + else { + func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f); + } + } } diff --git a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c index fd0a277e0..66ae484d4 100644 --- a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c +++ b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c @@ -129,7 +129,15 @@ void func_80A89160(EnJs* this, PlayState* play) { this->actor.parent = NULL; En_Js_SetupAction(this, func_80A8910C); } else { + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) && + !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN)) { + GiveItemEntryFromActor(&this->actor, play, + Randomizer_GetItemFromKnownCheck(RC_WASTELAND_BOMBCHU_SALESMAN, GI_BOMBCHUS_10), 90.0f, 10.0f); + Flags_SetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN); + } else { func_8002F434(&this->actor, play, GI_BOMBCHUS_10, 10000.0f, 50.0f); + } + } }