From 7fbb381db74a0aa2405866b99f7daa31fae431a8 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 27 Aug 2022 02:25:13 -0400 Subject: [PATCH 1/4] Changes Malon's behavior to so she won't leave Hyrule Castle until you've obtained her item. --- soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c index 88c5b0724..5e6b363b5 100644 --- a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c +++ b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c @@ -90,7 +90,16 @@ static void* sEyeTextures[] = { gMalonChildEyeClosedTex, }; +bool Randomizer_ObtainedMalonHCReward() { + return Flags_GetEventChkInf(0x12); +} + u16 EnMa1_GetText(GlobalContext* globalCtx, Actor* thisx) { + // Special case for Malon Hyrule Castle Text. Placing it here at the beginning + // has the added benefit of circumventing mask text if wearing bunny hood. + if (gSaveContext.n64ddFlag && globalCtx->sceneNum == SCENE_SPOT15) { + return Randomizer_ObtainedMalonHCReward() ? 0x2044 : 0x2043; + } u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x17); if (faceReaction != 0) { @@ -195,7 +204,8 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) { !(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) { return 1; } - if ((globalCtx->sceneNum == SCENE_SPOT15) && !(gSaveContext.eventChkInf[1] & 0x10)) { + if ((globalCtx->sceneNum == SCENE_SPOT15) && (!(gSaveContext.eventChkInf[1] & 0x10) || + (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()))) { if (gSaveContext.infTable[8] & 0x800) { return 1; } else { @@ -209,7 +219,7 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) { if (globalCtx->sceneNum != SCENE_SPOT20) { return 0; } - if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10)) { + if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10) && ((gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()) || !gSaveContext.n64ddFlag)) { return 1; } return 0; @@ -290,7 +300,7 @@ void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx) { this->actor.targetMode = 6; this->unk_1E8.unk_00 = 0; - if (!(gSaveContext.eventChkInf[1] & 0x10) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) || + if (!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) || (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F))) { this->actionFunc = func_80AA0D88; EnMa1_ChangeAnim(this, ENMA1_ANIM_2); @@ -322,9 +332,11 @@ void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx) { } } - if ((globalCtx->sceneNum == SCENE_SPOT15) && (gSaveContext.eventChkInf[1] & 0x10)) { - Actor_Kill(&this->actor); - } else if (!(gSaveContext.eventChkInf[1] & 0x10) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) { + if ((globalCtx->sceneNum == SCENE_SPOT15) && (gSaveContext.eventChkInf[1] & 0x10) && (gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward())) { + if (!gSaveContext.n64ddFlag) { + Actor_Kill(&this->actor); + } + } else if ((!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward())) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) { if (this->unk_1E8.unk_00 == 2) { this->actionFunc = func_80AA0EA0; globalCtx->msgCtx.stateTimer = 4; From bf1b327a955faa356a56e7ae02db13bf386628c5 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 27 Aug 2022 02:50:44 -0400 Subject: [PATCH 2/4] Cleanup and Documentation --- soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c index 5e6b363b5..d7dbea1f5 100644 --- a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c +++ b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c @@ -200,10 +200,14 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) { if (!LINK_IS_CHILD) { return 0; } + // Causes Malon to appear in the market if you haven't met her yet. if (((globalCtx->sceneNum == SCENE_MARKET_NIGHT) || (globalCtx->sceneNum == SCENE_MARKET_DAY)) && !(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) { return 1; } + // Causes Malon to appear at Hyrule Castle if you've met her already and either we're vanilla and Talon hasn't + // left Hyrule Castle, or if we're randomized and haven't obtained her check. If we haven't met Malon yet, this + // sets the flag for meeting her. if ((globalCtx->sceneNum == SCENE_SPOT15) && (!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()))) { if (gSaveContext.infTable[8] & 0x800) { @@ -213,13 +217,18 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) { return 0; } } + // Malon asleep in her bed if Talon has left Hyrule Castle and it is nighttime. if ((globalCtx->sceneNum == SCENE_SOUKO) && IS_NIGHT && (gSaveContext.eventChkInf[1] & 0x10)) { return 1; } + // Don't spawn Malon if none of the above are true and we are not in Lon Lon Ranch. if (globalCtx->sceneNum != SCENE_SPOT20) { return 0; } - if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10) && ((gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()) || !gSaveContext.n64ddFlag)) { + // If we've gotten this far, we're in Lon Lon Ranch. Spawn Malon if it is daytime, Talon has left Hyrule Castle, and + // either we are not randomized, or we are and we have received Malon's item at Hyrule Castle. + if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10) && + ((gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()) || !gSaveContext.n64ddFlag)) { return 1; } return 0; @@ -300,10 +309,14 @@ void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx) { this->actor.targetMode = 6; this->unk_1E8.unk_00 = 0; + // If Talon has not left Hyrule Castle, we are randomized and ahve not obtained Malon's HC Check, we are not randomized + // and have Epona's Song, or we are randomized and have obtained Malon's Epona's Song Check. This sets Malon's idle + // singing animation in such a way that she has dialog but does not give items or respond to the Ocarina. if (!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) || (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F))) { this->actionFunc = func_80AA0D88; EnMa1_ChangeAnim(this, ENMA1_ANIM_2); + // If none of the above conditions were true, set Malon up to teach Epona's Song. } else { if (gSaveContext.n64ddFlag) { // Skip straight to "let's sing it together" textbox in the ranch gSaveContext.eventChkInf[1] |= 0x40; @@ -332,10 +345,15 @@ void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx) { } } - if ((globalCtx->sceneNum == SCENE_SPOT15) && (gSaveContext.eventChkInf[1] & 0x10) && (gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward())) { + // If we're at Hyrule Castle, and either Talon has left or we're randomized and have obtained Malon's HC Check + if ((globalCtx->sceneNum == SCENE_SPOT15) && ((!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()))) { + // Only kill Malon's Actor here in vanilla. If we're in rando and speak to her we don't want her to pop + // out of existence immediately. The init function will properly kill her actor on the next scene load. if (!gSaveContext.n64ddFlag) { Actor_Kill(&this->actor); } + // If Talon has not run away, or we're randomized and have not received Malon's HC Check, or we're + // not randomized and have obtained Epona's Song, put Malon in the state to give Link the HC reward. } else if ((!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward())) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) { if (this->unk_1E8.unk_00 == 2) { this->actionFunc = func_80AA0EA0; From 928a39fe9e21f43a2e322a9cdf06bed98c04b6fe Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 27 Aug 2022 09:30:49 -0400 Subject: [PATCH 3/4] More Cleanup --- soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c index d7dbea1f5..eaf4b1419 100644 --- a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c +++ b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c @@ -309,9 +309,11 @@ void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx) { this->actor.targetMode = 6; this->unk_1E8.unk_00 = 0; - // If Talon has not left Hyrule Castle, we are randomized and ahve not obtained Malon's HC Check, we are not randomized - // and have Epona's Song, or we are randomized and have obtained Malon's Epona's Song Check. This sets Malon's idle - // singing animation in such a way that she has dialog but does not give items or respond to the Ocarina. + // To avoid missing a check, we want Malon to have the actionFunc for singing, but not reacting to Ocarina, if any of + // the following are true. + // 1. Talon has not left Hyrule Castle. + // 2. We are Randomized and have not obtained Malon's Weird Egg Check. + // 3. We are not Randomized and have obtained Epona's Song if (!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) || (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F))) { this->actionFunc = func_80AA0D88; @@ -345,15 +347,15 @@ void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx) { } } - // If we're at Hyrule Castle, and either Talon has left or we're randomized and have obtained Malon's HC Check - if ((globalCtx->sceneNum == SCENE_SPOT15) && ((!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()))) { - // Only kill Malon's Actor here in vanilla. If we're in rando and speak to her we don't want her to pop - // out of existence immediately. The init function will properly kill her actor on the next scene load. - if (!gSaveContext.n64ddFlag) { - Actor_Kill(&this->actor); - } - // If Talon has not run away, or we're randomized and have not received Malon's HC Check, or we're - // not randomized and have obtained Epona's Song, put Malon in the state to give Link the HC reward. + // We want to Kill Malon's Actor outside of randomizer when Talon is freed. In Randomizer we don't kill Malon's + // Actor here, otherwise if we wake up Talon first and then get her check she will spontaneously + // disappear. + if ((globalCtx->sceneNum == SCENE_SPOT15) && (!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[1] & 0x10)) { + Actor_Kill(&this->actor); + // We want Malon to give the Weird Egg Check (see function below) in the following situations: + // 1. Talon as not left Hyrule Castle (Vanilla) OR + // 2. We haven't obtained Malon's Weird Egg Check (Randomizer only) OR + // 3. We have Epona's Song? (Vanilla only, not sure why it's here but I didn't write that one) } else if ((!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward())) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) { if (this->unk_1E8.unk_00 == 2) { this->actionFunc = func_80AA0EA0; From 3d8b8066dadd584de71d17ba017ed6989d00041f Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 27 Aug 2022 14:33:38 -0400 Subject: [PATCH 4/4] More comment cleanup --- soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c index eaf4b1419..c2a19e621 100644 --- a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c +++ b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c @@ -205,16 +205,15 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) { !(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) { return 1; } - // Causes Malon to appear at Hyrule Castle if you've met her already and either we're vanilla and Talon hasn't - // left Hyrule Castle, or if we're randomized and haven't obtained her check. If we haven't met Malon yet, this - // sets the flag for meeting her. - if ((globalCtx->sceneNum == SCENE_SPOT15) && (!(gSaveContext.eventChkInf[1] & 0x10) || - (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()))) { - if (gSaveContext.infTable[8] & 0x800) { - return 1; - } else { - gSaveContext.infTable[8] |= 0x800; - return 0; + if ((globalCtx->sceneNum == SCENE_SPOT15) && // if we're at hyrule castle + (!(gSaveContext.eventChkInf[1] & 0x10) || // and talon hasn't left + (gSaveContext.n64ddFlag && + !Randomizer_ObtainedMalonHCReward()))) { // or we're rando'd and haven't gotten malon's HC check + if (gSaveContext.infTable[8] & 0x800) { // if we've met malon + return 1; // make her appear at the castle + } else { // if we haven't met malon + gSaveContext.infTable[8] |= 0x800; // set the flag for meeting malon + return 0; // don't make her appear at the castle } } // Malon asleep in her bed if Talon has left Hyrule Castle and it is nighttime.