Skip to content

Commit

Permalink
Boss Souls initial implementation
Browse files Browse the repository at this point in the history
Co-authored-by: Caladius <[email protected]>
  • Loading branch information
garrettjoecox and Caladius committed Feb 14, 2025
1 parent 4d25d0d commit 3087d1d
Show file tree
Hide file tree
Showing 17 changed files with 357 additions and 7 deletions.
1 change: 1 addition & 0 deletions mm/2s2h/GameInteractor/GameInteractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
1 change: 1 addition & 0 deletions mm/2s2h/Rando/ActorBehavior/ActorBehavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ void InitObjMoonStoneBehavior();
void InitObjTaruBehavior();
void InitObjTsuboBehavior();
void InitObjWarpstoneBehavior();
void InitSoulsBehavior();

} // namespace ActorBehavior

Expand Down
72 changes: 72 additions & 0 deletions mm/2s2h/Rando/ActorBehavior/Souls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "ActorBehavior.h"
#include <libultraship/libultraship.h>

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);
});
}
6 changes: 6 additions & 0 deletions mm/2s2h/Rando/ConvertItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
197 changes: 197 additions & 0 deletions mm/2s2h/Rando/DrawFuncs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#include "DrawFuncs.h"
#include <libultraship/libultraship.h>
#include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h"
#include "BenPort.h"

extern "C" {
#include <functions.h>
#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 });
}
11 changes: 11 additions & 0 deletions mm/2s2h/Rando/DrawFuncs.h
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions mm/2s2h/Rando/DrawItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <libultraship/libultraship.h>
#include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h"
#include "2s2h/ShipInit.hpp"
#include "2s2h/Rando/DrawFuncs.h"

extern "C" {
#include "variables.h"
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions mm/2s2h/Rando/GiveItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 11 additions & 4 deletions mm/2s2h/Rando/Logic/Logic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand Down
Loading

0 comments on commit 3087d1d

Please sign in to comment.