diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index 4659a3126d..4fdee90990 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -223,6 +223,7 @@ typedef enum { VB_OPEN_WOODFALL_FROM_SONG, VB_OPEN_GREAT_BAY_FROM_SONG, VB_OPEN_SNOWHEAD_FROM_SONG, + VB_GOHT_UNFREEZE, } GIVanillaBehavior; typedef enum { diff --git a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp index 30ea415717..4616284a1e 100644 --- a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp +++ b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp @@ -101,6 +101,7 @@ void Rando::ActorBehavior::OnFileLoad() { Rando::ActorBehavior::InitObjTaruBehavior(); Rando::ActorBehavior::InitObjTsuboBehavior(); Rando::ActorBehavior::InitObjWarpstoneBehavior(); + Rando::ActorBehavior::InitSoulsBehavior(); COND_HOOK(ShouldVanillaBehavior, IS_RANDO, MiscVanillaBehaviorHandler); } diff --git a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h index c333680954..93136fa869 100644 --- a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h +++ b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h @@ -79,6 +79,7 @@ void InitObjMoonStoneBehavior(); void InitObjTaruBehavior(); void InitObjTsuboBehavior(); void InitObjWarpstoneBehavior(); +void InitSoulsBehavior(); } // namespace ActorBehavior diff --git a/mm/2s2h/Rando/ActorBehavior/Souls.cpp b/mm/2s2h/Rando/ActorBehavior/Souls.cpp new file mode 100644 index 0000000000..5252517a7c --- /dev/null +++ b/mm/2s2h/Rando/ActorBehavior/Souls.cpp @@ -0,0 +1,72 @@ +#include "ActorBehavior.h" +#include + +extern "C" { +#include "variables.h" +#include "functions.h" + +#include "overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.h" + +void func_80B0CF24(BossHakugin*, PlayState*); +} + +void ShouldActorUpdate(Actor* actor, bool* should, RandoInf randoInf) { + if (!Flags_GetRandoInf(randoInf)) { + *should = false; + actor->flags &= ~ACTOR_FLAG_TARGETABLE; + } else if (!actor->flags & ACTOR_FLAG_TARGETABLE) { + actor->flags |= ACTOR_FLAG_TARGETABLE; + } +} + +void ShouldActorDraw(Actor* actor, bool* should, RandoInf randoInf) { + if (!Flags_GetRandoInf(randoInf)) { + *should = false; + } +} + +void Rando::ActorBehavior::InitSoulsBehavior() { + bool shouldRegister = IS_RANDO && RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_YES; + + COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_HAKUGIN, shouldRegister, [](Actor* actor, bool* should) { + if (!Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GOHT)) { + func_80B0CF24((BossHakugin*)actor, gPlayState); + *should = false; + } + }); + + COND_VB_SHOULD(VB_GOHT_UNFREEZE, shouldRegister, { + if (!Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GOHT)) { + *should = false; + } + }); + + COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_03, shouldRegister, + [](Actor* actor, bool* should) { ShouldActorDraw(actor, should, RANDO_INF_OBTAINED_SOUL_OF_GYORG); }); + + COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_07, shouldRegister, + [](Actor* actor, bool* should) { ShouldActorDraw(actor, should, RANDO_INF_OBTAINED_SOUL_OF_MAJORA); }); + + COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_01, shouldRegister, + [](Actor* actor, bool* should) { ShouldActorDraw(actor, should, RANDO_INF_OBTAINED_SOUL_OF_ODOLWA); }); + + COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_02, shouldRegister, [](Actor* actor, bool* should) { + ShouldActorDraw(actor, should, RANDO_INF_OBTAINED_SOUL_OF_TWINMOLD); + }); + + COND_ID_HOOK(ShouldActorUpdate, ACTOR_BOSS_03, shouldRegister, [](Actor* actor, bool* should) { + ShouldActorUpdate(actor, should, RANDO_INF_OBTAINED_SOUL_OF_GYORG); + }); + + COND_ID_HOOK(ShouldActorUpdate, ACTOR_BOSS_07, shouldRegister, [](Actor* actor, bool* should) { + ShouldActorUpdate(actor, should, RANDO_INF_OBTAINED_SOUL_OF_MAJORA); + }); + + COND_ID_HOOK(ShouldActorUpdate, ACTOR_BOSS_01, shouldRegister, [](Actor* actor, bool* should) { + ShouldActorUpdate(actor, should, RANDO_INF_OBTAINED_SOUL_OF_ODOLWA); + }); + + COND_ID_HOOK(ShouldActorUpdate, ACTOR_BOSS_02, shouldRegister, [](Actor* actor, bool* should) { + ShouldActorUpdate(actor, should, RANDO_INF_OBTAINED_SOUL_OF_TWINMOLD); + }); +} diff --git a/mm/2s2h/Rando/ConvertItem.cpp b/mm/2s2h/Rando/ConvertItem.cpp index 696334500b..477b6d3481 100644 --- a/mm/2s2h/Rando/ConvertItem.cpp +++ b/mm/2s2h/Rando/ConvertItem.cpp @@ -415,6 +415,12 @@ bool Rando::IsItemObtainable(RandoItemId randoItemId, RandoCheckId randoCheckId) return !CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD); case RI_TINGLE_MAP_STONE_TOWER: return !CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER); + case RI_SOUL_GOHT: + case RI_SOUL_GYORG: + case RI_SOUL_MAJORA: + case RI_SOUL_ODOLWA: + case RI_SOUL_TWINMOLD: + return !Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GOHT + (randoItemId - RI_SOUL_GOHT)); // These items are technically fine to receive again because they don't do anything, but we'll convert them to // ensure it's clear to the player something didn't go wrong. We just simply check the inventory state // Masks diff --git a/mm/2s2h/Rando/DrawFuncs.cpp b/mm/2s2h/Rando/DrawFuncs.cpp new file mode 100644 index 0000000000..7117e703fd --- /dev/null +++ b/mm/2s2h/Rando/DrawFuncs.cpp @@ -0,0 +1,197 @@ +#include "DrawFuncs.h" +#include +#include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" +#include "BenPort.h" + +extern "C" { +#include +#include "objects/gameplay_keep/gameplay_keep.h" + +// clang-format off +// Boss Includes +/* Goht */ #include "objects/object_boss_hakugin/object_boss_hakugin.h" +/* Gyorg */ #include "objects/object_boss03/object_boss03.h" +/* Odolwa */ #include "objects/object_boss01/object_boss01.h" +/* Twinmold */ #include "objects/object_boss02/object_boss02.h" +/* Majora */ #include "objects/object_boss07/object_boss07.h" +// clang-format on + +// Soul Effects +#include "src/overlays/actors/ovl_Obj_Moon_Stone/z_obj_moon_stone.h" +#include "assets/objects/object_gi_reserve00/object_gi_reserve00.h" +} + +// Soul Effects +void DrawEnLight(Color_RGB8 flameColor, Vec3f flameSize) { + Gfx* sp68; + static s8 unk_144 = (s8)(Rand_ZeroOne() * 255.0f); + static u32 lastUpdate = 0; + + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL25_Xlu(gPlayState->state.gfxCtx); + Matrix_ReplaceRotation(&gPlayState->billboardMtxF); + + gSPSegment(POLY_XLU_DISP++, 0x08, + (uintptr_t)Gfx_TwoTexScroll(gPlayState->state.gfxCtx, 0, 0, 0, 0x10, 0x20, 1, (unk_144 * 2) & 0x3F, + (unk_144 * -6) & 0x7F, 0x10, 0x20)); + sp68 = (Gfx*)gameplay_keep_DL_01ACF0; + gDPSetPrimColor(POLY_XLU_DISP++, 0xC0, 0xC0, flameColor.r, flameColor.g, flameColor.b, 0); + gDPSetEnvColor(POLY_XLU_DISP++, flameColor.r, flameColor.g, flameColor.b, 0); + Matrix_Scale(flameSize.x, flameSize.y, flameSize.z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gPlayState->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sp68); + + CLOSE_DISPS(gPlayState->state.gfxCtx); + + if (gPlayState != NULL && lastUpdate != gPlayState->state.frames) { + lastUpdate = gPlayState->state.frames; + unk_144++; + } +} + +// Boss Souls +extern void DrawGoht() { + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL25_Opa(gPlayState->state.gfxCtx); + Matrix_Translate(0.0f, -20.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(0.005f, 0.005f, 0.005f, MTXMODE_APPLY); + + static bool initialized = false; + static SkelAnime skelAnime; + static Vec3s jointTable[33]; + static Vec3s otherTable[33]; + static u32 lastUpdate = 0; + if (!initialized) { + initialized = true; + SkelAnime_InitFlex(gPlayState, &skelAnime, (FlexSkeletonHeader*)&gGohtSkel, (AnimationHeader*)&gGohtRunAnim, + jointTable, otherTable, 33); + } + if (gPlayState != NULL && lastUpdate != gPlayState->state.frames) { + lastUpdate = gPlayState->state.frames; + SkelAnime_Update(&skelAnime); + } + gSPSegment(POLY_OPA_DISP++, 0x08, (uintptr_t)gGohtMetalPlateWithCirclePatternTex); + SkelAnime_DrawFlexOpa(gPlayState, skelAnime.skeleton, skelAnime.jointTable, skelAnime.dListCount, NULL, NULL, NULL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); + DrawEnLight({ 10, 138, 46 }, { 30.0f, 30.0f, 30.0f }); +} + +extern void DrawGyorg() { + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL25_Opa(gPlayState->state.gfxCtx); + Matrix_Translate(0.0f, -20.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(0.05f, 0.05f, 0.05f, MTXMODE_APPLY); + + static bool initialized = false; + static SkelAnime skelAnime; + static Vec3s jointTable[15]; + static Vec3s otherTable[15]; + static u32 lastUpdate = 0; + if (!initialized) { + initialized = true; + SkelAnime_InitFlex(gPlayState, &skelAnime, (FlexSkeletonHeader*)&gGyorgSkel, + (AnimationHeader*)&gGyorgGentleSwimmingAnim, jointTable, otherTable, 15); + } + if (gPlayState != NULL && lastUpdate != gPlayState->state.frames) { + lastUpdate = gPlayState->state.frames; + SkelAnime_Update(&skelAnime); + } + SkelAnime_DrawFlexOpa(gPlayState, skelAnime.skeleton, skelAnime.jointTable, skelAnime.dListCount, NULL, NULL, NULL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); + DrawEnLight({ 19, 99, 165 }, { 3.0f, 3.0f, 3.0f }); +} + +extern void DrawOdolwa() { + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL25_Opa(gPlayState->state.gfxCtx); + Gfx_SetupDL25_Xlu(gPlayState->state.gfxCtx); + Matrix_Translate(0.0f, -20.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(0.005f, 0.005f, 0.005f, MTXMODE_APPLY); + + static bool initialized = false; + static SkelAnime skelAnime; + static Vec3s jointTable[52]; + static Vec3s otherTable[52]; + static u32 lastUpdate = 0; + if (!initialized) { + initialized = true; + SkelAnime_InitFlex(gPlayState, &skelAnime, (FlexSkeletonHeader*)&gOdolwaSkel, + (AnimationHeader*)&gOdolwaReadyAnim, jointTable, otherTable, 52); + } + if (gPlayState != NULL && lastUpdate != gPlayState->state.frames) { + lastUpdate = gPlayState->state.frames; + SkelAnime_Update(&skelAnime); + } + SkelAnime_DrawFlexOpa(gPlayState, skelAnime.skeleton, skelAnime.jointTable, skelAnime.dListCount, NULL, NULL, NULL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); + DrawEnLight({ 145, 20, 133 }, { 25.0f, 25.0f, 25.0f }); +} + +extern void DrawTwinmold() { + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL25_Opa(gPlayState->state.gfxCtx); + Matrix_Scale(0.06f, 0.06f, 0.06f, MTXMODE_APPLY); + + static bool initialized = false; + static SkelAnime skelAnime; + static Vec3s jointTable[13]; + static Vec3s otherTable[13]; + static u32 lastUpdate = 0; + if (!initialized) { + initialized = true; + SkelAnime_InitFlex(gPlayState, &skelAnime, (FlexSkeletonHeader*)&gTwinmoldHeadSkel, + (AnimationHeader*)&gTwinmoldHeadFlyAnim, jointTable, otherTable, 13); + } + if (gPlayState != NULL && lastUpdate != gPlayState->state.frames) { + lastUpdate = gPlayState->state.frames; + SkelAnime_Update(&skelAnime); + } + + Mtx* mtxHead = (Mtx*)GRAPH_ALLOC(gPlayState->state.gfxCtx, 23 * sizeof(Mtx)); + gSPSegment(POLY_OPA_DISP++, 0x0D, (uintptr_t)mtxHead); + gSPSegment(POLY_OPA_DISP++, 0x08, (uintptr_t)gTwinmoldBlueSkinTex); + SkelAnime_DrawOpa(gPlayState, skelAnime.skeleton, skelAnime.jointTable, NULL, NULL, NULL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); + DrawEnLight({ 168, 180, 20 }, { 3.0f, 3.0f, 3.0f }); +} + +extern void DrawMajora() { + static bool initialized = false; + static SkelAnime skelAnime; + static Vec3s jointTable[MAJORAS_MASK_LIMB_MAX]; + static Vec3s morphTable[MAJORAS_MASK_LIMB_MAX]; + static u32 lastUpdate = 0; + + OPEN_DISPS(gPlayState->state.gfxCtx); + Gfx_SetupDL25_Opa(gPlayState->state.gfxCtx); + Gfx_SetupDL25_Xlu(gPlayState->state.gfxCtx); + Matrix_Scale(0.05f, 0.05f, 0.05f, MTXMODE_APPLY); + Matrix_Translate(0, 0, 0, MTXMODE_APPLY); + + if (!initialized) { + initialized = true; + SkelAnime_Init(gPlayState, &skelAnime, (SkeletonHeader*)&gMajorasMaskSkel, + (AnimationHeader*)&gMajorasMaskFloatingAnim, jointTable, morphTable, MAJORAS_MASK_LIMB_MAX); + } + if (gPlayState != NULL && lastUpdate != gPlayState->state.frames) { + lastUpdate = gPlayState->state.frames; + SkelAnime_Update(&skelAnime); + } + + gSPSegment(POLY_OPA_DISP++, 8, (uintptr_t)gMajorasMaskWithNormalEyesTex); + SkelAnime_DrawOpa(gPlayState, skelAnime.skeleton, skelAnime.jointTable, NULL, NULL, NULL); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gMajorasMaskTentacleMaterialDL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); + DrawEnLight({ 232, 128, 21 }, { 3.0f, 3.0f, 3.0f }); +} diff --git a/mm/2s2h/Rando/DrawFuncs.h b/mm/2s2h/Rando/DrawFuncs.h new file mode 100644 index 0000000000..5be1d12cf0 --- /dev/null +++ b/mm/2s2h/Rando/DrawFuncs.h @@ -0,0 +1,11 @@ +#ifndef RANDO_DRAW_FUNCS_H +#define RANDO_DRAW_FUNCS_H + +// Boss Functions +void DrawGoht(); +void DrawGyorg(); +void DrawMajora(); +void DrawOdolwa(); +void DrawTwinmold(); + +#endif diff --git a/mm/2s2h/Rando/DrawItem.cpp b/mm/2s2h/Rando/DrawItem.cpp index 540aa11de6..0cc7c04b66 100644 --- a/mm/2s2h/Rando/DrawItem.cpp +++ b/mm/2s2h/Rando/DrawItem.cpp @@ -2,6 +2,7 @@ #include #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h/ShipInit.hpp" +#include "2s2h/Rando/DrawFuncs.h" extern "C" { #include "variables.h" @@ -400,6 +401,21 @@ void Rando::DrawItem(RandoItemId randoItemId, Actor* actor) { case RI_PROGRESSIVE_WALLET: Rando::DrawItem(Rando::ConvertItem(randoItemId), actor); break; + case RI_SOUL_GOHT: + DrawGoht(); + break; + case RI_SOUL_GYORG: + DrawGyorg(); + break; + case RI_SOUL_MAJORA: + DrawMajora(); + break; + case RI_SOUL_ODOLWA: + DrawOdolwa(); + break; + case RI_SOUL_TWINMOLD: + DrawTwinmold(); + break; case RI_NONE: case RI_UNKNOWN: break; diff --git a/mm/2s2h/Rando/GiveItem.cpp b/mm/2s2h/Rando/GiveItem.cpp index f52249f1b3..ccdf923e6c 100644 --- a/mm/2s2h/Rando/GiveItem.cpp +++ b/mm/2s2h/Rando/GiveItem.cpp @@ -251,6 +251,13 @@ void Rando::GiveItem(RandoItemId randoItemId) { // ITEM_POTION_RED will put a Red Potion bottle on the first bottle slot Item_Give(gPlayState, ITEM_LONGSHOT); break; + case RI_SOUL_GOHT: + case RI_SOUL_GYORG: + case RI_SOUL_MAJORA: + case RI_SOUL_ODOLWA: + case RI_SOUL_TWINMOLD: + Flags_SetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GOHT + (randoItemId - RI_SOUL_GOHT)); + break; case RI_JUNK: case RI_NONE: break; diff --git a/mm/2s2h/Rando/Logic/Logic.h b/mm/2s2h/Rando/Logic/Logic.h index 0d7a6e6fd5..981efb6248 100644 --- a/mm/2s2h/Rando/Logic/Logic.h +++ b/mm/2s2h/Rando/Logic/Logic.h @@ -154,15 +154,22 @@ inline bool CanKillEnemy(ActorId EnemyId) { switch (EnemyId) { case ACTOR_BOSS_01: // Odolwa return (CAN_USE_SWORD || CAN_BE_GORON || CAN_BE_ZORA || CAN_USE_EXPLOSIVE || CAN_USE_MAGIC_ARROW(FIRE) || - CAN_USE_MAGIC_ARROW(LIGHT)); + CAN_USE_MAGIC_ARROW(LIGHT)) && + (Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_ODOLWA) || + RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_NO); case ACTOR_BOSS_02: // Twinmold - return (HAS_ITEM(ITEM_BOW) || (HAS_ITEM(ITEM_MASK_GIANT) && HAS_MAGIC && CAN_USE_HUMAN_SWORD)); + return (HAS_ITEM(ITEM_BOW) || (HAS_ITEM(ITEM_MASK_GIANT) && HAS_MAGIC && CAN_USE_HUMAN_SWORD)) && + (Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_TWINMOLD) || + RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_NO); case ACTOR_BOSS_03: // Gyorg - return ((CAN_BE_DEITY && HAS_MAGIC) || (CAN_BE_ZORA && HAS_MAGIC)); + return ((CAN_BE_DEITY && HAS_MAGIC) || (CAN_BE_ZORA && HAS_MAGIC)) && + (Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GYORG) || + RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_NO); case ACTOR_BOSS_04: // Wart return (HAS_ITEM(ITEM_BOW) || HAS_ITEM(ITEM_HOOKSHOT) || CAN_BE_ZORA); case ACTOR_BOSS_HAKUGIN: // Goht - return (CAN_USE_MAGIC_ARROW(FIRE)); + return (CAN_USE_MAGIC_ARROW(FIRE)) && (Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GOHT) || + RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_NO); case ACTOR_EN_KNIGHT: // Igos du Ikana/IdI Lackey return (CAN_USE_MAGIC_ARROW(FIRE) && (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) >= EQUIP_VALUE_SHIELD_MIRROR) && diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index eeda5b54e0..c99453804b 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -210,8 +210,7 @@ static void DrawItemsTab() { ImGui::SameLine(); ImGui::BeginChild("randoItemsColumn2", ImVec2(columnWidth, halfHeight)); CVarCheckbox("Plentiful Items", Rando::StaticData::Options[RO_PLENTIFUL_ITEMS].cvar); - CVarCheckbox("Boss Souls", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + CVarCheckbox("Boss Souls", Rando::StaticData::Options[RO_SHUFFLE_BOSS_SOULS].cvar); CVarCheckbox("Enemy Souls", "gPlaceholderBool", CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); ImGui::EndChild(); diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp index a3f0a564c9..4fa5e0277f 100644 --- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp +++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp @@ -198,6 +198,11 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { itemPool.push_back(RI_SHIELD_HERO); // Add other items that don't have a vanilla location like Sun's Song or Song of Double Time + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_YES) { + for (int i = RI_SOUL_GOHT; i <= RI_SOUL_TWINMOLD; i++) { + itemPool.push_back((RandoItemId)i); + } + } // Remove starting items from the pool (but only one per entry in startingItems) for (RandoItemId startingItem : startingItems) { diff --git a/mm/2s2h/Rando/RemoveItem.cpp b/mm/2s2h/Rando/RemoveItem.cpp index 9907885759..8ad1bf282d 100644 --- a/mm/2s2h/Rando/RemoveItem.cpp +++ b/mm/2s2h/Rando/RemoveItem.cpp @@ -354,6 +354,13 @@ void Rando::RemoveItem(RandoItemId randoItemId) { case RI_REMAINS_TWINMOLD: REMOVE_QUEST_ITEM(QUEST_REMAINS_TWINMOLD); break; + case RI_SOUL_GOHT: + case RI_SOUL_GYORG: + case RI_SOUL_MAJORA: + case RI_SOUL_ODOLWA: + case RI_SOUL_TWINMOLD: + Flags_ClearRandoInf(RANDO_INF_OBTAINED_SOUL_OF_GOHT + (randoItemId - RI_SOUL_GOHT)); + break; // Ignore Ammo case RI_BOMBCHU: case RI_DEKU_STICK: diff --git a/mm/2s2h/Rando/StaticData/Items.cpp b/mm/2s2h/Rando/StaticData/Items.cpp index 99a593c607..fd4bd8dfe7 100644 --- a/mm/2s2h/Rando/StaticData/Items.cpp +++ b/mm/2s2h/Rando/StaticData/Items.cpp @@ -162,6 +162,11 @@ std::map Items = { RI(RI_SONG_STORMS, "the", "Song of Storms", RITYPE_MAJOR, ITEM_SONG_STORMS, GI_NONE, GID_NONE), RI(RI_SONG_SUN, "the", "Sun's Song", RITYPE_MAJOR, ITEM_SONG_SUN, GI_NONE, GID_NONE), RI(RI_SONG_TIME, "the", "Song of Time", RITYPE_MAJOR, ITEM_SONG_TIME, GI_NONE, GID_NONE), + RI(RI_SOUL_GOHT, "the", "Soul of Goht", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), + RI(RI_SOUL_GYORG, "the", "Soul of Gyorg", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), + RI(RI_SOUL_MAJORA, "the", "Soul of Majora", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), + RI(RI_SOUL_ODOLWA, "the", "Soul of Odolwa", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), + RI(RI_SOUL_TWINMOLD, "the", "Soul of Twinmold", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), RI(RI_STONE_TOWER_BOSS_KEY, "the", "Stone Tower Boss Key", RITYPE_BOSS_KEY, ITEM_KEY_BOSS, GI_KEY_BOSS, GID_KEY_BOSS), RI(RI_STONE_TOWER_COMPASS, "the", "Stone Tower Compass", RITYPE_LESSER, ITEM_COMPASS, GI_COMPASS, GID_COMPASS), RI(RI_STONE_TOWER_MAP, "the", "Stone Tower Map", RITYPE_LESSER, ITEM_DUNGEON_MAP, GI_MAP, GID_DUNGEON_MAP), diff --git a/mm/2s2h/Rando/StaticData/Options.cpp b/mm/2s2h/Rando/StaticData/Options.cpp index 90b205065c..d462ee0702 100644 --- a/mm/2s2h/Rando/StaticData/Options.cpp +++ b/mm/2s2h/Rando/StaticData/Options.cpp @@ -25,6 +25,7 @@ std::map Options = { RO(RO_PLENTIFUL_ITEMS, RO_GENERIC_OFF), RO(RO_SHUFFLE_BARREL_DROPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_BOSS_REMAINS, RO_GENERIC_OFF), + RO(RO_SHUFFLE_BOSS_SOULS, RO_GENERIC_OFF), RO(RO_SHUFFLE_COWS, RO_GENERIC_OFF), RO(RO_SHUFFLE_CRATE_DROPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_FREESTANDING_ITEMS, RO_GENERIC_OFF), diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index 6c3bd52927..f7ef6f92cd 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -1407,6 +1407,11 @@ typedef enum { RI_SONG_STORMS, RI_SONG_SUN, RI_SONG_TIME, + RI_SOUL_GOHT, + RI_SOUL_GYORG, + RI_SOUL_MAJORA, + RI_SOUL_ODOLWA, + RI_SOUL_TWINMOLD, RI_STONE_TOWER_BOSS_KEY, RI_STONE_TOWER_COMPASS, RI_STONE_TOWER_MAP, @@ -1742,6 +1747,7 @@ typedef enum { RO_PLENTIFUL_ITEMS, RO_SHUFFLE_BARREL_DROPS, RO_SHUFFLE_BOSS_REMAINS, + RO_SHUFFLE_BOSS_SOULS, RO_SHUFFLE_COWS, RO_SHUFFLE_CRATE_DROPS, RO_SHUFFLE_FREESTANDING_ITEMS, @@ -1804,6 +1810,11 @@ typedef enum { RANDO_INF_OBTAINED_LETTER_TO_MAMA, RANDO_INF_OBTAINED_LETTER_TO_KAFEI, RANDO_INF_OBTAINED_PENDANT_OF_MEMORIES, + RANDO_INF_OBTAINED_SOUL_OF_GOHT, + RANDO_INF_OBTAINED_SOUL_OF_GYORG, + RANDO_INF_OBTAINED_SOUL_OF_MAJORA, + RANDO_INF_OBTAINED_SOUL_OF_ODOLWA, + RANDO_INF_OBTAINED_SOUL_OF_TWINMOLD, RANDO_INF_MAX, } RandoInf; diff --git a/mm/src/overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.c b/mm/src/overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.c index 403940f2ac..84b6e317ec 100644 --- a/mm/src/overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.c +++ b/mm/src/overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.c @@ -13,6 +13,7 @@ #include "overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h" #include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" +#include "2s2h/GameInteractor/GameInteractor.h" #include "objects/gameplay_keep/gameplay_keep.h" @@ -1286,7 +1287,9 @@ void func_80B0813C(BossHakugin* this, PlayState* play) { DECR(this->unk_019C); } - if ((this->unk_0964.base.acFlags & AC_HIT) && (this->unk_0964.info.acHitInfo->toucher.dmgFlags == DMG_FIRE_ARROW)) { + if (GameInteractor_Should(VB_GOHT_UNFREEZE, + (this->unk_0964.base.acFlags & AC_HIT) && + (this->unk_0964.info.acHitInfo->toucher.dmgFlags == DMG_FIRE_ARROW))) { this->unk_0964.base.atFlags &= ~AT_HIT; this->unk_0964.base.acFlags &= ~AC_HIT; this->unk_0964.base.ocFlags1 &= ~OC1_HIT;