diff --git a/code/src/actor.c b/code/src/actor.c index 6f17a872..dd0cfe3d 100644 --- a/code/src/actor.c +++ b/code/src/actor.c @@ -54,6 +54,7 @@ #include "deku_scrubs.h" #include "bean_plant.h" #include "sheik.h" +#include "skulltula_people.h" #define OBJECT_GI_KEY 170 #define OBJECT_GI_BOSSKEY 185 @@ -201,6 +202,8 @@ void Actor_Init() { gActorOverlayTable[0x185].initInfo->update = EnWonderTalk2_rUpdate; + gActorOverlayTable[0x188].initInfo->update = EnSsh_rUpdate; + gActorOverlayTable[0x18A].initInfo->update = OceffWipe_rUpdate; gActorOverlayTable[0x18B].initInfo->update = OceffStorm_rUpdate; diff --git a/code/src/actors/skulltula_people.c b/code/src/actors/skulltula_people.c new file mode 100644 index 00000000..af794db3 --- /dev/null +++ b/code/src/actors/skulltula_people.c @@ -0,0 +1,11 @@ +#include "settings.h" + +#define EnSsh_Update_addr 0x1DF0A4 +#define EnSsh_Update ((ActorFunc)EnSsh_Update_addr) + +void EnSsh_rUpdate(Actor* thisx, GlobalContext* globalCtx) { + EnSsh_Update(thisx, globalCtx); + if (gSettingsContext.skulltulaHints && thisx->textId == 0x22) { + thisx->textId = 0x9400 + thisx->params - 1; + } +} diff --git a/code/src/actors/skulltula_people.h b/code/src/actors/skulltula_people.h new file mode 100644 index 00000000..47a6b3fa --- /dev/null +++ b/code/src/actors/skulltula_people.h @@ -0,0 +1,8 @@ +#ifndef _SKULLTULA_PEOPLE_H_ +#define _SKULLTULA_PEOPLE_H_ + +#include "z3D/z3D.h" + +void EnSsh_rUpdate(Actor* thisx, GlobalContext* globalCtx); + +#endif diff --git a/code/src/settings.h b/code/src/settings.h index 10a4b278..fb96bc3c 100644 --- a/code/src/settings.h +++ b/code/src/settings.h @@ -562,6 +562,8 @@ typedef struct { u8 totAltarHints; u8 ganonHints; u8 sheikHints; + u8 dampeHint; + u8 skulltulaHints; u8 compassesShowReward; u8 compassesShowWotH; u8 mapsShowDungeonMode; diff --git a/source/descriptions.cpp b/source/descriptions.cpp index 1905ed51..d7a9d47d 100644 --- a/source/descriptions.cpp +++ b/source/descriptions.cpp @@ -814,6 +814,11 @@ string_view ganonHintsDesc = "Talking to Ganondorf in his boss room w "\n" // "When trials are on, Sheik will appear to relay\n" // "these hints in Ganon's Castle."; // +string_view dampeHintDesc = "Reading Dampe's diary will reveal the location\n" // + "of a single progressive hookshot."; // +string_view skulltulaHintDesc = "Talking to a cursed House of Skulltula resident\n"// + "will tell you the reward they will give you for\n"// + "removing their curse."; // /*------------------------------ // | MAP AND COMPASS GIVES INFO | // ------------------------------*/ // diff --git a/source/descriptions.hpp b/source/descriptions.hpp index 69b1f3c7..1c97483a 100644 --- a/source/descriptions.hpp +++ b/source/descriptions.hpp @@ -265,6 +265,8 @@ extern string_view bonusGossipHintsDesc; extern string_view miscHintsDesc; extern string_view totAltarHintsDesc; extern string_view ganonHintsDesc; +extern string_view dampeHintDesc; +extern string_view skulltulaHintDesc; extern string_view compassesShowRewardsDesc; extern string_view compassesShowWotHDesc; diff --git a/source/hint_list.cpp b/source/hint_list.cpp index 4239a46d..28a813cb 100644 --- a/source/hint_list.cpp +++ b/source/hint_list.cpp @@ -2295,58 +2295,41 @@ void HintTable_Init() { | MERCHANTS' ITEMS | ---------------------------*/ - hintTable[MEDIGORON_DIALOG_FIRST] = HintText::MerchantsDialogs({ + hintTable[MEDIGORON_DIALOG] = HintText::MerchantsDialogs({ // obscure text - Text{"How about buying ", - /*french*/"Veux-tu acheter ", - /*spanish*/"¿Me compras ", - /*italian*/"Che ne dici? Vuoi comprrrarrre ", - /*german*/"Möchtest du "}, - }); - - hintTable[MEDIGORON_DIALOG_SECOND] = HintText::MerchantsDialogs({ - // obscure text - Text{" for #200 Rupees#?&"+TWO_WAY_CHOICE()+"#Buy&Don't buy#", - /*french*/" pour #200 rubis#?&"+TWO_WAY_CHOICE()+"#Acheter&Ne pas acheter#", - /*spanish*/" por #200 rupias#?&"+TWO_WAY_CHOICE()+"#Comprar&No comprar#", - /*italian*/" per #200 rupie#?&"+TWO_WAY_CHOICE()+"#Sì&No#", - /*german*/" für #200 Rubine# kaufen?&"+TWO_WAY_CHOICE()+"#Klar!&Nie im Leben!#"}, + Text{"How about buying %s for #200 Rupees#?&"+TWO_WAY_CHOICE()+"#Buy&Don't buy#", + /*french*/"Veux-tu acheter %s pour #200 rubis#?&"+TWO_WAY_CHOICE()+"#Acheter&Ne pas acheter#", + /*spanish*/"¿Me compras %s por #200 rupias#?&"+TWO_WAY_CHOICE()+"#Comprar&No comprar#", + /*italian*/"Che ne dici? Vuoi comprrrarrre %s per #200 rupie#?&"+TWO_WAY_CHOICE()+"#Sì&No#", + /*german*/"Möchtest du %s für #200 Rubine# kaufen?&"+TWO_WAY_CHOICE()+"#Klar!&Nie im Leben!#"}, }); hintTable[CARPET_SALESMAN_DIALOG_FIRST] = HintText::MerchantsDialogs({ // obscure text - Text{"Welcome!^I am selling stuff, strange and rare, from&all over the world to everybody. Today's&special is...^", - /*french*/"Bienvenue!^Je vends des objets rares et merveilleux du&monde entier. En spécial aujourd'hui...^", - /*spanish*/"¡Acércate!^Vendo productos extraños y difíciles de&encontrar... De todo el mundo a todo el&mundo. La oferta de hoy es...^¡", - /*italian*/"Benvenuto!^Vendo merce strana e introvabile che&proviene da ogni parte del mondo.&Oggi il pezzo forte è...^", - /*german*/"Willkommen!^Ich verkaufe hier seltsame,&seltene Sachen aus der ganzen Welt.^Mein heutiges Angebot...&"}, + Text{"Welcome!^I am selling stuff, strange and rare, from&all over the world to everybody. Today's&special is...^" + "%s! Terrifying! I won't tell you what it is until I see the money...^How about #200 Rupees#?&&"+TWO_WAY_CHOICE()+"#Buy&Don't buy#", + /*french*/"Bienvenue!^Je vends des objets rares et merveilleux du&monde entier. En spécial aujourd'hui...^" + "%s! Un concentré de puissance! Mais montre tes rubis avant que je te dise ce que c'est...^Disons #200 rubis#?&&"+TWO_WAY_CHOICE()+"#Acheter&Ne pas acheter#", + /*spanish*/"¡Acércate!^Vendo productos extraños y difíciles de&encontrar... De todo el mundo a todo el&mundo. La oferta de hoy es...^" + "¡%s! ¡Terrorífico! No te revelaré su nombre hasta que vea el dinero...^#200 rupias#, ¿qué te parece?&&"+TWO_WAY_CHOICE()+"#Comprar&No comprar#", + /*italian*/"Benvenuto!^Vendo merce strana e introvabile che&proviene da ogni parte del mondo.&Oggi il pezzo forte è...^" + "%s! Non ti dico di che si tratta finché non vedo la grana.^Hai #200 rupie#?&&"+TWO_WAY_CHOICE()+"#Tieni!&Veramente no...#", + /*german*/"Willkommen!^Ich verkaufe hier seltsame,&seltene Sachen aus der ganzen Welt.^Mein heutiges Angebot...&" + "%s!&Furchterregend oder? Ich erzähle Euch mehr, wenn ich Geld sehe...^Wie wär's mit #200 Rubinen#?&&"+TWO_WAY_CHOICE()+"#Aber sicher!&Ich bin weg!#"}, }); hintTable[CARPET_SALESMAN_DIALOG_SECOND] = HintText::MerchantsDialogs({ // obscure text - Text{"! Terrifying! I won't tell you what it is until I see the money...^How about #200 Rupees#?&&"+TWO_WAY_CHOICE()+"#Buy&Don't buy#", - /*french*/"! Un concentré de puissance! Mais montre tes rubis avant que je te dise ce que c'est...^Disons #200 rubis#?&&"+TWO_WAY_CHOICE()+"#Acheter&Ne pas acheter#", - /*spanish*/"! ¡Terrorífico! No te revelaré su nombre hasta que vea el dinero...^#200 rupias#, ¿qué te parece?&&"+TWO_WAY_CHOICE()+"#Comprar&No comprar#", - /*italian*/"! Non ti dico di che si tratta finché non vedo la grana.^Hai #200 rupie#?&&"+TWO_WAY_CHOICE()+"#Tieni!&Veramente no...#", - /*german*/"!&Furchterregend oder? Ich erzähle Euch mehr, wenn ich Geld sehe...^Wie wär's mit #200 Rubinen#?&&"+TWO_WAY_CHOICE()+"#Aber sicher!&Ich bin weg!#"}, - }); - - hintTable[CARPET_SALESMAN_DIALOG_THIRD] = HintText::MerchantsDialogs({ - // obscure text - Text{"Thank you very much!^What I'm selling is... #", - /*french*/"Merci beaucoup!^Cet objet extraordinaire est... #", - /*spanish*/"¡Muchas gracias!^Lo que vendo es... #¡", - /*italian*/"Grazie!^Hai appena comprato... #", - /*german*/"Vielen herzlichen Dank!&#"}, - }); - - hintTable[CARPET_SALESMAN_DIALOG_FOURTH] = HintText::MerchantsDialogs({ - // obscure text - Text{"!#^The mark that will lead you to the #Spirit&Temple# is the #flag on the "+IF_NOT_MQ()+"left"+MQ_ELSE()+"right"+MQ_END()+"# outside the shop. Be seeing you!", - /*french*/"!#^La marque qui te mènera au #Temple de l'esprit# est le #drapeau "+IF_NOT_MQ()+"gauche"+MQ_ELSE()+"droite"+MQ_END()+"# en sortant d'ici. À la prochaine!", - /*spanish*/"!#^La marca que te guiará al #Templo del&Espíritu# es la #bandera que está a la&"+IF_NOT_MQ()+"izquierda"+MQ_ELSE()+"derecha"+MQ_END()+"# al salir de aquí. ¡Nos vemos!", - /*italian*/"!#^La direzione per il #Santuario dello Spirito# è indicata dalla #bandiera sulla "+IF_NOT_MQ()+"sinistra"+MQ_ELSE()+"destra"+MQ_END()+"# del negozio. A presto!", - /*german*/"#! Toll oder?^Das Zeichen, welches Euch zum&#Geistertempel# führt, ist die #Flagge&zur "+IF_NOT_MQ()+"Linken"+MQ_ELSE()+"Rechten"+MQ_END()+"# außerhalb des Ladens.^Schaut mal wieder vorbei!"}, + Text{"Thank you very much!^What I'm selling is... " + "#%s!#^The mark that will lead you to the #Spirit&Temple# is the #flag on the "+IF_NOT_MQ()+"left"+MQ_ELSE()+"right"+MQ_END()+"# outside the shop. Be seeing you!", + /*french*/"Merci beaucoup!^Cet objet extraordinaire est... " + "#%s!#^La marque qui te mènera au #Temple de l'esprit# est le #drapeau "+IF_NOT_MQ()+"gauche"+MQ_ELSE()+"droite"+MQ_END()+"# en sortant d'ici. À la prochaine!", + /*spanish*/"¡Muchas gracias!^Lo que vendo es... " + "¡#%s!#^La marca que te guiará al #Templo del&Espíritu# es la #bandera que está a la&"+IF_NOT_MQ()+"izquierda"+MQ_ELSE()+"derecha"+MQ_END()+"# al salir de aquí. ¡Nos vemos!", + /*italian*/"Grazie!^Hai appena comprato... " + "#%s!#^La direzione per il #Santuario dello Spirito# è indicata dalla #bandiera sulla "+IF_NOT_MQ()+"sinistra"+MQ_ELSE()+"destra"+MQ_END()+"# del negozio. A presto!", + /*german*/"Vielen herzlichen Dank!&" + "#%s#! Toll oder?^Das Zeichen, welches Euch zum&#Geistertempel# führt, ist die #Flagge&zur "+IF_NOT_MQ()+"Linken"+MQ_ELSE()+"Rechten"+MQ_END()+"# außerhalb des Ladens.^Schaut mal wieder vorbei!"}, }); hintTable[GRANNY_DIALOG] = HintText::MerchantsDialogs({ @@ -2357,6 +2340,32 @@ void HintTable_Init() { /*italian*/"! Che ne dici di #100 rupie#?&"+TWO_WAY_CHOICE()+"#Aggiudicato!&Non mi serve...#", /*german*/"! Sagen wir #100 Rubine#!&"+TWO_WAY_CHOICE()+"#Gerne!&Auf keinen Fall!#"}, }); + + /*---------------------------------- + | DAMPÉ DIARY TEXT | + -----------------------------------*/ + + hintTable[DAMPE_DIARY_HINT] = HintText::DampeHint({ + // obscure text + Text{"Whoever reads this, please enter #%s#. I will let you have my stretching, shrinking #keepsake#.^I'm waiting for you.&--Dampé", + /*french*/"Toi, le petit curieux qui lit ce journal, viens dans #%s#. Et peut-être auras-tu droit à mon précieux #trésor#.^Je t'attends...&--Igor", + /*spanish*/"A quien lea estas palabras: Entra en #%s# y mi fantástico #tesoro# será para ti.^Te espero.&- Dampé", + /*italian*/"Chiunque legga questo, visiti #%s#. Gli darò il mio #tesoro# BOING BOING.^Firmato: Danpei", + /*german*/"Wer immer dies liest, der möge #%s# betreten. Ihm gebe ich meinen langen, kurzen #Schatz#^Ich warte! Boris"}, + }); + + /*---------------------------------- + | HOUSE OF SKULLTULA HINTS | + -----------------------------------*/ + + hintTable[HOUSE_OF_SKULLTULA_HINT] = HintText::SkulltulaHints({ + // obscure text + Text{"Yeaaarrgh! I'm cursed!! Please save me by destroying #%d Spiders of the Curse# and I will give you #%s#.", + /*french*/"Beuaaaaark! Je suis maudit! Je t'en supplie, aide-moi en vainquant #%d araignées d'or# et je te donnerai #%s#.", + /*spanish*/"¡Grrrrrr! ¡Estoy maldito! Por favor, sálvame destruyendo #%d Arañas de la Maldición# y yo te daría #%s#.", + /*italian*/"Ahhhhhhhh! Questa maledizione è davvero terribile! Ti prego, salvami distruggendo #%d Aracnule d'oro# e ti darò #%s#.", + /*german*/"Aarrrgh! Ich bin verflucht!^Bitte rette mich, indem du #%d Skulltulas# zerstörst und ich werde dir dafür #%s# geben!"}, + }); } // clang-format on s32 StonesRequiredBySettings() { diff --git a/source/hints.cpp b/source/hints.cpp index 5c97bd1f..8f9a336c 100644 --- a/source/hints.cpp +++ b/source/hints.cpp @@ -600,6 +600,36 @@ static void CreateGanonText() { } } +static void CreateDampeHint() { + auto hookshotLocation = FilterFromPool( + allLocations, [](const LocationKey loc) { return Location(loc)->GetPlacedItemKey() == PROGRESSIVE_HOOKSHOT; }); + + auto hookLocHint = hookshotLocation.empty() + ? Hint(YOUR_POCKET) + : GetHintRegion(Location(hookshotLocation[0])->GetParentRegionKey())->GetHint(); + + auto dampeText = Hint(DAMPE_DIARY_HINT).GetText(); + dampeText.Replace("%s", hookLocHint.GetText()); + CreateMessageFromTextObject(0x5003, 0, 2, 3, AddColorsAndFormat(dampeText, { QM_PINK, QM_RED })); +} + +static void CreateSkulltulaHints() { + // Create a message for each cursed man that gives a reward. + for (int i = 0; i < 5; i++) { + ItemLocation* location = Location(KAK_10_GOLD_SKULLTULA_REWARD + i); + Text rewardItemText = location->GetPlacedItem().GetHint().GetTextCopy(); + rewardItemText.Replace("$", ""); + + Text hintText = Hint(HOUSE_OF_SKULLTULA_HINT).GetTextCopy(); + hintText.Replace("%d", std::to_string((i + 1) * 10)); + hintText.Replace("%s", rewardItemText); + + CreateMessageFromTextObject(0x9400 + i, 0, 2, 3, AddColorsAndFormat(hintText, { QM_RED, QM_GREEN })); + + location->SetAsHinted(); + } +} + // Find the location which has the given itemKey and create the generic altar text for the reward static Text BuildDungeonRewardText(ItemID itemID, const ItemKey itemKey) { std::vector itemKeyLocations = FilterFromPool( @@ -813,6 +843,9 @@ void CreateMiscHints() { if (GanonHints) { CreateGanonText(); } + if (DampeHint) { + CreateDampeHint(); + } if (ToTAltarHints) { CreateAltarText(rewardHints); } @@ -821,6 +854,9 @@ void CreateMiscHints() { if (ShuffleMerchants.IsNot(SHUFFLEMERCHANTS_OFF)) { CreateMerchantsHints(); } + if (SkulltulaHints) { + CreateSkulltulaHints(); + } } void CreateMerchantsHints() { @@ -842,13 +878,13 @@ void CreateMerchantsHints() { Text grannyCapitalItemText = grannyItemText.Capitalize(); - Text medigoronText = - Hint(MEDIGORON_DIALOG_FIRST).GetText() + medigoronItemText + Hint(MEDIGORON_DIALOG_SECOND).GetText(); + Text medigoronText = Hint(MEDIGORON_DIALOG).GetText(); + medigoronText.Replace("%s", medigoronItemText); Text grannyText = grannyCapitalItemText + Hint(GRANNY_DIALOG).GetText(); - Text carpetSalesmanTextOne = Hint(CARPET_SALESMAN_DIALOG_FIRST).GetText() + carpetSalesmanItemText + - Hint(CARPET_SALESMAN_DIALOG_SECOND).GetText(); - Text carpetSalesmanTextTwo = Hint(CARPET_SALESMAN_DIALOG_THIRD).GetText() + carpetSalesmanItemClearText + - Hint(CARPET_SALESMAN_DIALOG_FOURTH).GetText(); + Text carpetSalesmanTextOne = Hint(CARPET_SALESMAN_DIALOG_FIRST).GetText(); + carpetSalesmanTextOne.Replace("%s", carpetSalesmanItemText); + Text carpetSalesmanTextTwo = Hint(CARPET_SALESMAN_DIALOG_SECOND).GetText(); + carpetSalesmanTextTwo.Replace("%s", carpetSalesmanItemClearText); CreateMessageFromTextObject(0x9120, 0, 2, 3, AddColorsAndFormat(medigoronText, { QM_RED, QM_GREEN })); CreateMessageFromTextObject(0x9121, 0, 2, 3, AddColorsAndFormat(grannyText, { QM_RED, QM_GREEN })); diff --git a/source/hints.hpp b/source/hints.hpp index df850be8..96914346 100644 --- a/source/hints.hpp +++ b/source/hints.hpp @@ -65,7 +65,9 @@ enum class HintCategory { MasterSword, GanonLine, SheikLine, + DampeHint, MerchantsDialogs, + SkulltulaHints, }; class HintText { @@ -156,6 +158,12 @@ class HintText { HintCategory::MasterSword }; } + static auto DampeHint(std::vector&& obscureText, std::vector&& ambiguousText = {}, + Text&& clearText = {}) { + return HintText{ std::move(obscureText), std::move(ambiguousText), std::move(clearText), + HintCategory::DampeHint }; + } + static auto GanonLine(std::vector&& obscureText, std::vector&& ambiguousText = {}, Text&& clearText = {}) { return HintText{ std::move(obscureText), std::move(ambiguousText), std::move(clearText), @@ -174,6 +182,12 @@ class HintText { HintCategory::MerchantsDialogs }; } + static auto SkulltulaHints(std::vector&& obscureText, std::vector&& ambiguousText = {}, + Text&& clearText = {}) { + return HintText{ std::move(obscureText), std::move(ambiguousText), std::move(clearText), + HintCategory::SkulltulaHints }; + } + Text& GetObscure() { return RandomElement(obscureText); } diff --git a/source/keys.hpp b/source/keys.hpp index f0ec83de..d8752443 100644 --- a/source/keys.hpp +++ b/source/keys.hpp @@ -1727,13 +1727,14 @@ typedef enum { SHEIK_LINE02, SHEIK_LINE03, - MEDIGORON_DIALOG_FIRST, - MEDIGORON_DIALOG_SECOND, + MEDIGORON_DIALOG, CARPET_SALESMAN_DIALOG_FIRST, CARPET_SALESMAN_DIALOG_SECOND, - CARPET_SALESMAN_DIALOG_THIRD, - CARPET_SALESMAN_DIALOG_FOURTH, GRANNY_DIALOG, + DAMPE_DIARY_HINT, + + HOUSE_OF_SKULLTULA_HINT, + KEY_ENUM_MAX, } Key; diff --git a/source/settings.cpp b/source/settings.cpp index 2c62b2e2..0d49587a 100644 --- a/source/settings.cpp +++ b/source/settings.cpp @@ -324,6 +324,8 @@ Option BonusGossipHints = Option::Bool(4, "Bonus Hints", {"Off", "On" Option MiscHints = Option::U8 ("Miscellaneous Hints", {"All Disabled", "All Enabled", "Choose"}, {miscHintsDesc}, OptionCategory::Setting, TOGGLE_ALL_ENABLED); Option ToTAltarHints = Option::Bool(2, "Temple of Time Altar",{"Off", "On"}, {totAltarHintsDesc}); Option GanonHints = Option::Bool(2, "Ganondorf", {"Off", "On"}, {ganonHintsDesc}); +Option DampeHint = Option::Bool(2, "Dampe's Diary", {"Off", "On"}, {dampeHintDesc}); +Option SkulltulaHints = Option::Bool(2, "House of Skulltula", {"Off", "On"}, {skulltulaHintDesc}); Option ClearerHints = Option::U8 ("Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, {obscureHintsDesc, ambiguousHintsDesc, clearHintsDesc}); Option CompassesShowReward = Option::U8 ("Compasses Show Rewards", {"No", "Yes"}, {compassesShowRewardsDesc}); Option CompassesShowWotH = Option::U8 ("Compasses Show WotH", {"No", "Yes"}, {compassesShowWotHDesc}, OptionCategory::Setting, ON); @@ -343,6 +345,8 @@ std::vector