From 3a952a655d57e063ad4211a2263ddee38c0feb9d Mon Sep 17 00:00:00 2001 From: Tom Overton Date: Thu, 23 Nov 2023 04:37:16 -0800 Subject: [PATCH] Boss05 (Bio Deku Baba) OK and documented (#1510) * Init, Destroy, Update, easy actions * Just the hard 3 + 1 left * One left * OK Co-authored-by: petrie911 <69443847+petrie911@users.noreply.github.com> * Data/bss in, extract colheader * Clean up externs * Better match for func_809EECBC * Sfx * Lot of naming * fix build * Name skel stuff * name limbdraws * name colliders * Document `object_boss05` * Port some names from Odolwa * Name bio baba types (and relatedly clean up EnPp) * Better names for the LimbDraw functions + some related vars * Name all functions * Name a lot of struct vars * Colliders and body parts * Name all struct vars (might come back for a few of these though) * Name most stack vars * Fragment states, ice data * Limb rot index * Damage effects * Enum for hit reactions * Enums for movement state/attack state * Finish up * Missed this in EnPp * Respond to reviews * Fix comment * Reverse order for postlimbdraw, etc. functions * Remove blank line at the top * Respond to review * Make a "getter" macro for the force detach timer * Respond to review --------- Co-authored-by: Elliptic Ellipsis Co-authored-by: petrie911 <69443847+petrie911@users.noreply.github.com> Co-authored-by: angie --- assets/xml/overlays/ovl_Boss_05.xml | 6 + include/z64scene.h | 2 +- spec | 4 +- src/overlays/actors/ovl_Boss_01/z_boss_01.c | 38 +- src/overlays/actors/ovl_Boss_01/z_boss_01.h | 4 +- src/overlays/actors/ovl_Boss_05/z_boss_05.c | 1765 +++++++++++++++++-- src/overlays/actors/ovl_Boss_05/z_boss_05.h | 109 +- src/overlays/actors/ovl_En_Pp/z_en_pp.c | 84 +- src/overlays/actors/ovl_En_Pp/z_en_pp.h | 62 +- tools/disasm/functions.txt | 80 +- tools/disasm/variables.txt | 42 +- undefined_syms.txt | 15 - 12 files changed, 1861 insertions(+), 350 deletions(-) create mode 100644 assets/xml/overlays/ovl_Boss_05.xml diff --git a/assets/xml/overlays/ovl_Boss_05.xml b/assets/xml/overlays/ovl_Boss_05.xml new file mode 100644 index 0000000000..b9465cee1d --- /dev/null +++ b/assets/xml/overlays/ovl_Boss_05.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/include/z64scene.h b/include/z64scene.h index 3c74f6541e..ff09738066 100644 --- a/include/z64scene.h +++ b/include/z64scene.h @@ -349,7 +349,7 @@ typedef struct { /* 0x10 */ UNK_TYPE1 pad10[0x4]; } Room; // size = 0x14 -typedef struct { +typedef struct RoomContext { /* 0x00 */ Room curRoom; /* 0x14 */ Room prevRoom; /* 0x28 */ void* roomMemPages[2]; // In a scene with transitions, roomMemory is split between two pages that toggle each transition. This is one continuous range, as the second page allocates from the end diff --git a/spec b/spec index 49b24db348..2bf8013150 100644 --- a/spec +++ b/spec @@ -2316,9 +2316,7 @@ beginseg name "ovl_Boss_05" compress include "build/src/overlays/actors/ovl_Boss_05/z_boss_05.o" - include "build/data/ovl_Boss_05/ovl_Boss_05.data.o" - include "build/data/ovl_Boss_05/ovl_Boss_05.bss.o" - include "build/data/ovl_Boss_05/ovl_Boss_05.reloc.o" + include "build/src/overlays/actors/ovl_Boss_05/ovl_Boss_05_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_Boss_01/z_boss_01.c b/src/overlays/actors/ovl_Boss_01/z_boss_01.c index 3674a227bb..6a9e6c8388 100644 --- a/src/overlays/actors/ovl_Boss_01/z_boss_01.c +++ b/src/overlays/actors/ovl_Boss_01/z_boss_01.c @@ -1953,17 +1953,17 @@ void Boss01_UpdateDamage(Boss01* this, PlayState* play) { if (this->shieldCollider.elements[ODOLWA_SHIELD_COLLIDER_SHIELD].info.bumperFlags & BUMP_HIT) { this->bodyInvincibilityTimer = 5; - if (this->damageTimer == 0) { + if (this->damagedTimer == 0) { ColliderInfo* acHitInfo = this->shieldCollider.elements[ODOLWA_SHIELD_COLLIDER_SHIELD].info.acHitInfo; if (acHitInfo->toucher.dmgFlags == DMG_SWORD_BEAM) { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.focus.pos.x, this->actor.focus.pos.y, this->actor.focus.pos.z, 0, 0, 3, CLEAR_TAG_PARAMS(CLEAR_TAG_LARGE_LIGHT_RAYS)); Actor_PlaySfx(&this->actor, NA_SE_IT_SHIELD_BOUND); - this->damageTimer = 5; + this->damagedTimer = 5; } } - } else if (this->damageTimer == 0) { + } else if (this->damagedTimer == 0) { for (i = 0; i < ODOLWA_SWORD_COLLIDER_MAX; i++) { if (this->swordCollider.elements[i].info.toucherFlags & TOUCH_HIT) { this->swordCollider.elements[i].info.toucherFlags &= ~TOUCH_HIT; @@ -2014,7 +2014,7 @@ void Boss01_UpdateDamage(Boss01* this, PlayState* play) { case ODOLWA_DMGEFF_STUN: stunned: Boss01_SetupStunned(this, play); - this->damageTimer = 15; + this->damagedTimer = 15; Actor_PlaySfx(&this->actor, NA_SE_EN_COMMON_FREEZE); this->canGuardOrEvade = false; return; @@ -2030,14 +2030,14 @@ void Boss01_UpdateDamage(Boss01* this, PlayState* play) { //! @bug: unreachable code. If Odolwa's damage effect is ODOLWA_DMGEFF_STUN, we early-return out of //! the function in the above switch statement. Boss01_SetupStunned(this, play); - this->damageTimer = 15; + this->damagedTimer = 15; } else if (this->actor.colChkInfo.damageEffect == ODOLWA_DMGEFF_DAZE) { Boss01_SetupDazed(this, play); Audio_PlaySfx_AtPos(&sOdolwaDamageSfxPos, NA_SE_EN_MIBOSS_DAMAGE_OLD); - this->damageTimer = 15; + this->damagedTimer = 15; } else { - this->damageFlashTimer = 15; - this->damageTimer = 5; + this->damagedFlashTimer = 15; + this->damagedTimer = 5; this->actor.colChkInfo.health -= damage; if ((s8)this->actor.colChkInfo.health <= 0) { Boss01_SetupDeathCutscene(this, play); @@ -2382,8 +2382,8 @@ void Boss01_Update(Actor* thisx, PlayState* play2) { DECR(this->timers[i]); } - DECR(this->damageTimer); - DECR(this->damageFlashTimer); + DECR(this->damagedTimer); + DECR(this->damagedFlashTimer); this->actor.flags |= ACTOR_FLAG_TARGETABLE; this->actionFunc(this, play); @@ -2896,7 +2896,7 @@ void Boss01_Draw(Actor* thisx, PlayState* play) { Gfx_SetupDL25_Opa(play->state.gfxCtx); Gfx_SetupDL25_Xlu(play->state.gfxCtx); - if (this->damageFlashTimer & 1) { + if ((this->damagedFlashTimer % 2) != 0) { POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099); } @@ -3252,16 +3252,16 @@ void Boss01_Bug_UpdateDamage(Boss01* this, PlayState* play) { this->bugACCollider.base.acFlags &= ~AC_HIT; acHitInfo = this->bugACCollider.info.acHitInfo; - if (this->damageTimer == 0) { + if (this->damagedTimer == 0) { Matrix_RotateYS(this->actor.yawTowardsPlayer, MTXMODE_NEW); if (acHitInfo->toucher.dmgFlags & 0x300000) { - this->damageTimer = 10; + this->damagedTimer = 10; Matrix_MultVecZ(-10.0f, &additionalVelocity); this->additionalVelocityX = additionalVelocity.x; this->additionalVelocityZ = additionalVelocity.z; } else { - this->damageTimer = 15; - this->damageFlashTimer = 15; + this->damagedTimer = 15; + this->damagedFlashTimer = 15; Matrix_MultVecZ(-20.0f, &additionalVelocity); this->additionalVelocityX = additionalVelocity.x; this->additionalVelocityZ = additionalVelocity.z; @@ -3294,7 +3294,7 @@ void Boss01_Bug_UpdateDamage(Boss01* this, PlayState* play) { if ((distXZ < (KREG(49) + 210.0f)) && (distXZ > (KREG(49) + 190.0f))) { Actor_PlaySfx(&this->actor, NA_SE_EN_MIZUBABA2_DAMAGE); Boss01_Bug_SetupDead(this, play); - this->damageFlashTimer = 15; + this->damagedFlashTimer = 15; this->bugDrawDmgEffType = ACTOR_DRAW_DMGEFF_FIRE; this->actor.speed = 0.0f; this->actor.velocity.y = 5.0f; @@ -3313,8 +3313,8 @@ void Boss01_Bug_Update(Actor* thisx, PlayState* play) { DECR(this->timers[i]); } - DECR(this->damageTimer); - DECR(this->damageFlashTimer); + DECR(this->damagedTimer); + DECR(this->damagedFlashTimer); this->actionFunc(this, play); @@ -3366,7 +3366,7 @@ void Boss01_Bug_Draw(Actor* thisx, PlayState* play) { Gfx_SetupDL25_Opa(play->state.gfxCtx); - if (this->damageFlashTimer & 1) { + if ((this->damagedFlashTimer % 2) != 0) { POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099); } diff --git a/src/overlays/actors/ovl_Boss_01/z_boss_01.h b/src/overlays/actors/ovl_Boss_01/z_boss_01.h index 3587751790..68363208a9 100644 --- a/src/overlays/actors/ovl_Boss_01/z_boss_01.h +++ b/src/overlays/actors/ovl_Boss_01/z_boss_01.h @@ -85,8 +85,8 @@ typedef struct Boss01 { /* 0x14E */ s16 timers[3]; /* 0x154 */ f32 animMorphFrames1; /* 0x158 */ f32 animMorphFrames2; - /* 0x15C */ s16 damageTimer; - /* 0x15E */ s16 damageFlashTimer; + /* 0x15C */ s16 damagedTimer; + /* 0x15E */ s16 damagedFlashTimer; /* 0x160 */ u8 isPerformingVerticalSlash; // set, but never checked /* 0x160 */ u8 landedFromJump; /* 0x162 */ u8 waitType; diff --git a/src/overlays/actors/ovl_Boss_05/z_boss_05.c b/src/overlays/actors/ovl_Boss_05/z_boss_05.c index 56d26129ab..ead388dc90 100644 --- a/src/overlays/actors/ovl_Boss_05/z_boss_05.c +++ b/src/overlays/actors/ovl_Boss_05/z_boss_05.c @@ -1,7 +1,30 @@ /* * File: z_boss_05.c * Overlay: ovl_Boss_05 - * Description: Bio Deku Baba + * Description: Bio Deku Baba and its lily pad + * + * This actor has five main types which can be further split into many different subtypes: + * - LilyPadWithHead: This variant of the actor consists of a Deku Baba head dangling from a lily pad. It can attack the + * player if they get close, and if the player attempts to stand on the lily pad, it will eventually flip the lily pad + * over and attack. The Deku Baba head can be detached by attacking the stem that connects it to the lily pad, or it + * can be killed by attacking the head directly. This type has a subtype which hides the lily pad leaf; this is used + * exclusively by Bio Deku Babas that hang from the ceiling. + * + * - LilyPad: If the head attached to a lily pad is detached or killed, then this harmless variant of the actor is left + * behind. This is a simple lily pad that the player can stand on. It sinks slightly in the water when the player + * stands on it, but it otherwise doesn't do much. + * + * - FallingHead: If the head attached to a lily pad with detached by hitting its connected stem, then this variant of + * the actor is spawned. It falls until it hits the ground, at which point it will grow legs and eyestalks and + * transform into a walking head. + * + * - WalkingHead: This is the "transformed" version of the Bio Deku Baba that spawns when the head is detached from a + * lily pad and touches the ground. It walks around on three legs and can attack the player by charging at them and + * biting them. + * + * - Fragment: When a Bio Deku Baba head is defeated (whether it's still attached to a lily pad or it's walking around + * freely), it will spawn multiple variants of the actor. This type can be divided into many different subtypes, one + * for each limb on the head. */ #include "z_boss_05.h" @@ -10,141 +33,300 @@ #define THIS ((Boss05*)thisx) +// This actor has an array of timers in its instance, but for the most part, it only uses the first entry +#define TIMER_CURRENT_ACTION 0 +#define TIMER_FALLING_HEAD_FALL 2 + void Boss05_Init(Actor* thisx, PlayState* play); void Boss05_Destroy(Actor* thisx, PlayState* play); void Boss05_Update(Actor* thisx, PlayState* play); void Boss05_Draw(Actor* thisx, PlayState* play); -void func_809EEDE8(Boss05* this, PlayState* play); -void func_809EF9BC(Boss05* this, PlayState* play); -void func_809EFAB4(Boss05* this, PlayState* play); -void func_809F010C(Boss05* this, PlayState* play); -void func_809F0244(Boss05* this, PlayState* play); -void func_809F0374(Boss05* this, PlayState* play); -void func_809F04C0(Boss05* this, PlayState* play); -void func_809F0590(Boss05* this, PlayState* play); -void func_809F06B8(Boss05* this, PlayState* play); -void func_809F0780(Boss05* this, PlayState* play); -void func_809F0ABC(Boss05* this, PlayState* play); -void func_809F0B0C(Boss05* this, PlayState* play); +void Boss05_LilyPadWithHead_SetupMove(Boss05* this, PlayState* play); +void Boss05_LilyPadWithHead_Move(Boss05* this, PlayState* play); +void Boss05_LilyPad_Idle(Boss05* this, PlayState* play); +void Boss05_FallingHead_Fall(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupTransform(Boss05* this, PlayState* play); +void Boss05_WalkingHead_Transform(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupIdle(Boss05* this, PlayState* play); +void Boss05_WalkingHead_Idle(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupWalk(Boss05* this, PlayState* play); +void Boss05_WalkingHead_Walk(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupSpottedPlayer(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SpottedPlayer(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupCharge(Boss05* this, PlayState* arg1); +void Boss05_WalkingHead_Charge(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupAttack(Boss05* this, PlayState* arg1); +void Boss05_WalkingHead_Attack(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupDamaged(Boss05* this, PlayState* play); +void Boss05_WalkingHead_Damaged(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupStunned(Boss05* this, PlayState* play); +void Boss05_WalkingHead_SetupFreeze(Boss05* this, PlayState* play); +void Boss05_WalkingHead_Stunned(Boss05* this, PlayState* play); +void Boss05_Fragment_Move(Boss05* this, PlayState* play); -#if 0 -// static ColliderJntSphElementInit sJntSphElementsInit[2] = { -static ColliderJntSphElementInit D_809F1B2C[2] = { +typedef enum BioDekuBabaDrawDmgEffState { + /* 0 */ BIO_BABA_DRAW_DMGEFF_STATE_NONE, + /* 1 */ BIO_BABA_DRAW_DMGEFF_STATE_FIRE_INIT, + /* 2 */ BIO_BABA_DRAW_DMGEFF_STATE_FIRE_ACTIVE, + /* 10 */ BIO_BABA_DRAW_DMGEFF_STATE_FROZEN_INIT = 10, + /* 11 */ BIO_BABA_DRAW_DMGEFF_STATE_FROZEN_ACTIVE, + /* 20 */ BIO_BABA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT = 20, + /* 21 */ BIO_BABA_DRAW_DMGEFF_STATE_LIGHT_ORB_ACTIVE +} BioDekuBabaDrawDmgEffState; + +typedef enum BioDekuBabaFragmentState { + /* 0 */ BIO_BABA_FRAGMENT_STATE_SPAWNED, + /* 1 */ BIO_BABA_FRAGMENT_STATE_UNDERWATER, + /* 2 */ BIO_BABA_FRAGMENT_STATE_ABOVE_WATER, + /* 3 */ BIO_BABA_FRAGMENT_STATE_FLYING_THROUGH_AIR +} BioDekuBabaFragmentState; + +typedef enum BioDekuBabaHeadHitReaction { + /* 0 */ BIO_BABA_HEAD_HIT_REACTION_NONE_OR_DAMAGED, + /* 2 */ BIO_BABA_HEAD_HIT_REACTION_DEAD = 2, + /* 10 */ BIO_BABA_HEAD_HIT_REACTION_DEATCH = 10 +} BioDekuBabaHeadHitReaction; + +typedef enum BioDekuBabaLilyPadWithHeadAttackState { + /* 0 */ BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_IDLE, + /* 1 */ BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_WIND_UP, + /* 2 */ BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_LUNGE_ATTACK, + /* 10 */ BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_FLIP_ATTACK = 10 +} BioDekuBabaLilyPadWithHeadAttackState; + +typedef enum BioDekuBabaLilyPadWithHeadMovementState { + /* 0 */ BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_IDLE, + /* 1 */ BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_WIND_UP, + /* 2 */ BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_LUNGE_ATTACK, + /* 3 */ BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_FLIP_ATTACK +} BioDekuBabaLilyPadWithHeadMovementState; + +#include "assets/overlays/ovl_Boss_05/ovl_Boss_05.c" + +// The limbs referenced here are not used. The spheres are positioned manually by Boss05_LilyPad_PostLimbDraw. +static ColliderJntSphElementInit sLilyPadJntSphElementsInit[] = { { - { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x08 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, - { 0, { { 0, 0, 0 }, 15 }, 100 }, + { + ELEMTYPE_UNK3, + { 0xF7CFFFFF, 0x00, 0x08 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { BIO_DEKU_BABA_LILY_PAD_LIMB_NONE, { { 0, 0, 0 }, 15 }, 100 }, }, { - { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x08 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, - { 0, { { 0, 0, 0 }, 15 }, 100 }, + { + ELEMTYPE_UNK3, + { 0xF7CFFFFF, 0x00, 0x08 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { BIO_DEKU_BABA_LILY_PAD_LIMB_NONE, { { 0, 0, 0 }, 15 }, 100 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_809F1B74 = { - { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_809F1B2C, // sJntSphElementsInit, +static ColliderJntSphInit sLilyPadJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sLilyPadJntSphElementsInit), + sLilyPadJntSphElementsInit, }; -// static ColliderJntSphElementInit sJntSphElementsInit[1] = { -static ColliderJntSphElementInit D_809F1B84[1] = { +// The limb referenced here is not used. The sphere is positioned manually by Boss05_Head_PostLimbDraw. +static ColliderJntSphElementInit sHeadJntSphElementsInit[] = { { - { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x08 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, - { 0, { { 0, 0, 0 }, 20 }, 100 }, + { + ELEMTYPE_UNK3, + { 0xF7CFFFFF, 0x00, 0x08 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { BIO_DEKU_BABA_HEAD_LIMB_NONE, { { 0, 0, 0 }, 20 }, 100 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_809F1BA8 = { - { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_809F1B84, // sJntSphElementsInit, +static ColliderJntSphInit sHeadJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sHeadJntSphElementsInit), + sHeadJntSphElementsInit, }; -// static ColliderJntSphElementInit sJntSphElementsInit[1] = { -static ColliderJntSphElementInit D_809F1BB8[1] = { +// The limb referenced here is not used. The sphere is positioned manually by Boss05_Head_PostLimbDraw. +static ColliderJntSphElementInit sWalkingHeadJntSphElementsInit[] = { { - { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x08 }, { 0xF7FFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, - { 0, { { 0, 0, 0 }, 15 }, 100 }, + { + ELEMTYPE_UNK3, + { 0xF7CFFFFF, 0x00, 0x08 }, + { 0xF7FFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { BIO_DEKU_BABA_HEAD_LIMB_NONE, { { 0, 0, 0 }, 15 }, 100 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_809F1BDC = { - { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_809F1BB8, // sJntSphElementsInit, +static ColliderJntSphInit sWalkingHeadJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sWalkingHeadJntSphElementsInit), + sWalkingHeadJntSphElementsInit, }; -// static DamageTable sDamageTable = { -static DamageTable D_809F1C00 = { - /* Deku Nut */ DMG_ENTRY(0, 0x0), - /* Deku Stick */ DMG_ENTRY(1, 0xF), - /* Horse trample */ DMG_ENTRY(0, 0x0), - /* Explosives */ DMG_ENTRY(1, 0xF), - /* Zora boomerang */ DMG_ENTRY(1, 0xF), - /* Normal arrow */ DMG_ENTRY(3, 0xF), - /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0), - /* Hookshot */ DMG_ENTRY(3, 0x0), - /* Goron punch */ DMG_ENTRY(2, 0xF), - /* Sword */ DMG_ENTRY(1, 0xE), - /* Goron pound */ DMG_ENTRY(1, 0xF), - /* Fire arrow */ DMG_ENTRY(1, 0x2), - /* Ice arrow */ DMG_ENTRY(1, 0x3), - /* Light arrow */ DMG_ENTRY(3, 0x4), - /* Goron spikes */ DMG_ENTRY(1, 0xF), - /* Deku spin */ DMG_ENTRY(1, 0xF), - /* Deku bubble */ DMG_ENTRY(3, 0xF), - /* Deku launch */ DMG_ENTRY(2, 0xF), - /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0x0), - /* Zora barrier */ DMG_ENTRY(1, 0xF), - /* Normal shield */ DMG_ENTRY(0, 0x0), - /* Light ray */ DMG_ENTRY(0, 0x0), - /* Thrown object */ DMG_ENTRY(1, 0xF), - /* Zora punch */ DMG_ENTRY(1, 0xF), - /* Spin attack */ DMG_ENTRY(1, 0xF), - /* Sword beam */ DMG_ENTRY(0, 0x0), - /* Normal Roll */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1B */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1C */ DMG_ENTRY(0, 0x0), - /* Unblockable */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1E */ DMG_ENTRY(0, 0x0), - /* Powder Keg */ DMG_ENTRY(1, 0xF), +/** + * Spawns eight ice shards on the Bio Deku Baba's head that fly off in random directions. + */ +void Boss05_WalkingHead_Thaw(Boss05* this, PlayState* play) { + static Color_RGBA8 sIcePrimColor = { 170, 255, 255, 255 }; + static Color_RGBA8 sIceEnvColor = { 200, 200, 255, 255 }; + static Vec3f sIceAccel = { 0.0f, -1.0f, 0.0f }; + Vec3f pos; + Vec3f velocity; + s32 i; + + SoundSource_PlaySfxAtFixedWorldPos(play, &this->bodyPartsPos[BIO_BABA_BODYPART_HEAD], 30, NA_SE_EV_ICE_BROKEN); + for (i = 0; i < 8; i++) { + velocity.x = Rand_CenteredFloat(7.0f); + velocity.z = Rand_CenteredFloat(7.0f); + velocity.y = Rand_ZeroFloat(6.0f) + 4.0f; + pos.x = this->bodyPartsPos[BIO_BABA_BODYPART_HEAD].x + velocity.x; + pos.y = this->bodyPartsPos[BIO_BABA_BODYPART_HEAD].y + velocity.y; + pos.z = this->bodyPartsPos[BIO_BABA_BODYPART_HEAD].z + velocity.z; + EffectSsEnIce_Spawn(play, &pos, Rand_ZeroFloat(0.5f) + 0.7f, &velocity, &sIceAccel, &sIcePrimColor, + &sIceEnvColor, 30); + } +} + +/** + * Manually sets the position of a sphere collider to a specific position. + */ +void Boss05_SetColliderSphere(s32 index, ColliderJntSph* collider, Vec3f* sphereCenter) { + collider->elements[index].dim.worldSphere.center.x = sphereCenter->x; + collider->elements[index].dim.worldSphere.center.y = sphereCenter->y; + collider->elements[index].dim.worldSphere.center.z = sphereCenter->z; + collider->elements[index].dim.worldSphere.radius = + collider->elements[index].dim.modelSphere.radius * collider->elements[index].dim.scale; +} + +typedef enum BioDekuBabaDamageEffect { + // Named based on the fact that everything with this damage effect deals zero damage. If this effect is given to an + // attack that deals non-zero damage, it will behave exactly like BIO_BABA_DMGEFF_DAMAGE. + /* 0x0 */ BIO_BABA_DMGEFF_IMMUNE, + + // Deals no damage, but turns the Bio Deku Baba blue, stops all animations, and makes it wait for 40 frames. + /* 0x1 */ BIO_BABA_DMGEFF_STUN, + + // Deals damage and surrounds the Bio Deku Baba with fire. + /* 0x2 */ BIO_BABA_DMGEFF_FIRE, + + // Behaves exactly like BIO_BABA_DMGEFF_STUN, but also surrounds it with ice and lasts for 80 frames. + /* 0x3 */ BIO_BABA_DMGEFF_FREEZE, + + // Deals damage and surrounds the Bio Deku Baba with yellow light orbs. + /* 0x4 */ BIO_BABA_DMGEFF_LIGHT_ORB, + + // Behaves exactly like BIO_BABA_DMGEFF_DAMAGE, so its purpose is unknown. Only used for the lily pad with head + // variant of the Bio Deku Baba. + /* 0xE */ BIO_BABA_DMGEFF_SWORD = 0xE, + + // Deals damage and has no special effect. + /* 0xF */ BIO_BABA_DMGEFF_DAMAGE +} BioDekuBabaDamageEffect; + +static DamageTable sLilyPadWithHeadDamageTable = { + /* Deku Nut */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Deku Stick */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Horse trample */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Explosives */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Zora boomerang */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Normal arrow */ DMG_ENTRY(3, BIO_BABA_DMGEFF_DAMAGE), + /* UNK_DMG_0x06 */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Hookshot */ DMG_ENTRY(3, BIO_BABA_DMGEFF_IMMUNE), + /* Goron punch */ DMG_ENTRY(2, BIO_BABA_DMGEFF_DAMAGE), + /* Sword */ DMG_ENTRY(1, BIO_BABA_DMGEFF_SWORD), + /* Goron pound */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Fire arrow */ DMG_ENTRY(1, BIO_BABA_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(1, BIO_BABA_DMGEFF_FREEZE), + /* Light arrow */ DMG_ENTRY(3, BIO_BABA_DMGEFF_LIGHT_ORB), + /* Goron spikes */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Deku spin */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Deku bubble */ DMG_ENTRY(3, BIO_BABA_DMGEFF_DAMAGE), + /* Deku launch */ DMG_ENTRY(2, BIO_BABA_DMGEFF_DAMAGE), + /* UNK_DMG_0x12 */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Zora barrier */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Normal shield */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Light ray */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Thrown object */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Zora punch */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Spin attack */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Sword beam */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Normal Roll */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* UNK_DMG_0x1B */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* UNK_DMG_0x1C */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Unblockable */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* UNK_DMG_0x1E */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Powder Keg */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), }; -// static DamageTable sDamageTable = { -static DamageTable D_809F1C20 = { - /* Deku Nut */ DMG_ENTRY(0, 0x1), - /* Deku Stick */ DMG_ENTRY(3, 0xF), - /* Horse trample */ DMG_ENTRY(0, 0x0), - /* Explosives */ DMG_ENTRY(1, 0xF), - /* Zora boomerang */ DMG_ENTRY(1, 0xF), - /* Normal arrow */ DMG_ENTRY(1, 0xF), - /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0), - /* Hookshot */ DMG_ENTRY(0, 0x1), - /* Goron punch */ DMG_ENTRY(1, 0xF), - /* Sword */ DMG_ENTRY(1, 0xF), - /* Goron pound */ DMG_ENTRY(1, 0xF), - /* Fire arrow */ DMG_ENTRY(2, 0x2), - /* Ice arrow */ DMG_ENTRY(2, 0x3), - /* Light arrow */ DMG_ENTRY(2, 0x4), - /* Goron spikes */ DMG_ENTRY(1, 0xF), - /* Deku spin */ DMG_ENTRY(0, 0x1), - /* Deku bubble */ DMG_ENTRY(1, 0xF), - /* Deku launch */ DMG_ENTRY(2, 0xF), - /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0x1), - /* Zora barrier */ DMG_ENTRY(1, 0xF), - /* Normal shield */ DMG_ENTRY(0, 0x0), - /* Light ray */ DMG_ENTRY(0, 0x0), - /* Thrown object */ DMG_ENTRY(1, 0xF), - /* Zora punch */ DMG_ENTRY(1, 0xF), - /* Spin attack */ DMG_ENTRY(1, 0xF), - /* Sword beam */ DMG_ENTRY(0, 0x0), - /* Normal Roll */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1B */ DMG_ENTRY(1, 0x2), - /* UNK_DMG_0x1C */ DMG_ENTRY(0, 0x0), - /* Unblockable */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1E */ DMG_ENTRY(0, 0x0), - /* Powder Keg */ DMG_ENTRY(1, 0xF), +static DamageTable sWalkingHeadDamageTable = { + /* Deku Nut */ DMG_ENTRY(0, BIO_BABA_DMGEFF_STUN), + /* Deku Stick */ DMG_ENTRY(3, BIO_BABA_DMGEFF_DAMAGE), + /* Horse trample */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Explosives */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Zora boomerang */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Normal arrow */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* UNK_DMG_0x06 */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Hookshot */ DMG_ENTRY(0, BIO_BABA_DMGEFF_STUN), + /* Goron punch */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Sword */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Goron pound */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Fire arrow */ DMG_ENTRY(2, BIO_BABA_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(2, BIO_BABA_DMGEFF_FREEZE), + /* Light arrow */ DMG_ENTRY(2, BIO_BABA_DMGEFF_LIGHT_ORB), + /* Goron spikes */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Deku spin */ DMG_ENTRY(0, BIO_BABA_DMGEFF_STUN), + /* Deku bubble */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Deku launch */ DMG_ENTRY(2, BIO_BABA_DMGEFF_DAMAGE), + /* UNK_DMG_0x12 */ DMG_ENTRY(0, BIO_BABA_DMGEFF_STUN), + /* Zora barrier */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Normal shield */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Light ray */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Thrown object */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Zora punch */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Spin attack */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), + /* Sword beam */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Normal Roll */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* UNK_DMG_0x1B */ DMG_ENTRY(1, BIO_BABA_DMGEFF_FIRE), + /* UNK_DMG_0x1C */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Unblockable */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* UNK_DMG_0x1E */ DMG_ENTRY(0, BIO_BABA_DMGEFF_IMMUNE), + /* Powder Keg */ DMG_ENTRY(1, BIO_BABA_DMGEFF_DAMAGE), }; ActorInit Boss_05_InitVars = { @@ -159,107 +341,1354 @@ ActorInit Boss_05_InitVars = { /**/ Boss05_Draw, }; -#endif +void Boss05_Init(Actor* thisx, PlayState* play) { + s32 pad; + Boss05* this = THIS; + CollisionHeader* colHeader = NULL; -extern ColliderJntSphElementInit D_809F1B2C[2]; -extern ColliderJntSphInit D_809F1B74; -extern ColliderJntSphElementInit D_809F1B84[1]; -extern ColliderJntSphInit D_809F1BA8; -extern ColliderJntSphElementInit D_809F1BB8[1]; -extern ColliderJntSphInit D_809F1BDC; -extern DamageTable D_809F1C00; -extern DamageTable D_809F1C20; + this->dyna.actor.targetMode = TARGET_MODE_3; + this->dyna.actor.colChkInfo.mass = MASS_HEAVY; + this->dyna.actor.colChkInfo.health = 2; + this->frameCounter = Rand_ZeroFloat(1000.0f); + this->lowerJawScaleXZ = 1.0f; + this->dyna.actor.gravity = -0.3f; -extern UNK_TYPE D_060006A4; -extern UNK_TYPE D_06000A5C; -extern UNK_TYPE D_06000ABC; -extern UNK_TYPE D_06002F0C; -extern UNK_TYPE D_06003448; -extern UNK_TYPE D_06006240; -extern UNK_TYPE D_06006E50; -extern UNK_TYPE D_06007488; -extern UNK_TYPE D_06007908; + Actor_SetScale(&this->dyna.actor, 0.01f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EE4E0.s") + if ((BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_LILY_PAD_WITH_HEAD) || + (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_NO_LEAF_LILY_PAD_WITH_HEAD)) { + if (BIO_BABA_FORCE_DETACH_TIMER(&this->dyna.actor) == 0) { + // This assignment is redundant; the timer already needs to be 0 to enter this block. + BIO_BABA_FORCE_DETACH_TIMER(&this->dyna.actor) = 0; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EE668.s") + this->dyna.actor.shape.rot.z = 0; + this->forceDetachTimer = BIO_BABA_FORCE_DETACH_TIMER(&this->dyna.actor); + // BIO_BABA_FORCE_DETACH_TIMER uses world.rot.z, so once we've stored the value, reset the rotation to 0 here. + this->dyna.actor.world.rot.z = this->dyna.actor.shape.rot.z; + this->dyna.actor.colChkInfo.damageTable = &sLilyPadWithHeadDamageTable; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/Boss05_Init.s") + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&sBioBabaLilypadCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, colHeader); + Boss05_LilyPadWithHead_SetupMove(this, play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/Boss05_Destroy.s") + SkelAnime_InitFlex(play, &this->lilyPadSkelAnime, &gBioDekuBabaLilyPadSkel, &gBioDekuBabaLilyPadIdleAnim, + this->lilyPadJointTable, this->lilyPadMorphTable, BIO_DEKU_BABA_LILY_PAD_LIMB_MAX); + SkelAnime_InitFlex(play, &this->headSkelAnime, &gBioDekuBabaHeadSkel, &gBioDekuBabaHeadChompAnim, + this->headJointTable, this->headMorphTable, BIO_DEKU_BABA_HEAD_LIMB_MAX); + this->animEndFrame = Animation_GetLastFrame(&gBioDekuBabaHeadChompAnim); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EECBC.s") + Collider_InitAndSetJntSph(play, &this->lilyPadCollider, &this->dyna.actor, &sLilyPadJntSphInit, + this->lilyPadColliderElements); + Collider_InitAndSetJntSph(play, &this->headCollider, &this->dyna.actor, &sHeadJntSphInit, + this->headColliderElements); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EEDD0.s") + if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { + this->dyna.actor.params = BIO_BABA_TYPE_LILY_PAD; + this->actionFunc = Boss05_LilyPad_Idle; + this->dyna.actor.flags &= ~ACTOR_FLAG_TARGETABLE; + func_800BC154(play, &play->actorCtx, &this->dyna.actor, ACTORCAT_BG); + } + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_LILY_PAD) { + this->actionFunc = Boss05_LilyPad_Idle; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EEDE8.s") + CollisionHeader_GetVirtual(&sBioBabaLilypadCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, colHeader); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EF9BC.s") + SkelAnime_InitFlex(play, &this->lilyPadSkelAnime, &gBioDekuBabaLilyPadSkel, &gBioDekuBabaLilyPadIdleAnim, + this->lilyPadJointTable, this->lilyPadMorphTable, BIO_DEKU_BABA_LILY_PAD_LIMB_MAX); + this->dyna.actor.flags &= ~ACTOR_FLAG_TARGETABLE; + func_800BC154(play, &play->actorCtx, &this->dyna.actor, ACTORCAT_BG); + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_FALLING_HEAD) { + this->actionFunc = Boss05_FallingHead_Fall; + this->fallingHeadLilyPadLimbScale = 1.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EFAB4.s") + SkelAnime_InitFlex(play, &this->lilyPadSkelAnime, &gBioDekuBabaLilyPadSkel, &gBioDekuBabaLilyPadIdleAnim, + this->lilyPadJointTable, this->lilyPadMorphTable, BIO_DEKU_BABA_LILY_PAD_LIMB_MAX); + SkelAnime_InitFlex(play, &this->headSkelAnime, &gBioDekuBabaHeadSkel, &gBioDekuBabaHeadChompAnim, + this->headJointTable, this->headMorphTable, BIO_DEKU_BABA_HEAD_LIMB_MAX); + this->animEndFrame = Animation_GetLastFrame(&gBioDekuBabaHeadChompAnim); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809EFE50.s") + Collider_InitAndSetJntSph(play, &this->lilyPadCollider, &this->dyna.actor, &sLilyPadJntSphInit, + this->lilyPadColliderElements); + Collider_InitAndSetJntSph(play, &this->headCollider, &this->dyna.actor, &sHeadJntSphInit, + this->headColliderElements); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0014.s") + ActorShape_Init(&this->dyna.actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->dyna.actor.colChkInfo.damageTable = &sLilyPadWithHeadDamageTable; + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_WALKING_HEAD) { + Boss05_WalkingHead_SetupTransform(this, play); + this->dyna.actor.colChkInfo.mass = 90; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0058.s") + SkelAnime_InitFlex(play, &this->headSkelAnime, &gBioDekuBabaHeadSkel, &gBioDekuBabaHeadChompAnim, + this->headJointTable, this->headMorphTable, BIO_DEKU_BABA_HEAD_LIMB_MAX); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F00CC.s") + Collider_InitAndSetJntSph(play, &this->headCollider, &this->dyna.actor, &sWalkingHeadJntSphInit, + this->headColliderElements); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F010C.s") + ActorShape_Init(&this->dyna.actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->dyna.actor.colChkInfo.damageTable = &sWalkingHeadDamageTable; + this->dyna.actor.flags |= ACTOR_FLAG_10 | ACTOR_FLAG_20; + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) >= BIO_BABA_TYPE_FRAGMENT_BASE) { + SkelAnime_InitFlex(play, &this->headSkelAnime, &gBioDekuBabaHeadSkel, &gBioDekuBabaHeadChompAnim, + this->headJointTable, this->headMorphTable, BIO_DEKU_BABA_HEAD_LIMB_MAX); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F01CC.s") + this->dyna.actor.gravity = 0.0f; + this->dyna.actor.world.rot.y = Rand_ZeroFloat(0x8000); + this->dyna.actor.speed = Rand_ZeroFloat(3.0f) + 3.0f; + this->dyna.actor.velocity.y = Rand_ZeroFloat(1.5f) + 1.5f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0244.s") + this->fragmentAngularVelocity.x = Rand_CenteredFloat(700.0f); + this->fragmentAngularVelocity.y = Rand_CenteredFloat(1500.0f); + this->timers[TIMER_CURRENT_ACTION] = Rand_ZeroFloat(30.0f) + 50.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F02D0.s") + this->dyna.actor.flags &= ~ACTOR_FLAG_TARGETABLE; + this->actionFunc = Boss05_Fragment_Move; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0374.s") +void Boss05_Destroy(Actor* thisx, PlayState* play) { + Boss05* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0474.s") + if ((BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_LILY_PAD) || + (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_LILY_PAD_WITH_HEAD) || + (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_NO_LEAF_LILY_PAD_WITH_HEAD)) { + DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F04C0.s") +/** + * Checks to see if the lily pad stem or the head was hit by an attack. If either of them were hit, this function will + * return a value indicating what the head should do; if the head should detach from the lily pad, then the draw damage + * effect to apply to the falling head will be included in the return value. + */ +s32 Boss05_LilyPadWithHead_UpdateDamage(Boss05* this, PlayState* play) { + if (this->damagedFlashTimer == 0) { + s32 i = 0; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0538.s") + while (true) { + if (this->lilyPadCollider.elements[i].info.bumperFlags & BUMP_HIT) { + switch (this->dyna.actor.colChkInfo.damageEffect) { + case BIO_BABA_DMGEFF_FIRE: + return BIO_BABA_HEAD_HIT_REACTION_DEATCH + BIO_BABA_DRAW_DMGEFF_STATE_FIRE_INIT; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0590.s") + case BIO_BABA_DMGEFF_FREEZE: + return BIO_BABA_HEAD_HIT_REACTION_DEATCH + BIO_BABA_DRAW_DMGEFF_STATE_FROZEN_INIT; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0650.s") + case BIO_BABA_DMGEFF_LIGHT_ORB: + return BIO_BABA_HEAD_HIT_REACTION_DEATCH + BIO_BABA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F06B8.s") + default: + return BIO_BABA_HEAD_HIT_REACTION_DEATCH + BIO_BABA_DRAW_DMGEFF_STATE_NONE; + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0708.s") + i++; + if (i == BIO_BABA_LILY_PAD_COLLIDER_MAX) { + if (this->headCollider.elements[BIO_BABA_HEAD_COLLIDER_HEAD].info.bumperFlags & BUMP_HIT) { + u8 damage = this->dyna.actor.colChkInfo.damage; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0780.s") + this->dyna.actor.colChkInfo.health -= damage; + if ((s8)this->dyna.actor.colChkInfo.health <= 0) { + Enemy_StartFinishingBlow(play, &this->dyna.actor); + return BIO_BABA_HEAD_HIT_REACTION_DEAD; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0A0C.s") + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_DAMAGE); + this->damagedFlashTimer = 15; + this->lilyPadWithHeadAttackState = BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_IDLE; + this->timers[TIMER_CURRENT_ACTION] = 30; + return BIO_BABA_HEAD_HIT_REACTION_NONE_OR_DAMAGED; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0A64.s") + break; + } + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0ABC.s") + return BIO_BABA_HEAD_HIT_REACTION_NONE_OR_DAMAGED; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F0B0C.s") +void Boss05_LilyPadWithHead_SetupMove(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_LilyPadWithHead_Move; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/Boss05_Update.s") +static Vec3s sWindUpLimbRot[BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MAX] = { + { 0x3200, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_UPPER_STEM + { -0x1E00, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MIDDLE_STEM + { -0x1400, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LOWER_STEM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_UPPER_ARM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_LOWER_ARM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_UPPER_ARM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_LOWER_ARM +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1050.s") +static Vec3s sLungeAttackLimbRot[BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MAX] = { + { -0x3200, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_UPPER_STEM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MIDDLE_STEM + { 0x1E00, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LOWER_STEM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_UPPER_ARM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_LOWER_ARM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_UPPER_ARM + { 0, 0, 0 }, // BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_LOWER_ARM +}; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1170.s") +/** + * Controls everything about how the lily pad and head should move and act, including manually rotating the limbs of the + * lily pad, changing the speed of the head animations, enabling collisions, spawning child actors when the head is + * detached or destroyed, etc. + */ +void Boss05_LilyPadWithHead_Move(Boss05* this, PlayState* play) { + s32 i; + Player* player = GET_PLAYER(play); + s32 j; + u8 disableATCollisions = false; + Vec3s targetLimbRot[BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MAX]; + s32 frequencyX; + s32 shiftX; + s32 amplitudeX; + s32 frequencyY; + s32 shiftY; + s32 amplitudeY; + s32 frequencyZ; + s32 shiftZ; + s32 amplitudeZ; + s32 limbRotMaxAngularVelocityFrac; + s32 limbRotAngularVelocity; + s16 rotAngularVelocity; + s16 rotMaxAngularVelocityFrac; + f32 diffY; + f32 attackRange; + Vec3f splashPos; + s32 hitReaction; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1284.s") + this->dyna.actor.hintId = TATL_HINT_ID_BIO_DEKU_BABA; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F12A0.s") + if (this->lilyPadWithHeadMovementState == BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_IDLE) { + frequencyX = 0x3E8; + shiftX = 0x3E80; + amplitudeX = 0x7D0; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F135C.s") + frequencyY = 0x5DC; + shiftY = 0x1770; + amplitudeY = 0xBB8; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1404.s") + frequencyZ = 0x514; + shiftZ = 0x5208; + amplitudeZ = 0xDAC; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1430.s") + limbRotMaxAngularVelocityFrac = 0xA; + limbRotAngularVelocity = 0x300; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1464.s") + if (this->damagedFlashTimer != 0) { + frequencyX = 0x1B58; + amplitudeX = 0x1770; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F14AC.s") + amplitudeY = 0x1770; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/func_809F1550.s") + frequencyZ = 0x1C84; + amplitudeZ = 0x1964; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_05/Boss05_Draw.s") + limbRotMaxAngularVelocityFrac = 1; + limbRotAngularVelocity = 0x1000; + } + } else if (this->lilyPadWithHeadMovementState == BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_FLIP_ATTACK) { + frequencyX = 0x1B58; + shiftX = 0x3E80; + amplitudeX = 0x1388; + + frequencyY = 0x5DC; + shiftY = 0x2328; + amplitudeY = 0x1388; + + frequencyZ = 0x1C84; + shiftZ = 0x5208; + amplitudeZ = 0x157C; + + limbRotMaxAngularVelocityFrac = 1; + limbRotAngularVelocity = 0x1000; + } else { + frequencyX = shiftX = amplitudeX = frequencyY = shiftY = amplitudeY = frequencyZ = shiftZ = amplitudeZ = + limbRotMaxAngularVelocityFrac = limbRotAngularVelocity = 0; + } + + for (i = 0; i < BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MAX; i++) { + if ((this->lilyPadWithHeadMovementState == BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_IDLE) || + (this->lilyPadWithHeadMovementState == BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_FLIP_ATTACK)) { + if (i <= BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LOWER_STEM) { + targetLimbRot[i].y = 0; + targetLimbRot[i].x = Math_SinS((this->frameCounter * frequencyX) + (i * shiftX)) * amplitudeX; + } else { + targetLimbRot[i].x = 0; + targetLimbRot[i].y = Math_SinS((this->frameCounter * frequencyY) + (i * shiftY)) * amplitudeY; + } + + if ((i == BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_LOWER_ARM) || + (i == BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_LOWER_ARM)) { + targetLimbRot[i].z = Math_SinS((this->frameCounter * frequencyZ) + (i * shiftZ)) * amplitudeZ * 2.0f; + } else { + targetLimbRot[i].z = Math_SinS((this->frameCounter * frequencyZ) + (i * shiftZ)) * amplitudeZ; + } + + rotMaxAngularVelocityFrac = limbRotMaxAngularVelocityFrac; + rotAngularVelocity = limbRotAngularVelocity; + } else { + if (this->lilyPadWithHeadMovementState == BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_WIND_UP) { + limbRotMaxAngularVelocityFrac = 5; + limbRotAngularVelocity = 0x1000; + + targetLimbRot[i].x = sWindUpLimbRot[i].x; + targetLimbRot[i].y = sWindUpLimbRot[i].y; + targetLimbRot[i].z = sWindUpLimbRot[i].z; + } else if (this->lilyPadWithHeadMovementState == BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_LUNGE_ATTACK) { + limbRotMaxAngularVelocityFrac = 1; + limbRotAngularVelocity = 0x2000; + + targetLimbRot[i].x = sLungeAttackLimbRot[i].x; + targetLimbRot[i].y = sLungeAttackLimbRot[i].y; + targetLimbRot[i].z = sLungeAttackLimbRot[i].z; + } + + rotMaxAngularVelocityFrac = limbRotMaxAngularVelocityFrac; + rotAngularVelocity = limbRotAngularVelocity; + } + + Math_ApproachS(&this->lilyPadWithHeadLimbRot[i].x, targetLimbRot[i].x, limbRotMaxAngularVelocityFrac, + limbRotAngularVelocity); + Math_ApproachS(&this->lilyPadWithHeadLimbRot[i].y, targetLimbRot[i].y, limbRotMaxAngularVelocityFrac, + limbRotAngularVelocity); + Math_ApproachS(&this->lilyPadWithHeadLimbRot[i].z, targetLimbRot[i].z, limbRotMaxAngularVelocityFrac, + limbRotAngularVelocity); + } + + diffY = (player->actor.world.pos.y - this->dyna.actor.world.pos.y) + 10.0f; + if (diffY < 0.0f) { + Math_ApproachS(&this->dyna.actor.shape.rot.y, this->dyna.actor.yawTowardsPlayer, rotMaxAngularVelocityFrac, + rotAngularVelocity); + } + + SkelAnime_Update(&this->headSkelAnime); + Math_ApproachF(&this->lowerJawScaleXZ, 1.0f, 0.1f, 0.1f); + + if (this->lilyPadWithHeadAttackState != BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_FLIP_ATTACK) { + Math_ApproachS(&this->dyna.actor.shape.rot.x, 0, 0x14, 0x800); + } + + switch (this->lilyPadWithHeadAttackState) { + case BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_IDLE: + this->lilyPadWithHeadMovementState = BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_IDLE; + this->headSkelAnime.playSpeed = 1.0f; + + if (diffY < 0.0f) { + if (player->actor.speed > 10.0f) { + attackRange = 220.0f; + } else { + attackRange = 150.0f; + } + + if ((this->timers[TIMER_CURRENT_ACTION] == 0) && + (sqrtf(this->dyna.actor.xyzDistToPlayerSq) <= attackRange)) { + this->lilyPadWithHeadAttackState = BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_WIND_UP; + this->timers[TIMER_CURRENT_ACTION] = 10; + } + + this->flipAttackFrameCounter = 0; + } else { + disableATCollisions = true; + + if (sqrtf(this->dyna.actor.xyzDistToPlayerSq) <= 40.0f) { + this->flipAttackFrameCounter++; + + if (this->flipAttackFrameCounter > 30) { + this->lilyPadWithHeadAttackState = 10; + this->timers[TIMER_CURRENT_ACTION] = 30; + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_ATTACK); + } + } else { + this->flipAttackFrameCounter = 0; + } + } + + if (((this->frameCounter % 4) == 0) && (Rand_ZeroOne() < 0.5f)) { + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_MOUTH); + } + break; + + case BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_FLIP_ATTACK: + if ((this->frameCounter % 2) == 0) { + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_MOUTH); + } + + this->lilyPadWithHeadStemRotX = (this->timers[TIMER_CURRENT_ACTION] & 1) * 0x200; + this->lilyPadWithHeadMovementState = BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_FLIP_ATTACK; + this->headSkelAnime.playSpeed = 4.0f; + Math_ApproachS(&this->dyna.actor.shape.rot.x, -0x8000, 2, 0x2000); + Math_ApproachS(&this->dyna.actor.shape.rot.y, this->dyna.actor.yawTowardsPlayer, 2, 0x2000); + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + this->flipAttackFrameCounter = 0; + this->lilyPadWithHeadAttackState = BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_IDLE; + this->timers[TIMER_CURRENT_ACTION] = 100; + } + + if (this->timers[TIMER_CURRENT_ACTION] == 27) { + Math_Vec3f_Copy(&splashPos, &this->dyna.actor.world.pos); + splashPos.y += 40.0f; + EffectSsGSplash_Spawn(play, &splashPos, NULL, NULL, 1, 2000); + Actor_PlaySfx(&this->dyna.actor, NA_SE_EV_OUT_OF_WATER); + } + break; + + case BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_WIND_UP: + if (Animation_OnFrame(&this->headSkelAnime, this->animEndFrame)) { + this->headSkelAnime.playSpeed = 0.0f; + } + + this->lilyPadWithHeadMovementState = BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_WIND_UP; + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + this->lilyPadWithHeadAttackState = BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_LUNGE_ATTACK; + this->timers[TIMER_CURRENT_ACTION] = 20; + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_ATTACK); + } + break; + + case BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_LUNGE_ATTACK: + if ((this->frameCounter % 2) == 0) { + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_MOUTH); + } + + this->lilyPadWithHeadStemRotX = (this->timers[TIMER_CURRENT_ACTION] & 1) * 0x200; + this->headSkelAnime.playSpeed = 4.0f; + Math_ApproachF(&this->lowerJawScaleXZ, 1.5f, 1.0f, 0.7f); + this->lilyPadWithHeadMovementState = BIO_BABA_LILY_PAD_WITH_HEAD_MOVEMENT_STATE_LUNGE_ATTACK; + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + this->lilyPadWithHeadAttackState = BIO_BABA_LILY_PAD_WITH_HEAD_ATTACK_STATE_IDLE; + this->timers[TIMER_CURRENT_ACTION] = 30; + } + break; + + default: + break; + } + + hitReaction = Boss05_LilyPadWithHead_UpdateDamage(this, play); + if ((hitReaction != BIO_BABA_HEAD_HIT_REACTION_NONE_OR_DAMAGED) || (this->forceDetachTimer == 1)) { + Boss05* child; + + this->dyna.actor.params = BIO_BABA_TYPE_LILY_PAD; + this->actionFunc = Boss05_LilyPad_Idle; + this->dyna.actor.flags &= ~ACTOR_FLAG_TARGETABLE; + func_800BC154(play, &play->actorCtx, &this->dyna.actor, ACTORCAT_BG); + if (this->forceDetachTimer != 0) { + hitReaction = BIO_BABA_HEAD_HIT_REACTION_DEATCH; + } + + if (hitReaction >= BIO_BABA_HEAD_HIT_REACTION_DEATCH) { + child = (Boss05*)Actor_SpawnAsChild( + &play->actorCtx, &this->dyna.actor, play, ACTOR_BOSS_05, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, this->dyna.actor.shape.rot.x, + this->dyna.actor.shape.rot.y, this->dyna.actor.shape.rot.z, BIO_BABA_TYPE_FALLING_HEAD); + + if (child != NULL) { + Player* player2 = GET_PLAYER(play); + + if (player2->lockOnActor == &this->dyna.actor) { + player2->lockOnActor = &child->dyna.actor; + play->actorCtx.targetCtx.fairyActor = &child->dyna.actor; + play->actorCtx.targetCtx.lockOnActor = &child->dyna.actor; + } + + for (i = 0; i < BIO_DEKU_BABA_LILY_PAD_LIMB_MAX; i++) { + child->lilyPadSkelAnime.jointTable[i] = this->lilyPadSkelAnime.jointTable[i]; + } + + for (i = 0; i < BIO_DEKU_BABA_HEAD_LIMB_MAX; i++) { + child->headSkelAnime.jointTable[i] = this->headSkelAnime.jointTable[i]; + } + + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_DAMAGE); + + if (hitReaction > BIO_BABA_HEAD_HIT_REACTION_DEATCH) { + child->drawDmgEffState = hitReaction - BIO_BABA_HEAD_HIT_REACTION_DEATCH; + } + } + } else if (hitReaction == BIO_BABA_HEAD_HIT_REACTION_DEAD) { + for (i = 0; i < 2; i++) { + child = (Boss05*)Actor_SpawnAsChild(&play->actorCtx, &this->dyna.actor, play, ACTOR_BOSS_05, + this->headPos.x, this->headPos.y, this->headPos.z, this->headRot.x, + this->headRot.y, this->headRot.z, BIO_BABA_TYPE_FRAGMENT_BASE + i); + + if (child != NULL) { + for (j = 0; j < BIO_DEKU_BABA_HEAD_LIMB_MAX; j++) { + child->headSkelAnime.jointTable[j] = this->headSkelAnime.jointTable[j]; + } + + child->timers[TIMER_CURRENT_ACTION] = Rand_ZeroFloat(20.0f) + 20.0f; + } + } + + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA_DEAD); + } + } + + if (!disableATCollisions) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->lilyPadCollider.base); + CollisionCheck_SetAT(play, &play->colChkCtx, &this->headCollider.base); + } + + CollisionCheck_SetAC(play, &play->colChkCtx, &this->lilyPadCollider.base); + CollisionCheck_SetAC(play, &play->colChkCtx, &this->headCollider.base); +} + +/** + * Makes the lily pad sink slightly and spawn some ripples when the player stands on it. + */ +void Boss05_LilyPad_Idle(Boss05* this, PlayState* play) { + Player* player = GET_PLAYER(play); + f32 distToPlayer = sqrtf(this->dyna.actor.xyzDistToPlayerSq); + s16 targetRotX; + + if ((distToPlayer < 50.0f) && (player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { + targetRotX = distToPlayer * 80.0f; + targetRotX = CLAMP_MAX(targetRotX, 0x7D0); + + Math_ApproachS(&this->lilyPadRotX, targetRotX, 0x14, 0x7D0); + Math_ApproachS(&this->lilyPadRotY, this->dyna.actor.yawTowardsPlayer, 0x14, 0xFA0); + if ((this->frameCounter % 16) == 0) { + EffectSsGRipple_Spawn(play, &this->dyna.actor.world.pos, 500, 1000, 0); + } + } else { + Math_ApproachS(&this->lilyPadRotX, 0, 0x14, 0x100); + } +} + +/** + * Controls everything about how the falling head should move and act, including manually adjusting its gravity, + * spawning a splash if the head touches the water, spawning bubbles as it sinks through the water, spawning a walking + * head when it touches the ground, etc. + */ +void Boss05_FallingHead_Fall(Boss05* this, PlayState* play) { + s32 i; + Vec3f unusedPos; + Vec3f bubblePos; + Boss05* walkingHead; + + Actor_MoveWithGravity(&this->dyna.actor); + this->dyna.actor.world.pos.y -= 85.0f; + this->dyna.actor.prevPos.y -= 85.0f; + Actor_UpdateBgCheckInfo(play, &this->dyna.actor, 20.0f, 50.0f, 40.0f, + UPDBGCHECKINFO_FLAG_4 | UPDBGCHECKINFO_FLAG_40); + this->dyna.actor.world.pos.y += 85.0f; + this->dyna.actor.prevPos.y += 85.0f; + + if (this->dyna.actor.bgCheckFlags & BGCHECKFLAG_WATER) { + this->dyna.actor.gravity = -0.3f; + this->dyna.actor.terminalVelocity = -5.0f; + } else { + this->dyna.actor.gravity = -2.0f; + this->dyna.actor.terminalVelocity = -20.0f; + } + + if (this->dyna.actor.bgCheckFlags & BGCHECKFLAG_WATER_TOUCH) { + this->dyna.actor.velocity.y = -3.0f; + this->dyna.actor.gravity = -0.3f; + this->dyna.actor.terminalVelocity = -5.0f; + Math_Vec3f_Copy(&unusedPos, &this->dyna.actor.world.pos); + unusedPos.y += 20.0f; + EffectSsGSplash_Spawn(play, &this->dyna.actor.world.pos, NULL, NULL, 1, 1000); + this->timers[TIMER_FALLING_HEAD_FALL] = 20; + } + + if (this->timers[TIMER_FALLING_HEAD_FALL] != 0) { + for (i = 0; i < 3; i++) { + bubblePos.x = Rand_CenteredFloat(40.0f) + this->dyna.actor.world.pos.x; + bubblePos.y = Rand_CenteredFloat(40.0f) + this->dyna.actor.world.pos.y; + bubblePos.z = Rand_CenteredFloat(40.0f) + this->dyna.actor.world.pos.z; + EffectSsBubble_Spawn(play, &bubblePos, 20.0f, 10.0f, 20.0f, 0.13f); + } + } + + if (this->dyna.actor.bgCheckFlags & BGCHECKFLAG_GROUND) { + if ((this->drawDmgEffType == ACTOR_DRAW_DMGEFF_FROZEN_NO_SFX) && (this->drawDmgEffTimer != 0)) { + this->drawDmgEffTimer = 0; + } + + Math_ApproachZeroF(&this->fallingHeadLilyPadLimbScale, 1.0f, 0.05f); + + if (this->fallingHeadLilyPadLimbScale == 0.0f) { + walkingHead = (Boss05*)Actor_SpawnAsChild( + &play->actorCtx, &this->dyna.actor, play, ACTOR_BOSS_05, this->headPos.x, this->headPos.y, + this->headPos.z, this->headRot.x, this->headRot.y, this->headRot.z, BIO_BABA_TYPE_WALKING_HEAD); + + if (walkingHead != NULL) { + Player* player = GET_PLAYER(play); + + if (player->lockOnActor == &this->dyna.actor) { + player->lockOnActor = &walkingHead->dyna.actor; + play->actorCtx.targetCtx.fairyActor = &walkingHead->dyna.actor; + play->actorCtx.targetCtx.lockOnActor = &walkingHead->dyna.actor; + } + + for (i = 0; i < BIO_DEKU_BABA_HEAD_LIMB_MAX; i++) { + walkingHead->headSkelAnime.jointTable[i] = this->headSkelAnime.jointTable[i]; + } + + Actor_PlaySfx(&walkingHead->dyna.actor, NA_SE_EN_MIZUBABA_TRANSFORM); + } + + Actor_Kill(&this->dyna.actor); + } + } +} + +void Boss05_WalkingHead_UpdateDamage(Boss05* this, PlayState* play) { + s32 pad[2]; + u8 attackDealsDamage = false; + ColliderInfo* acHitInfo; + + if ((this->damagedTimer == 0) && + (this->headCollider.elements[BIO_BABA_HEAD_COLLIDER_HEAD].info.bumperFlags & BUMP_HIT)) { + this->headCollider.elements[BIO_BABA_HEAD_COLLIDER_HEAD].info.bumperFlags &= ~BUMP_HIT; + acHitInfo = this->headCollider.elements[BIO_BABA_HEAD_COLLIDER_HEAD].info.acHitInfo; + if (acHitInfo->toucher.dmgFlags & 0x300000) { // (DMG_NORMAL_SHIELD | DMG_LIGHT_RAY) + this->knockbackMagnitude = -12.0f; + this->knockbackAngle = this->dyna.actor.yawTowardsPlayer; + this->damagedTimer = 6; + return; + } + + this->damagedTimer = 10; + this->dyna.actor.speed = 0.0f; + this->knockbackMagnitude = -20.0f; + this->knockbackAngle = this->dyna.actor.yawTowardsPlayer; + + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_DAMAGE); + + switch (this->dyna.actor.colChkInfo.damageEffect) { + case BIO_BABA_DMGEFF_STUN: + Boss05_WalkingHead_SetupStunned(this, play); + break; + + case BIO_BABA_DMGEFF_FIRE: + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_FIRE_INIT; + attackDealsDamage = true; + break; + + case BIO_BABA_DMGEFF_FREEZE: + Boss05_WalkingHead_SetupFreeze(this, play); + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_FROZEN_INIT; + break; + + case BIO_BABA_DMGEFF_LIGHT_ORB: + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT; + attackDealsDamage = true; + break; + + default: + attackDealsDamage = true; + break; + } + + if (attackDealsDamage) { + u8 damage; + + if ((this->actionFunc == Boss05_WalkingHead_Stunned) && + (this->drawDmgEffType == ACTOR_DRAW_DMGEFF_FROZEN_NO_SFX) && (this->drawDmgEffTimer != 0)) { + Boss05_WalkingHead_Thaw(this, play); + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_NONE; + } + + damage = this->dyna.actor.colChkInfo.damage; + this->dyna.actor.colChkInfo.health -= damage; + if ((s8)this->dyna.actor.colChkInfo.health <= 0) { + Enemy_StartFinishingBlow(play, &this->dyna.actor); + } + + Boss05_WalkingHead_SetupDamaged(this, play); + this->damagedFlashTimer = 15; + } + } +} + +/** + * Returns true if the Bio Deku Baba's model is rotated such that it is roughly facing the player, false otherwise. + */ +s32 Boss05_WalkingHead_IsLookingAtPlayer(Boss05* this, PlayState* play) { + s16 yawDiff = this->dyna.actor.yawTowardsPlayer - this->dyna.actor.shape.rot.y; + + if (ABS_ALT(yawDiff) < 0x3000) { + return true; + } else { + return false; + } +} + +/** + * Checks to see if the Bio Deku Baba is looking at the player, if it's within 200 units of the player, and if the + * player is not too far above or below the Bio Deku Baba. If all of these conditions are met, it will setup the Bio + * Deku Baba to play its spotted animation and start charging towards the player. + */ +void Boss05_WalkingHead_TrySpottingPlayer(Boss05* this, PlayState* play) { + if (Boss05_WalkingHead_IsLookingAtPlayer(this, play) && (this->dyna.actor.xyzDistToPlayerSq <= SQ(200.0f)) && + (fabsf(this->dyna.actor.playerHeightRel) < 70.0f)) { + Boss05_WalkingHead_SetupSpottedPlayer(this, play); + } +} + +void Boss05_WalkingHead_SetupTransform(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_Transform; + Animation_MorphToPlayOnce(&this->headSkelAnime, &gBioDekuBabaHeadTransformAnim, -5.0f); +} + +/** + * Handles the "transformation" the Bio Deku Baba undergoes where its head grows legs and eyestalks. This function will + * gradually scale up the Bio Deku Baba's various limbs, and once they reach their full size, it will put the Bio Deku + * Baba into an idle state. + */ +void Boss05_WalkingHead_Transform(Boss05* this, PlayState* play) { + SkelAnime_Update(&this->headSkelAnime); + Math_ApproachS(&this->dyna.actor.shape.rot.x, 0, 2, 0x400); + Math_ApproachS(&this->dyna.actor.shape.rot.z, 0, 2, 0x400); + Math_ApproachF(&this->headBodyScale, 1.0f, 1.0f, 0.5f); + Math_ApproachF(&this->headLimbScale, 1.0f, 1.0f, 0.14f); + + if (this->headLimbScale == 1.0f) { + Boss05_WalkingHead_SetupIdle(this, play); + } +} + +void Boss05_WalkingHead_SetupIdle(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_Idle; + Animation_MorphToLoop(&this->headSkelAnime, &gBioDekuBabaHeadIdleAnim, -10.0f); + this->timers[TIMER_CURRENT_ACTION] = Rand_ZeroFloat(25.0f) + 25.0f; + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA1_MOUTH); +} + +/** + * Waits in place for 25-50 frames, then starts walking. + */ +void Boss05_WalkingHead_Idle(Boss05* this, PlayState* play) { + SkelAnime_Update(&this->headSkelAnime); + Math_ApproachZeroF(&this->dyna.actor.speed, 1.0f, 2.5f); + Math_ApproachS(&this->dyna.actor.shape.rot.x, 0, 2, 0x400); + Math_ApproachS(&this->dyna.actor.shape.rot.z, 0, 2, 0x400); + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + Boss05_WalkingHead_SetupWalk(this, play); + } + + Boss05_WalkingHead_TrySpottingPlayer(this, play); +} + +void Boss05_WalkingHead_SetupWalk(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_Walk; + Animation_MorphToLoop(&this->headSkelAnime, &gBioDekuBabaHeadWalkAnim, 0.0f); + this->timers[TIMER_CURRENT_ACTION] = Rand_ZeroFloat(80.0f) + 60.0f; + this->walkTargetPos.x = Rand_CenteredFloat(400.0f) + this->dyna.actor.world.pos.x; + this->walkTargetPos.z = Rand_CenteredFloat(400.0f) + this->dyna.actor.world.pos.z; + this->walkAngularVelocityY = 0.0f; +} + +/** + * Walks toward a randomized target position until either 60-140 frames pass or until it gets close enough to its + * target. If either condition is met, the Bio Deku Baba will go back to being idle. + */ +void Boss05_WalkingHead_Walk(Boss05* this, PlayState* play) { + f32 diffX; + f32 diffZ; + + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_WALK - SFX_FLAG); + SkelAnime_Update(&this->headSkelAnime); + Math_ApproachF(&this->dyna.actor.speed, 5.0f, 1.0f, 2.0f); + diffX = this->walkTargetPos.x - this->dyna.actor.world.pos.x; + diffZ = this->walkTargetPos.z - this->dyna.actor.world.pos.z; + Math_ApproachS(&this->dyna.actor.world.rot.y, Math_Atan2S(diffX, diffZ), 5, this->walkAngularVelocityY); + Math_ApproachF(&this->walkAngularVelocityY, 2000.0f, 1.0f, 100.0f); + + if ((this->timers[TIMER_CURRENT_ACTION] == 0) || ((SQ(diffX) + SQ(diffZ)) < SQ(50.0f))) { + Boss05_WalkingHead_SetupIdle(this, play); + } + + Boss05_WalkingHead_TrySpottingPlayer(this, play); +} + +void Boss05_WalkingHead_SetupSpottedPlayer(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_SpottedPlayer; + Animation_MorphToPlayOnce(&this->headSkelAnime, &gBioDekuBabaHeadSpotAnim, 0.0f); + this->timers[TIMER_CURRENT_ACTION] = 20; +} + +/** + * Play the spotted animation and turn to face the player for 20 frames, then sets the Bio Deku Baba up to charge. + */ +void Boss05_WalkingHead_SpottedPlayer(Boss05* this, PlayState* play) { + SkelAnime_Update(&this->headSkelAnime); + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_VOICE - SFX_FLAG); + Math_ApproachZeroF(&this->dyna.actor.speed, 1.0f, 2.5f); + Math_ApproachS(&this->dyna.actor.world.rot.y, this->dyna.actor.yawTowardsPlayer, 5, 0x1000); + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + Boss05_WalkingHead_SetupCharge(this, play); + } +} + +void Boss05_WalkingHead_SetupCharge(Boss05* this, PlayState* arg1) { + this->actionFunc = Boss05_WalkingHead_Charge; + Animation_MorphToLoop(&this->headSkelAnime, &gBioDekuBabaHeadChargeAnim, 0.0f); + this->timers[TIMER_CURRENT_ACTION] = 60; + this->walkAngularVelocityY = 0.0f; +} + +/** + * Charge toward the player until either 60 frames pass or the Bio Deku Baba gets close enough to the player. If either + * condition is met, the Bio Deku Baba will switch to attacking the player. + */ +void Boss05_WalkingHead_Charge(Boss05* this, PlayState* play) { + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_VOICE - SFX_FLAG); + SkelAnime_Update(&this->headSkelAnime); + Math_ApproachF(&this->dyna.actor.speed, 8.0f, 1.0f, 4.0f); + Math_ApproachS(&this->dyna.actor.world.rot.y, this->dyna.actor.yawTowardsPlayer, 5, this->walkAngularVelocityY); + Math_ApproachF(&this->walkAngularVelocityY, 4000.0f, 1.0f, 400.0f); + + if ((this->timers[TIMER_CURRENT_ACTION] == 0) || (this->dyna.actor.xyzDistToPlayerSq <= SQ(150.0f))) { + Boss05_WalkingHead_SetupAttack(this, play); + } +} + +void Boss05_WalkingHead_SetupAttack(Boss05* this, PlayState* arg1) { + this->actionFunc = Boss05_WalkingHead_Attack; + Animation_MorphToPlayOnce(&this->headSkelAnime, &gBioDekuBabaHeadAttackAnim, 0.0f); + this->animEndFrame = Animation_GetLastFrame(&gBioDekuBabaHeadAttackAnim); + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_ATTACK); +} + +/** + * Play the attack animation to completion, then go back to being idle. + */ +void Boss05_WalkingHead_Attack(Boss05* this, PlayState* play) { + SkelAnime_Update(&this->headSkelAnime); + + if (Animation_OnFrame(&this->headSkelAnime, this->animEndFrame)) { + Boss05_WalkingHead_SetupIdle(this, play); + } +} + +void Boss05_WalkingHead_SetupDamaged(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_Damaged; + Animation_MorphToPlayOnce(&this->headSkelAnime, &gBioDekuBabaHeadDamagedAnim, 0.0f); + this->animEndFrame = Animation_GetLastFrame(&gBioDekuBabaHeadAttackAnim); + Actor_SetColorFilter(&this->dyna.actor, COLORFILTER_COLORFLAG_RED, 120, COLORFILTER_BUFFLAG_OPA, 30); +} + +/** + * If the Bio Deku Baba was killed by the most recent attack, this function will play its damaged animation for 22 + * frames, then spawn fragments for each of the walking head's limbs along with many bubble effects. Otherwise, it will + * play the damaged animation to completion and then make the Bio Deku Baba spot the player. + */ +void Boss05_WalkingHead_Damaged(Boss05* this, PlayState* play) { + s32 i; + s32 j; + Vec3f bubblePos; + Boss05* fragment; + + SkelAnime_Update(&this->headSkelAnime); + + if ((s8)this->dyna.actor.colChkInfo.health <= 0) { + if (Animation_OnFrame(&this->headSkelAnime, 22.0f)) { + for (i = 0; i < BIO_BABA_TYPE_MAX - BIO_BABA_TYPE_FRAGMENT_BASE; i++) { + fragment = (Boss05*)Actor_SpawnAsChild( + &play->actorCtx, &this->dyna.actor, play, ACTOR_BOSS_05, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, this->dyna.actor.shape.rot.x, + this->dyna.actor.shape.rot.y, this->dyna.actor.shape.rot.z, BIO_BABA_TYPE_FRAGMENT_BASE + i); + + if (fragment != NULL) { + for (j = 0; j < BIO_DEKU_BABA_HEAD_LIMB_MAX; j++) { + fragment->headSkelAnime.jointTable[j] = this->headSkelAnime.jointTable[j]; + } + } + } + + for (i = 0; i < 20; i++) { + bubblePos.x = Rand_CenteredFloat(40.0f) + this->dyna.actor.world.pos.x; + bubblePos.y = Rand_CenteredFloat(40.0f) + this->dyna.actor.world.pos.y; + bubblePos.z = Rand_CenteredFloat(40.0f) + this->dyna.actor.world.pos.z; + EffectSsBubble_Spawn(play, &bubblePos, 20.0f, 10.0f, 20.0f, 0.13f); + } + + SoundSource_PlaySfxAtFixedWorldPos(play, &this->dyna.actor.world.pos, 40, NA_SE_EN_MIZUBABA_DEAD); + Actor_Kill(&this->dyna.actor); + Item_DropCollectibleRandom(play, NULL, &this->dyna.actor.world.pos, 0xE0); + } + } else if (Animation_OnFrame(&this->headSkelAnime, this->animEndFrame)) { + Boss05_WalkingHead_SetupSpottedPlayer(this, play); + } +} + +void Boss05_WalkingHead_SetupStunned(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_Stunned; + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_DAMAGE); + this->timers[TIMER_CURRENT_ACTION] = 40; + Actor_SetColorFilter(&this->dyna.actor, COLORFILTER_COLORFLAG_BLUE, 120, COLORFILTER_BUFFLAG_OPA, 40); +} + +void Boss05_WalkingHead_SetupFreeze(Boss05* this, PlayState* play) { + this->actionFunc = Boss05_WalkingHead_Stunned; + Actor_PlaySfx(&this->dyna.actor, NA_SE_EN_MIZUBABA2_DAMAGE); + this->timers[TIMER_CURRENT_ACTION] = 80; + Actor_SetColorFilter(&this->dyna.actor, COLORFILTER_COLORFLAG_BLUE, 120, COLORFILTER_BUFFLAG_OPA, 80); +} + +/** + * Turns the Bio Deku Baba blue, stops all animations and locks the Bio Deku Baba in place for either 40 or 80 frames + * (depending on whether or not it was stunned via being frozen from an ice attack). Once the timer runs out, the Bio + * Deku Baba will go back to being idle. + */ +void Boss05_WalkingHead_Stunned(Boss05* this, PlayState* play) { + Math_ApproachZeroF(&this->dyna.actor.speed, 1.0f, 2.5f); + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + Boss05_WalkingHead_SetupIdle(this, play); + } +} + +/** + * Controls how fragments of a dead Bio Deku Baba should move and act. If the fragments are underwater, then they slowly + * drift upwards for 50-80 frames before despawning. If the fragments are above water, they instead fly through the air + * until they touch the ground. + */ +void Boss05_Fragment_Move(Boss05* this, PlayState* play) { + Actor_MoveWithGravity(&this->dyna.actor); + + if (this->fragmentState == BIO_BABA_FRAGMENT_STATE_SPAWNED) { + Actor_UpdateBgCheckInfo(play, &this->dyna.actor, 20.0f, 50.0f, 40.0f, + UPDBGCHECKINFO_FLAG_4 | UPDBGCHECKINFO_FLAG_40); + if (this->dyna.actor.bgCheckFlags & BGCHECKFLAG_WATER) { + this->fragmentState = BIO_BABA_FRAGMENT_STATE_UNDERWATER; + } else { + this->fragmentState = BIO_BABA_FRAGMENT_STATE_ABOVE_WATER; + } + } + + if (this->fragmentState == BIO_BABA_FRAGMENT_STATE_UNDERWATER) { + Math_ApproachF(&this->dyna.actor.velocity.y, 1.0f, 1.0f, 0.1f); + Math_ApproachZeroF(&this->dyna.actor.speed, 0.5f, 0.5f); + this->dyna.actor.shape.rot.x += this->fragmentAngularVelocity.x; + this->dyna.actor.shape.rot.y += this->fragmentAngularVelocity.y; + + if (this->timers[TIMER_CURRENT_ACTION] == 0) { + Actor_Kill(&this->dyna.actor); + } + } else { + switch (this->fragmentState) { + case BIO_BABA_FRAGMENT_STATE_ABOVE_WATER: + this->dyna.actor.velocity.y = Rand_ZeroFloat(3.0f) + 3.0f; + this->dyna.actor.speed = Rand_CenteredFloat(5.0f) + 5.0f; + this->dyna.actor.world.rot.y = Rand_ZeroFloat(0x10000); + this->dyna.actor.gravity = -1.0f; + this->fragmentState = BIO_BABA_FRAGMENT_STATE_FLYING_THROUGH_AIR; + break; + + case BIO_BABA_FRAGMENT_STATE_FLYING_THROUGH_AIR: + Actor_MoveWithGravity(&this->dyna.actor); + + if (this->fragmentPos.y < (this->dyna.actor.floorHeight - 30.0f)) { + Actor_Kill(&this->dyna.actor); + } + break; + + default: + break; + } + } +} + +void Boss05_Update(Actor* thisx, PlayState* play) { + s32 pad; + Boss05* this = THIS; + s16 i; + + this->frameCounter++; + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + DECR(this->timers[i]); + } + + DECR(this->damagedTimer); + DECR(this->damagedFlashTimer); + DECR(this->forceDetachTimer); + DECR(this->drawDmgEffTimer); + + this->actionFunc(this, play); + + if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_WALKING_HEAD) { + Actor_MoveWithGravity(&this->dyna.actor); + Matrix_RotateYS(this->knockbackAngle, MTXMODE_NEW); + Matrix_MultVecZ(this->knockbackMagnitude, &this->knockbackVelocity); + this->dyna.actor.world.pos.x += this->knockbackVelocity.x; + this->dyna.actor.world.pos.z += this->knockbackVelocity.z; + Math_ApproachZeroF(&this->knockbackMagnitude, 1.0f, 1.0f); + Actor_UpdateBgCheckInfo(play, &this->dyna.actor, 20.0f, 50.0f, 40.0f, + UPDBGCHECKINFO_FLAG_1 | UPDBGCHECKINFO_FLAG_4 | UPDBGCHECKINFO_FLAG_40); + Boss05_WalkingHead_UpdateDamage(this, play); + CollisionCheck_SetAT(play, &play->colChkCtx, &this->headCollider.base); + CollisionCheck_SetAC(play, &play->colChkCtx, &this->headCollider.base); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->headCollider.base); + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; + } + + switch (this->drawDmgEffState) { + case BIO_BABA_DRAW_DMGEFF_STATE_NONE: + this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FIRE; + this->drawDmgEffTimer = 0; + this->drawDmgEffAlpha = 0.0f; + break; + + case BIO_BABA_DRAW_DMGEFF_STATE_FIRE_INIT: + this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FIRE; + this->drawDmgEffTimer = 80; + this->drawDmgEffAlpha = 1.0f; + this->drawDmgEffState++; + this->drawDmgEffScale = 0.0f; + // fallthrough + case BIO_BABA_DRAW_DMGEFF_STATE_FIRE_ACTIVE: + if (this->drawDmgEffTimer == 0) { + Math_ApproachZeroF(&this->drawDmgEffAlpha, 1.0f, 0.02f); + if (this->drawDmgEffAlpha == 0.0f) { + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_NONE; + } + } else { + Math_ApproachF(&this->drawDmgEffScale, 1.0f, 0.1f, 0.5f); + } + break; + + case BIO_BABA_DRAW_DMGEFF_STATE_FROZEN_INIT: + this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FROZEN_NO_SFX; + this->drawDmgEffTimer = 80; + this->drawDmgEffAlpha = 1.0f; + this->drawDmgEffState++; + this->drawDmgEffScale = 0.0f; + this->drawDmgEffFrozenSteamScale = 2.0f; + // fallthrough + case BIO_BABA_DRAW_DMGEFF_STATE_FROZEN_ACTIVE: + if (this->drawDmgEffTimer == 0) { + Boss05_WalkingHead_Thaw(this, play); + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_NONE; + } else { + Math_ApproachF(&this->drawDmgEffScale, 1.0f, 1.0f, 0.25f); + Math_ApproachF(&this->drawDmgEffFrozenSteamScale, 1.0f, 0.1f, 0.1f); + } + break; + + case BIO_BABA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT: + this->drawDmgEffType = ACTOR_DRAW_DMGEFF_LIGHT_ORBS; + this->drawDmgEffTimer = 80; + this->drawDmgEffAlpha = 1.0f; + this->drawDmgEffState++; + this->drawDmgEffScale = 0.0f; + break; + + case BIO_BABA_DRAW_DMGEFF_STATE_LIGHT_ORB_ACTIVE: + if (this->drawDmgEffTimer == 0) { + Math_ApproachZeroF(&this->drawDmgEffScale, 1.0f, 0.03f); + + if (this->drawDmgEffScale == 0.0f) { + this->drawDmgEffState = BIO_BABA_DRAW_DMGEFF_STATE_NONE; + this->drawDmgEffAlpha = 0.0f; + } + } else { + Math_ApproachF(&this->drawDmgEffScale, 1.0f, 0.5f, 0.5f); + } + break; + + default: + break; + } +} + +static s8 sLimbIndexToLimbRotIndex[] = { + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_NONE, // BIO_DEKU_BABA_LILY_PAD_LIMB_NONE + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_NONE, // BIO_DEKU_BABA_LILY_PAD_LIMB_ROOTS + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_UPPER_STEM, // BIO_DEKU_BABA_LILY_PAD_LIMB_UPPER_STEM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MIDDLE_STEM, // BIO_DEKU_BABA_LILY_PAD_LIMB_MIDDLE_STEM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LOWER_STEM, // BIO_DEKU_BABA_LILY_PAD_LIMB_LOWER_STEM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_UPPER_ARM, // BIO_DEKU_BABA_LILY_PAD_LIMB_LEFT_UPPER_ARM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_LOWER_ARM, // BIO_DEKU_BABA_LILY_PAD_LIMB_LEFT_LOWER_ARM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_UPPER_ARM, // BIO_DEKU_BABA_LILY_PAD_LIMB_RIGHT_UPPER_ARM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_LOWER_ARM, // BIO_DEKU_BABA_LILY_PAD_LIMB_RIGHT_LOWER_ARM + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_NONE, // BIO_DEKU_BABA_LILY_PAD_LIMB_LEAF + BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_NONE, // Doesn't correspond to a real limb on the lily pad +}; + +s32 Boss05_LilyPadWithHead_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + Boss05* this = THIS; + s8 limbRotIndex; + + if (limbIndex == KREG(32)) { + if ((this->frameCounter % 4) == 0) { + *dList = NULL; + } + + rot->x += KREG(33) * 0x100; + rot->y += KREG(34) * 0x100; + rot->z += KREG(35) * 0x100; + } + + if ((BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_NO_LEAF_LILY_PAD_WITH_HEAD) && + (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_LEAF)) { + *dList = NULL; + } + + if (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_UPPER_STEM) { + rot->x += this->lilyPadWithHeadStemRotX; + } + + limbRotIndex = sLimbIndexToLimbRotIndex[limbIndex]; + if (limbRotIndex > BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_NONE) { + rot->x = rot->x + this->lilyPadWithHeadLimbRot[limbRotIndex].x; + rot->y = rot->y + this->lilyPadWithHeadLimbRot[limbRotIndex].y; + rot->z = rot->z + this->lilyPadWithHeadLimbRot[limbRotIndex].z; + } + + return false; +} + +void Boss05_LilyPad_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + static Vec3f sHeadOffset = { 0.0f, -1400.0f, 600.0f }; + Boss05* this = THIS; + MtxF mf; + Vec3f upperStemPos; + Vec3f lowerStemPos; + + if (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_LOWER_STEM) { + Matrix_MultVec3f(&sHeadOffset, &this->headPos); + + if (this->actionFunc == Boss05_FallingHead_Fall) { + Matrix_MultVec3f(&sHeadOffset, &this->dyna.actor.focus.pos); + } + + Matrix_Get(&mf); + Matrix_MtxFToYXZRot(&mf, &this->headRot, false); + this->headRot.x += 0xF00; + } + + if (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_UPPER_STEM) { + Matrix_MultZero(&upperStemPos); + Boss05_SetColliderSphere(BIO_BABA_LILY_PAD_COLLIDER_UPPER_STEM, &this->lilyPadCollider, &upperStemPos); + } + + if (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_MIDDLE_STEM) { + Matrix_MultVecY(-500.0f, &lowerStemPos); + Boss05_SetColliderSphere(BIO_BABA_LILY_PAD_COLLIDER_MIDDLE_STEM, &this->lilyPadCollider, &lowerStemPos); + + if (this->actionFunc == Boss05_LilyPadWithHead_Move) { + Matrix_MultVecY(1500.0f, &this->dyna.actor.focus.pos); + } + } +} + +s32 Boss05_Head_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + return false; +} + +Vec3f sBioDekuBabaHeadColliderPos; + +void Boss05_Head_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + static Vec3f sHeadColliderOffset = { 1600.0f, -300.0f, 0.0f }; + static Vec3f sHeadOffset = { 700.0f, 0.0f, 0.0f }; + Boss05* this = THIS; + + if (limbIndex == BIO_DEKU_BABA_HEAD_LIMB_BODY) { + Matrix_MultVec3f(&sHeadColliderOffset, &sBioDekuBabaHeadColliderPos); + Boss05_SetColliderSphere(BIO_BABA_HEAD_COLLIDER_HEAD, &this->headCollider, &sBioDekuBabaHeadColliderPos); + + if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_WALKING_HEAD) { + Matrix_MultVec3f(&sHeadOffset, &this->dyna.actor.focus.pos); + } + + if (this->drawDmgEffState != BIO_BABA_DRAW_DMGEFF_STATE_NONE) { + Matrix_MultVec3f(&sHeadOffset, &this->bodyPartsPos[BIO_BABA_BODYPART_HEAD]); + if (this->drawDmgEffType == ACTOR_DRAW_DMGEFF_FIRE) { + this->bodyPartsPos[BIO_BABA_BODYPART_HEAD].y -= 15.0f; + } + } + } +} + +void Boss05_Head_TransformLimbDraw(PlayState* play, s32 limbIndex, Actor* thisx) { + Boss05* this = THIS; + + if ((limbIndex == BIO_DEKU_BABA_HEAD_LIMB_LOWER_JAW) || (limbIndex == BIO_DEKU_BABA_HEAD_LIMB_UPPER_JAW)) { + Matrix_Scale(this->lowerJawScaleXZ, 1.0f, this->lowerJawScaleXZ, MTXMODE_APPLY); + } + + if (limbIndex == BIO_DEKU_BABA_HEAD_LIMB_BODY) { + Matrix_Scale(this->headBodyScale, this->headBodyScale, this->headBodyScale, MTXMODE_APPLY); + } + + if ((limbIndex >= BIO_DEKU_BABA_HEAD_LIMB_BACK_UPPER_LEG) && (limbIndex <= BIO_DEKU_BABA_HEAD_LIMB_LEAVES)) { + Matrix_Scale(this->headLimbScale, this->headLimbScale, this->headLimbScale, MTXMODE_APPLY); + } +} + +s32 Boss05_LilyPad_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + if ((limbIndex >= BIO_DEKU_BABA_LILY_PAD_LIMB_MIDDLE_STEM) && + (limbIndex <= BIO_DEKU_BABA_LILY_PAD_LIMB_RIGHT_LOWER_ARM)) { + *dList = NULL; + } + + return false; +} + +s32 Boss05_FallingHeadLilyPad_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + if ((limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_ROOTS) || (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_UPPER_STEM) || + (limbIndex == BIO_DEKU_BABA_LILY_PAD_LIMB_LEAF)) { + *dList = NULL; + } + + return false; +} + +void Boss05_FallingHeadLilyPad_TransformLimbDraw(PlayState* play, s32 limbIndex, Actor* thisx) { + Boss05* this = THIS; + + if ((limbIndex >= BIO_DEKU_BABA_LILY_PAD_LIMB_MIDDLE_STEM) && + (limbIndex <= BIO_DEKU_BABA_LILY_PAD_LIMB_RIGHT_LOWER_ARM)) { + Matrix_Scale(this->fallingHeadLilyPadLimbScale, this->fallingHeadLilyPadLimbScale, + this->fallingHeadLilyPadLimbScale, MTXMODE_APPLY); + } +} + +static BioDekuBabaHeadLimb sFragmentIndexToLimbIndex[BIO_BABA_TYPE_MAX - BIO_BABA_TYPE_FRAGMENT_BASE] = { + BIO_DEKU_BABA_HEAD_LIMB_LOWER_JAW, // BIO_BABA_TYPE_FRAGMENT_LOWER_JAW + BIO_DEKU_BABA_HEAD_LIMB_UPPER_JAW, // BIO_BABA_TYPE_FRAGMENT_UPPER_JAW + BIO_DEKU_BABA_HEAD_LIMB_BODY, // BIO_BABA_TYPE_FRAGMENT_BODY + BIO_DEKU_BABA_HEAD_LIMB_BACK_UPPER_LEG, // BIO_BABA_TYPE_FRAGMENT_BACK_UPPER_LEG + BIO_DEKU_BABA_HEAD_LIMB_BACK_LOWER_LEG, // BIO_BABA_TYPE_FRAGMENT_BACK_LOWER_LEG + BIO_DEKU_BABA_HEAD_LIMB_LEFT_UPPER_LEG, // BIO_BABA_TYPE_FRAGMENT_LEFT_UPPER_LEG + BIO_DEKU_BABA_HEAD_LIMB_LEFT_LOWER_LEG, // BIO_BABA_TYPE_FRAGMENT_LEFT_LOWER_LEG + BIO_DEKU_BABA_HEAD_LIMB_LEFT_LOWER_EYESTALK, // BIO_BABA_TYPE_FRAGMENT_LEFT_LOWER_EYESTALK + BIO_DEKU_BABA_HEAD_LIMB_LEFT_UPPER_EYESTALK, // BIO_BABA_TYPE_FRAGMENT_LEFT_UPPER_EYESTALK + BIO_DEKU_BABA_HEAD_LIMB_RIGHT_LOWER_EYESTALK, // BIO_BABA_TYPE_FRAGMENT_RIGHT_LOWER_EYESTALK + BIO_DEKU_BABA_HEAD_LIMB_RIGHT_UPPER_EYESTALK, // BIO_BABA_TYPE_FRAGMENT_RIGHT_UPPER_EYESTALK + BIO_DEKU_BABA_HEAD_LIMB_RIGHT_UPPER_LEG, // BIO_BABA_TYPE_FRAGMENT_RIGHT_UPPER_LEG + BIO_DEKU_BABA_HEAD_LIMB_RIGHT_LOWER_LEG, // BIO_BABA_TYPE_FRAGMENT_RIGHT_LOWER_LEG + BIO_DEKU_BABA_HEAD_LIMB_LEAVES, // BIO_BABA_TYPE_FRAGMENT_LEAVES +}; + +s32 Boss05_Fragment_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + Boss05* this = THIS; + + if (limbIndex != sFragmentIndexToLimbIndex[BIO_BABA_GET_FRAGMENT_INDEX(&this->dyna.actor)]) { + *dList = NULL; + } else if (this->fragmentState >= BIO_BABA_FRAGMENT_STATE_ABOVE_WATER) { + rot->x += this->frameCounter * 0x3000; + rot->y += this->frameCounter * 0x1A00; + rot->z += this->frameCounter * 0x2000; + } + + return false; +} + +void Boss05_Fragment_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + Boss05* this = THIS; + + if (limbIndex != sFragmentIndexToLimbIndex[BIO_BABA_GET_FRAGMENT_INDEX(&this->dyna.actor)]) { + Matrix_MultZero(&this->fragmentPos); + } +} + +void Boss05_Draw(Actor* thisx, PlayState* play) { + s32 pad; + Boss05* this = THIS; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + + if (this->actionFunc == Boss05_LilyPadWithHead_Move) { + SkelAnime_DrawFlexOpa(play, this->lilyPadSkelAnime.skeleton, this->lilyPadSkelAnime.jointTable, + this->lilyPadSkelAnime.dListCount, Boss05_LilyPadWithHead_OverrideLimbDraw, + Boss05_LilyPad_PostLimbDraw, &this->dyna.actor); + + if ((this->damagedFlashTimer % 2) != 0) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099); + } + + Matrix_Translate(this->headPos.x, this->headPos.y, this->headPos.z, MTXMODE_NEW); + Matrix_RotateYS(this->headRot.y, MTXMODE_APPLY); + Matrix_RotateXS(this->headRot.x, MTXMODE_APPLY); + Matrix_RotateZS(this->headRot.z, MTXMODE_APPLY); + Matrix_Scale(this->dyna.actor.scale.x, this->dyna.actor.scale.y, this->dyna.actor.scale.z, MTXMODE_APPLY); + + AnimatedMat_Draw(play, Lib_SegmentedToVirtual(gBioDekuBabaHeadEyeFlashTexAnim)); + + SkelAnime_DrawTransformFlexOpa(play, this->headSkelAnime.skeleton, this->headSkelAnime.jointTable, + this->headSkelAnime.dListCount, Boss05_Head_OverrideLimbDraw, + Boss05_Head_PostLimbDraw, Boss05_Head_TransformLimbDraw, &this->dyna.actor); + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_LILY_PAD) { + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateYS(this->lilyPadRotY, MTXMODE_APPLY); + Matrix_RotateXS(this->lilyPadRotX, MTXMODE_APPLY); + Matrix_RotateYS(-this->lilyPadRotY, MTXMODE_APPLY); + Matrix_RotateYS(this->dyna.actor.shape.rot.y, MTXMODE_APPLY); + Matrix_RotateXS(this->dyna.actor.shape.rot.x, MTXMODE_APPLY); + Matrix_RotateZS(this->dyna.actor.shape.rot.z, MTXMODE_APPLY); + Matrix_Scale(this->dyna.actor.scale.x, this->dyna.actor.scale.y, this->dyna.actor.scale.z, MTXMODE_APPLY); + + SkelAnime_DrawFlexOpa(play, this->lilyPadSkelAnime.skeleton, this->lilyPadSkelAnime.jointTable, + this->lilyPadSkelAnime.dListCount, Boss05_LilyPad_OverrideLimbDraw, NULL, + &this->dyna.actor); + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_FALLING_HEAD) { + SkelAnime_DrawTransformFlexOpa(play, this->lilyPadSkelAnime.skeleton, this->lilyPadSkelAnime.jointTable, + this->lilyPadSkelAnime.dListCount, Boss05_FallingHeadLilyPad_OverrideLimbDraw, + Boss05_LilyPad_PostLimbDraw, Boss05_FallingHeadLilyPad_TransformLimbDraw, + &this->dyna.actor); + + Matrix_Translate(this->headPos.x, this->headPos.y, this->headPos.z, MTXMODE_NEW); + Matrix_RotateYS(this->headRot.y, MTXMODE_APPLY); + Matrix_RotateXS(this->headRot.x, MTXMODE_APPLY); + Matrix_RotateZS(this->headRot.z, MTXMODE_APPLY); + Matrix_Scale(this->dyna.actor.scale.x, this->dyna.actor.scale.y, this->dyna.actor.scale.z, MTXMODE_APPLY); + + AnimatedMat_Draw(play, Lib_SegmentedToVirtual(gBioDekuBabaHeadEyeFlashTexAnim)); + + SkelAnime_DrawTransformFlexOpa(play, this->headSkelAnime.skeleton, this->headSkelAnime.jointTable, + this->headSkelAnime.dListCount, Boss05_Head_OverrideLimbDraw, + Boss05_Head_PostLimbDraw, Boss05_Head_TransformLimbDraw, &this->dyna.actor); + + Actor_DrawDamageEffects(play, &this->dyna.actor, this->bodyPartsPos, BIO_BABA_BODYPART_MAX, + this->drawDmgEffScale, this->drawDmgEffFrozenSteamScale, this->drawDmgEffAlpha, + this->drawDmgEffType); + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) == BIO_BABA_TYPE_WALKING_HEAD) { + AnimatedMat_Draw(play, Lib_SegmentedToVirtual(gBioDekuBabaHeadEyeFlashTexAnim)); + + if ((this->damagedFlashTimer % 2) != 0) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099); + } + + SkelAnime_DrawTransformFlexOpa(play, this->headSkelAnime.skeleton, this->headSkelAnime.jointTable, + this->headSkelAnime.dListCount, Boss05_Head_OverrideLimbDraw, + Boss05_Head_PostLimbDraw, Boss05_Head_TransformLimbDraw, &this->dyna.actor); + + Actor_DrawDamageEffects(play, &this->dyna.actor, this->bodyPartsPos, BIO_BABA_BODYPART_MAX, + this->drawDmgEffScale, this->drawDmgEffFrozenSteamScale, this->drawDmgEffAlpha, + this->drawDmgEffType); + } else if (BIO_BABA_GET_TYPE(&this->dyna.actor) >= BIO_BABA_TYPE_FRAGMENT_LOWER_JAW) { + AnimatedMat_Draw(play, Lib_SegmentedToVirtual(gBioDekuBabaHeadEyeFlashTexAnim)); + + SkelAnime_DrawFlexOpa(play, this->headSkelAnime.skeleton, this->headSkelAnime.jointTable, + this->headSkelAnime.dListCount, Boss05_Fragment_OverrideLimbDraw, + Boss05_Fragment_PostLimbDraw, &this->dyna.actor); + } + + POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_Boss_05/z_boss_05.h b/src/overlays/actors/ovl_Boss_05/z_boss_05.h index 8f46476eae..c0017d80fa 100644 --- a/src/overlays/actors/ovl_Boss_05/z_boss_05.h +++ b/src/overlays/actors/ovl_Boss_05/z_boss_05.h @@ -2,16 +2,119 @@ #define Z_BOSS_05_H #include "global.h" +#include "assets/objects/object_boss05/object_boss05.h" struct Boss05; typedef void (*Boss05ActionFunc)(struct Boss05*, PlayState*); +typedef enum BioDekuBabaType { + /* 0 */ BIO_BABA_TYPE_LILY_PAD_WITH_HEAD, + /* 1 */ BIO_BABA_TYPE_NO_LEAF_LILY_PAD_WITH_HEAD, + /* 2 */ BIO_BABA_TYPE_LILY_PAD, + /* 3 */ BIO_BABA_TYPE_FALLING_HEAD, + /* 4 */ BIO_BABA_TYPE_WALKING_HEAD, + /* 10 */ BIO_BABA_TYPE_FRAGMENT_BASE = 10, + /* 10 */ BIO_BABA_TYPE_FRAGMENT_LOWER_JAW = BIO_BABA_TYPE_FRAGMENT_BASE, + /* 11 */ BIO_BABA_TYPE_FRAGMENT_UPPER_JAW, + /* 12 */ BIO_BABA_TYPE_FRAGMENT_BODY, + /* 13 */ BIO_BABA_TYPE_FRAGMENT_BACK_UPPER_LEG, + /* 14 */ BIO_BABA_TYPE_FRAGMENT_BACK_LOWER_LEG, + /* 15 */ BIO_BABA_TYPE_FRAGMENT_LEFT_UPPER_LEG, + /* 16 */ BIO_BABA_TYPE_FRAGMENT_LEFT_LOWER_LEG, + /* 17 */ BIO_BABA_TYPE_FRAGMENT_LEFT_LOWER_EYESTALK, + /* 18 */ BIO_BABA_TYPE_FRAGMENT_LEFT_UPPER_EYESTALK, + /* 19 */ BIO_BABA_TYPE_FRAGMENT_RIGHT_LOWER_EYESTALK, + /* 20 */ BIO_BABA_TYPE_FRAGMENT_RIGHT_UPPER_EYESTALK, + /* 21 */ BIO_BABA_TYPE_FRAGMENT_RIGHT_UPPER_LEG, + /* 22 */ BIO_BABA_TYPE_FRAGMENT_RIGHT_LOWER_LEG, + /* 23 */ BIO_BABA_TYPE_FRAGMENT_LEAVES, + /* 24 */ BIO_BABA_TYPE_MAX +} BioDekuBabaType; + +typedef enum BioDekuBabaBodyPart { + /* 0 */ BIO_BABA_BODYPART_HEAD, + /* 1 */ BIO_BABA_BODYPART_MAX +} BioDekuBabaBodyPart; + +typedef enum BioDekuBabaHeadCollider { + /* 0 */ BIO_BABA_HEAD_COLLIDER_HEAD, + /* 1 */ BIO_BABA_HEAD_COLLIDER_MAX +} BioDekuBabaHeadCollider; + +typedef enum BioDekuBabaLilyPadCollider { + /* 0 */ BIO_BABA_LILY_PAD_COLLIDER_UPPER_STEM, + /* 1 */ BIO_BABA_LILY_PAD_COLLIDER_MIDDLE_STEM, + /* 2 */ BIO_BABA_LILY_PAD_COLLIDER_MAX +} BioDekuBabaLilyPadCollider; + +typedef enum BioDekuBabaLilyPadWithHeadLimbRotIndex { + /* -1 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_NONE = -1, + /* 0 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_UPPER_STEM, + /* 1 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MIDDLE_STEM, + /* 2 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LOWER_STEM, + /* 3 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_UPPER_ARM, + /* 4 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_LEFT_LOWER_ARM, + /* 5 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_UPPER_ARM, + /* 6 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_RIGHT_LOWER_ARM, + /* 7 */ BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MAX +} BioDekuBabaLilyPadWithHeadLimbRotIndex; + +#define BIO_BABA_GET_TYPE(thisx) ((thisx)->params) +#define BIO_BABA_GET_FRAGMENT_INDEX(thisx) ((thisx)->params - BIO_BABA_TYPE_FRAGMENT_BASE) +#define BIO_BABA_FORCE_DETACH_TIMER(thisx) ((thisx)->world.rot.z) + typedef struct Boss05 { - /* 0x000 */ Actor actor; - /* 0x144 */ char unk_144[0x2C]; + /* 0x000 */ DynaPolyActor dyna; + /* 0x15C */ union { + u8 lilyPadWithHeadAttackState; + u8 fragmentState; + }; + /* 0x15E */ s16 flipAttackFrameCounter; + /* 0x160 */ s16 frameCounter; + /* 0x162 */ s16 timers[3]; + /* 0x168 */ s16 forceDetachTimer; + /* 0x16A */ s16 damagedTimer; + /* 0x16C */ s16 damagedFlashTimer; /* 0x170 */ Boss05ActionFunc actionFunc; - /* 0x174 */ char unk_174[0x394]; + /* 0x174 */ u8 lilyPadWithHeadMovementState; + /* 0x176 */ s16 lilyPadRotY; + /* 0x178 */ s16 lilyPadRotX; + /* 0x17A */ s16 drawDmgEffTimer; + /* 0x17C */ f32 drawDmgEffScale; + /* 0x180 */ f32 drawDmgEffFrozenSteamScale; + /* 0x184 */ f32 drawDmgEffAlpha; + /* 0x188 */ u8 drawDmgEffState; + /* 0x189 */ u8 drawDmgEffType; + /* 0x18C */ Vec3f bodyPartsPos[BIO_BABA_BODYPART_MAX]; + /* 0x198 */ f32 fallingHeadLilyPadLimbScale; + /* 0x19C */ s16 lilyPadWithHeadStemRotX; + /* 0x19E */ Vec3s lilyPadWithHeadLimbRot[BIO_BABA_LILY_PAD_WITH_HEAD_LIMB_ROT_INDEX_MAX]; + /* 0x1C8 */ ColliderJntSph lilyPadCollider; + /* 0x1E8 */ ColliderJntSphElement lilyPadColliderElements[BIO_BABA_LILY_PAD_COLLIDER_MAX]; + /* 0x268 */ SkelAnime lilyPadSkelAnime; + /* 0x2AC */ Vec3s lilyPadJointTable[BIO_DEKU_BABA_LILY_PAD_LIMB_MAX]; + /* 0x2E8 */ Vec3s lilyPadMorphTable[BIO_DEKU_BABA_LILY_PAD_LIMB_MAX]; + /* 0x324 */ union { + Vec3f headPos; + Vec3f fragmentPos; + }; + /* 0x330 */ Vec3s headRot; + /* 0x338 */ f32 knockbackMagnitude; + /* 0x33C */ Vec3f knockbackVelocity; + /* 0x348 */ s16 knockbackAngle; + /* 0x34C */ Vec3f walkTargetPos; + /* 0x35C */ f32 walkAngularVelocityY; + /* 0x35C */ f32 lowerJawScaleXZ; + /* 0x360 */ f32 headBodyScale; + /* 0x364 */ f32 headLimbScale; + /* 0x368 */ ColliderJntSph headCollider; + /* 0x388 */ ColliderJntSphElement headColliderElements[BIO_BABA_HEAD_COLLIDER_MAX]; + /* 0x3C8 */ SkelAnime headSkelAnime; + /* 0x40C */ Vec3s headJointTable[BIO_DEKU_BABA_HEAD_LIMB_MAX]; + /* 0x484 */ Vec3s headMorphTable[BIO_DEKU_BABA_HEAD_LIMB_MAX]; + /* 0x4FC */ f32 animEndFrame; + /* 0x500 */ Vec3s fragmentAngularVelocity; } Boss05; // size = 0x508 #endif // Z_BOSS_05_H diff --git a/src/overlays/actors/ovl_En_Pp/z_en_pp.c b/src/overlays/actors/ovl_En_Pp/z_en_pp.c index 30613db970..fee468c428 100644 --- a/src/overlays/actors/ovl_En_Pp/z_en_pp.c +++ b/src/overlays/actors/ovl_En_Pp/z_en_pp.c @@ -34,8 +34,8 @@ void EnPp_Damaged(EnPp* this, PlayState* play); void EnPp_SetupDead(EnPp* this, PlayState* play); void EnPp_Dead(EnPp* this, PlayState* play); void EnPp_Mask_Detach(EnPp* this, PlayState* play); -void EnPp_BodyPart_SetupMove(EnPp* this); -void EnPp_BodyPart_Move(EnPp* this, PlayState* play); +void EnPp_Fragment_SetupMove(EnPp* this); +void EnPp_Fragment_Move(EnPp* this, PlayState* play); typedef enum { /* 0 */ EN_PP_COLLISION_RESULT_OK, @@ -66,7 +66,7 @@ typedef enum { /* 2 */ EN_PP_MASK_DETACH_STATE_DIE } EnPpMaskDetachState; -static s32 sCurrentDeadBodyPartIndex = 0; +static s32 sCurrentFragmentIndex = 0; typedef enum { /* 0x0 */ EN_PP_DMGEFF_JUMP, // Forces the Hiploop to jump @@ -233,12 +233,12 @@ void EnPp_Init(Actor* thisx, PlayState* play) { this->actor.params = EN_PP_TYPE_MASKED; } - if (EN_PP_GET_TYPE(&this->actor) >= EN_PP_TYPE_BODY_PART_BASE) { - this->deadBodyPartIndex = sCurrentDeadBodyPartIndex; - sCurrentDeadBodyPartIndex++; + if (EN_PP_GET_TYPE(&this->actor) >= EN_PP_TYPE_FRAGMENT_BASE) { + this->fragmentIndex = sCurrentFragmentIndex; + sCurrentFragmentIndex++; this->actor.shape.rot.y = this->actor.world.rot.y; Actor_SetScale(&this->actor, 0.03f); - EnPp_BodyPart_SetupMove(this); + EnPp_Fragment_SetupMove(this); } else { Collider_InitAndSetJntSph(play, &this->maskCollider, &this->actor, &sMaskColliderJntSphInit, this->maskColliderElements); @@ -304,7 +304,7 @@ void EnPp_Init(Actor* thisx, PlayState* play) { void EnPp_Destroy(Actor* thisx, PlayState* play) { EnPp* this = THIS; - if (EN_PP_GET_TYPE(&this->actor) < EN_PP_TYPE_BODY_PART_BASE) { + if (EN_PP_GET_TYPE(&this->actor) < EN_PP_TYPE_FRAGMENT_BASE) { Collider_DestroyJntSph(play, &this->maskCollider); Collider_DestroyJntSph(play, &this->bodyCollider); Collider_DestroyQuad(play, &this->hornCollider); @@ -1175,26 +1175,26 @@ void EnPp_Mask_Detach(EnPp* this, PlayState* play) { } } -void EnPp_BodyPart_SetupMove(EnPp* this) { +void EnPp_Fragment_SetupMove(EnPp* this) { EnPp_ChangeAnim(this, EN_PP_ANIM_DAMAGE); this->actor.velocity.y = Rand_ZeroFloat(5.0f) + 13.0f; this->actor.gravity = -2.0f; this->timer = Rand_S16Offset(30, 30); - this->deadBodyPartAngularVelocity.x = (this->deadBodyPartIndex * 0x2E) + 0xFF00; - this->deadBodyPartAngularVelocity.z = (this->deadBodyPartIndex * 0x2E) + 0xFF00; - if (EN_PP_GET_TYPE(&this->actor) != EN_PP_TYPE_BODY_PART_BODY) { + this->fragmentAngularVelocity.x = (this->fragmentIndex * 0x2E) + 0xFF00; + this->fragmentAngularVelocity.z = (this->fragmentIndex * 0x2E) + 0xFF00; + if (EN_PP_GET_TYPE(&this->actor) != EN_PP_TYPE_FRAGMENT_BODY) { this->actor.speed = Rand_ZeroFloat(4.0f) + 4.0f; - this->actor.world.rot.y = ((s32)Rand_CenteredFloat(223.0f) + 0x1999) * this->deadBodyPartIndex; + this->actor.world.rot.y = ((s32)Rand_CenteredFloat(223.0f) + 0x1999) * this->fragmentIndex; } this->action = EN_PP_ACTION_BODY_PART_MOVE; - this->actionFunc = EnPp_BodyPart_Move; + this->actionFunc = EnPp_Fragment_Move; } /** - * Makes the body part fly through the air. If it touches water, it will make a splash. + * Makes the fragment fly through the air. If it touches water, it will make a splash. */ -void EnPp_BodyPart_Move(EnPp* this, PlayState* play) { +void EnPp_Fragment_Move(EnPp* this, PlayState* play) { s32 pad; Vec3f splashPos; WaterBox* waterBox; @@ -1202,26 +1202,29 @@ void EnPp_BodyPart_Move(EnPp* this, PlayState* play) { s32 i; SkelAnime_Update(&this->skelAnime); - if (EN_PP_GET_TYPE(&this->actor) == EN_PP_TYPE_BODY_PART_BODY) { - this->deadBodyPartCount = EN_PP_DEAD_BODYPART_MAX; - for (i = 0; i < EN_PP_DEAD_BODYPART_MAX; i++) { - Math_Vec3f_Copy(&this->deadBodyPartsPos[i], &this->deadBodyPartPos); - this->deadBodyPartsPos[i].x += Math_SinS(0xCCC * i) * 15.0f; - this->deadBodyPartsPos[i].y += -5.0f; - this->deadBodyPartsPos[i].z += Math_CosS(0xCCC * i) * 15.0f; + + // Updates the positions of the blue flames for this fragment. The body fragment has 10 flames, while all other + // fragments only have a single flame. + if (EN_PP_GET_TYPE(&this->actor) == EN_PP_TYPE_FRAGMENT_BODY) { + this->fragmentFlameCount = ARRAY_COUNT(this->fragmentFlamesPos); + for (i = 0; i < ARRAY_COUNT(this->fragmentFlamesPos); i++) { + Math_Vec3f_Copy(&this->fragmentFlamesPos[i], &this->fragmentPos); + this->fragmentFlamesPos[i].x += Math_SinS(0xCCC * i) * 15.0f; + this->fragmentFlamesPos[i].y += -5.0f; + this->fragmentFlamesPos[i].z += Math_CosS(0xCCC * i) * 15.0f; } } else { - Math_Vec3f_Copy(&this->deadBodyPartsPos[0], &this->deadBodyPartPos); - this->deadBodyPartCount = 1; - this->actor.shape.rot.x += this->deadBodyPartAngularVelocity.x; - this->actor.shape.rot.z += this->deadBodyPartAngularVelocity.z; + Math_Vec3f_Copy(&this->fragmentFlamesPos[0], &this->fragmentPos); + this->fragmentFlameCount = 1; + this->actor.shape.rot.x += this->fragmentAngularVelocity.x; + this->actor.shape.rot.z += this->fragmentAngularVelocity.z; } if (WaterBox_GetSurface1(play, &play->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, &waterSurface, &waterBox) && (this->actor.world.pos.y < (waterSurface + 5.0f))) { this->timer = 0; - if (EN_PP_GET_TYPE(&this->actor) == EN_PP_TYPE_BODY_PART_BODY) { + if (EN_PP_GET_TYPE(&this->actor) == EN_PP_TYPE_FRAGMENT_BODY) { for (i = 0; i < 6; i++) { Math_Vec3f_Copy(&splashPos, &this->actor.world.pos); splashPos.x += Rand_CenteredFloat(10 + (5 * i)); @@ -1485,7 +1488,7 @@ s32 EnPp_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* po pos->y += this->maskPos.y; pos->z += this->maskPos.z; } - } else if ((limbIndex + EN_PP_TYPE_BODY_PART_BASE) != EN_PP_GET_TYPE(&this->actor)) { + } else if ((limbIndex + EN_PP_TYPE_FRAGMENT_BASE) != EN_PP_GET_TYPE(&this->actor)) { *dList = NULL; } @@ -1538,9 +1541,9 @@ void EnPp_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, } } } else { - if ((EN_PP_GET_TYPE(&this->actor) >= EN_PP_TYPE_BODY_PART_BASE) && - ((limbIndex + EN_PP_TYPE_BODY_PART_BASE) == EN_PP_GET_TYPE(&this->actor))) { - Matrix_MultVec3f(&gZeroVec3f, &this->deadBodyPartPos); + if ((EN_PP_GET_TYPE(&this->actor) >= EN_PP_TYPE_FRAGMENT_BASE) && + ((limbIndex + EN_PP_TYPE_FRAGMENT_BASE) == EN_PP_GET_TYPE(&this->actor))) { + Matrix_MultVec3f(&gZeroVec3f, &this->fragmentPos); } } @@ -1561,15 +1564,16 @@ void EnPp_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, this->bodyPartIndex = 0; } - if ((this->action == EN_PP_ACTION_SPAWN_BODY_PARTS) && (this->deadBodyPartsSpawnedCount < 6) && + if ((this->action == EN_PP_ACTION_SPAWN_BODY_PARTS) && (this->fragmentsSpawnedCount < 6) && ((limbIndex == HIPLOOP_LIMB_BODY) || (limbIndex == HIPLOOP_LIMB_FRONT_LEFT_LOWER_LEG) || (limbIndex == HIPLOOP_LIMB_FRONT_RIGHT_LOWER_LEG) || (limbIndex == HIPLOOP_LIMB_LEFT_WING_MIDDLE) || (limbIndex == HIPLOOP_LIMB_RIGHT_WING_MIDDLE) || (limbIndex == HIPLOOP_LIMB_CENTER_WING_MIDDLE))) { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_PP, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, this->actor.world.rot.x, this->actor.world.rot.y, - this->actor.world.rot.z, limbIndex + 7); - this->deadBodyPartsSpawnedCount++; - if (this->deadBodyPartsSpawnedCount >= 6) { + this->actor.world.rot.z, limbIndex + EN_PP_TYPE_FRAGMENT_BASE); + + this->fragmentsSpawnedCount++; + if (this->fragmentsSpawnedCount >= 6) { this->action = EN_PP_ACTION_DONE_SPAWNING_BODY_PARTS; } } @@ -1589,14 +1593,14 @@ void EnPp_Draw(Actor* thisx, PlayState* play) { SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, EnPp_OverrideLimbDraw, EnPp_PostLimbDraw, &this->actor); - if (this->deadBodyPartCount != 0) { + if (this->fragmentFlameCount != 0) { scale = 0.4f; - if (EN_PP_GET_TYPE(&this->actor) == EN_PP_TYPE_BODY_PART_BODY) { + if (EN_PP_GET_TYPE(&this->actor) == EN_PP_TYPE_FRAGMENT_BODY) { scale = 0.6f; } - Actor_DrawDamageEffects(play, &this->actor, this->deadBodyPartsPos, this->deadBodyPartCount, scale, scale, 1.0f, - ACTOR_DRAW_DMGEFF_BLUE_FIRE); + Actor_DrawDamageEffects(play, &this->actor, this->fragmentFlamesPos, this->fragmentFlameCount, scale, scale, + 1.0f, ACTOR_DRAW_DMGEFF_BLUE_FIRE); } if (this->drawDmgEffTimer != 0) { diff --git a/src/overlays/actors/ovl_En_Pp/z_en_pp.h b/src/overlays/actors/ovl_En_Pp/z_en_pp.h index 6491227d56..50ac30e515 100644 --- a/src/overlays/actors/ovl_En_Pp/z_en_pp.h +++ b/src/overlays/actors/ovl_En_Pp/z_en_pp.h @@ -16,44 +16,30 @@ typedef enum { /* 0 */ EN_PP_TYPE_MASKED, /* 1 */ EN_PP_TYPE_NO_MASK, /* 2 */ EN_PP_TYPE_UNMASKED, - /* 7 */ EN_PP_TYPE_BODY_PART_BASE = 7, - /* 8 */ EN_PP_TYPE_BODY_PART_BODY, - /* 10 */ EN_PP_TYPE_BODY_PART_FRONT_LEFT_LOWER_LEG = 10, - /* 13 */ EN_PP_TYPE_BODY_PART_FRONT_RIGHT_LOWER_LEG = 13, - /* 17 */ EN_PP_TYPE_BODY_PART_LEFT_WING_MIDDLE = 17, - /* 23 */ EN_PP_TYPE_BODY_PART_RIGHT_WING_MIDDLE = 23, - /* 26 */ EN_PP_TYPE_BODY_PART_CENTER_WING_MIDDLE = 26 + /* 7 */ EN_PP_TYPE_FRAGMENT_BASE = 7, + /* 8 */ EN_PP_TYPE_FRAGMENT_BODY = HIPLOOP_LIMB_BODY + EN_PP_TYPE_FRAGMENT_BASE, + /* 10 */ EN_PP_TYPE_FRAGMENT_FRONT_LEFT_LOWER_LEG = HIPLOOP_LIMB_FRONT_LEFT_LOWER_LEG + EN_PP_TYPE_FRAGMENT_BASE, + /* 13 */ EN_PP_TYPE_FRAGMENT_FRONT_RIGHT_LOWER_LEG = HIPLOOP_LIMB_FRONT_RIGHT_LOWER_LEG + EN_PP_TYPE_FRAGMENT_BASE, + /* 17 */ EN_PP_TYPE_FRAGMENT_LEFT_WING_MIDDLE = HIPLOOP_LIMB_LEFT_WING_MIDDLE + EN_PP_TYPE_FRAGMENT_BASE, + /* 23 */ EN_PP_TYPE_FRAGMENT_RIGHT_WING_MIDDLE = HIPLOOP_LIMB_RIGHT_WING_MIDDLE + EN_PP_TYPE_FRAGMENT_BASE, + /* 26 */ EN_PP_TYPE_FRAGMENT_CENTER_WING_MIDDLE = HIPLOOP_LIMB_CENTER_WING_MIDDLE + EN_PP_TYPE_FRAGMENT_BASE } EnPpType; typedef enum EnPpBodyPart { - /* 0 */ EN_PP_BODYPART_0, - /* 1 */ EN_PP_BODYPART_1, - /* 2 */ EN_PP_BODYPART_2, - /* 3 */ EN_PP_BODYPART_3, - /* 4 */ EN_PP_BODYPART_4, - /* 5 */ EN_PP_BODYPART_5, - /* 6 */ EN_PP_BODYPART_6, - /* 7 */ EN_PP_BODYPART_7, - /* 8 */ EN_PP_BODYPART_8, - /* 9 */ EN_PP_BODYPART_9, - /* 10 */ EN_PP_BODYPART_10, + /* 0 */ EN_PP_BODYPART_BODY, + /* 1 */ EN_PP_BODYPART_FRONT_LEFT_LOWER_LEG, + /* 2 */ EN_PP_BODYPART_FRONT_LEFT_UPPER_LEG, + /* 3 */ EN_PP_BODYPART_LEFT_WING_MIDDLE, + /* 4 */ EN_PP_BODYPART_BACK_RIGHT_LOWER_LEG, + /* 5 */ EN_PP_BODYPART_RIGHT_WING_MIDDLE, + /* 6 */ EN_PP_BODYPART_CENTER_WING_BASE, + /* 7 */ EN_PP_BODYPART_CENTER_WING_MIDDLE, + /* 8 */ EN_PP_BODYPART_BACK_LEFT_LOWER_LEG, + /* 9 */ EN_PP_BODYPART_RIGHT_EYE, + /* 10 */ EN_PP_BODYPART_LEFT_EYE, /* 11 */ EN_PP_BODYPART_MAX } EnPpBodyPart; -typedef enum EnPpDeadBodyPart { - /* 0 */ EN_PP_DEAD_BODYPART_0, - /* 1 */ EN_PP_DEAD_BODYPART_1, - /* 2 */ EN_PP_DEAD_BODYPART_2, - /* 3 */ EN_PP_DEAD_BODYPART_3, - /* 4 */ EN_PP_DEAD_BODYPART_4, - /* 5 */ EN_PP_DEAD_BODYPART_5, - /* 6 */ EN_PP_DEAD_BODYPART_6, - /* 7 */ EN_PP_DEAD_BODYPART_7, - /* 8 */ EN_PP_DEAD_BODYPART_8, - /* 9 */ EN_PP_DEAD_BODYPART_9, - /* 10 */ EN_PP_DEAD_BODYPART_MAX -} EnPpDeadBodyPart; - typedef struct EnPp { /* 0x000 */ Actor actor; /* 0x144 */ SkelAnime skelAnime; @@ -61,7 +47,7 @@ typedef struct EnPp { /* 0x224 */ Vec3s morphTable[HIPLOOP_LIMB_MAX]; /* 0x2C0 */ EnPpActionFunc actionFunc; /* 0x2C4 */ s16 chargesInStraightLines; // If false, the Hiploop will instead charge directly at the player. - /* 0x2C6 */ s16 deadBodyPartIndex; + /* 0x2C6 */ s16 fragmentIndex; /* 0x2C8 */ s16 action; /* 0x2CA */ s16 timer; /* 0x2CC */ s16 secondaryTimer; @@ -74,7 +60,7 @@ typedef struct EnPp { s16 maskDetachState; } actionVar; /* 0x2D4 */ s16 maskBounceAngularVelocity; // Controls the speed that the Hiploop rotates to face the player when an attack bounces off the mask. - /* 0x2D6 */ s16 deadBodyPartsSpawnedCount; + /* 0x2D6 */ s16 fragmentsSpawnedCount; /* 0x2D8 */ f32 animEndFrame; /* 0x2DC */ f32 chargeAndBounceSpeed; /* 0x2DC */ Vec3f ledgeCheckPos[3]; @@ -85,10 +71,10 @@ typedef struct EnPp { /* 0x340 */ Vec3f maskRot; /* 0x34C */ Vec3f maskVelocity; /* 0x358 */ Vec3f targetPos; - /* 0x364 */ Vec3f deadBodyPartPos; - /* 0x370 */ s32 deadBodyPartCount; - /* 0x374 */ Vec3f deadBodyPartsPos[EN_PP_DEAD_BODYPART_MAX]; - /* 0x3EC */ Vec3s deadBodyPartAngularVelocity; + /* 0x364 */ Vec3f fragmentPos; + /* 0x370 */ s32 fragmentFlameCount; + /* 0x374 */ Vec3f fragmentFlamesPos[10]; + /* 0x3EC */ Vec3s fragmentAngularVelocity; /* 0x3F2 */ s16 drawDmgEffTimer; /* 0x3F4 */ s16 drawDmgEffType; /* 0x3F8 */ f32 drawDmgEffScale; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 7054d45144..96f0784a01 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -9378,47 +9378,47 @@ 0x809EDCCC:("Boss04_OverrideLimbDraw",), 0x809EDECC:("Boss04_PostLimbDraw",), 0x809EDF58:("Boss04_Draw",), - 0x809EE4E0:("func_809EE4E0",), - 0x809EE668:("func_809EE668",), + 0x809EE4E0:("Boss05_WalkingHead_Thaw",), + 0x809EE668:("Boss05_SetColliderSphere",), 0x809EE6F8:("Boss05_Init",), 0x809EEC70:("Boss05_Destroy",), - 0x809EECBC:("func_809EECBC",), - 0x809EEDD0:("func_809EEDD0",), - 0x809EEDE8:("func_809EEDE8",), - 0x809EF9BC:("func_809EF9BC",), - 0x809EFAB4:("func_809EFAB4",), - 0x809EFE50:("func_809EFE50",), - 0x809F0014:("func_809F0014",), - 0x809F0058:("func_809F0058",), - 0x809F00CC:("func_809F00CC",), - 0x809F010C:("func_809F010C",), - 0x809F01CC:("func_809F01CC",), - 0x809F0244:("func_809F0244",), - 0x809F02D0:("func_809F02D0",), - 0x809F0374:("func_809F0374",), - 0x809F0474:("func_809F0474",), - 0x809F04C0:("func_809F04C0",), - 0x809F0538:("func_809F0538",), - 0x809F0590:("func_809F0590",), - 0x809F0650:("func_809F0650",), - 0x809F06B8:("func_809F06B8",), - 0x809F0708:("func_809F0708",), - 0x809F0780:("func_809F0780",), - 0x809F0A0C:("func_809F0A0C",), - 0x809F0A64:("func_809F0A64",), - 0x809F0ABC:("func_809F0ABC",), - 0x809F0B0C:("func_809F0B0C",), + 0x809EECBC:("Boss05_LilyPadWithHead_UpdateDamage",), + 0x809EEDD0:("Boss05_LilyPadWithHead_SetupMove",), + 0x809EEDE8:("Boss05_LilyPadWithHead_Move",), + 0x809EF9BC:("Boss05_LilyPad_Idle",), + 0x809EFAB4:("Boss05_FallingHead_Fall",), + 0x809EFE50:("Boss05_WalkingHead_UpdateDamage",), + 0x809F0014:("Boss05_WalkingHead_IsLookingAtPlayer",), + 0x809F0058:("Boss05_WalkingHead_TrySpottingPlayer",), + 0x809F00CC:("Boss05_WalkingHead_SetupTransform",), + 0x809F010C:("Boss05_WalkingHead_Transform",), + 0x809F01CC:("Boss05_WalkingHead_SetupIdle",), + 0x809F0244:("Boss05_WalkingHead_Idle",), + 0x809F02D0:("Boss05_WalkingHead_SetupWalk",), + 0x809F0374:("Boss05_WalkingHead_Walk",), + 0x809F0474:("Boss05_WalkingHead_SetupSpottedPlayer",), + 0x809F04C0:("Boss05_WalkingHead_SpottedPlayer",), + 0x809F0538:("Boss05_WalkingHead_SetupCharge",), + 0x809F0590:("Boss05_WalkingHead_Charge",), + 0x809F0650:("Boss05_WalkingHead_SetupAttack",), + 0x809F06B8:("Boss05_WalkingHead_Attack",), + 0x809F0708:("Boss05_WalkingHead_SetupDamaged",), + 0x809F0780:("Boss05_WalkingHead_Damaged",), + 0x809F0A0C:("Boss05_WalkingHead_SetupStunned",), + 0x809F0A64:("Boss05_WalkingHead_SetupFreeze",), + 0x809F0ABC:("Boss05_WalkingHead_Stunned",), + 0x809F0B0C:("Boss05_Fragment_Move",), 0x809F0CCC:("Boss05_Update",), - 0x809F1050:("func_809F1050",), - 0x809F1170:("func_809F1170",), - 0x809F1284:("func_809F1284",), - 0x809F12A0:("func_809F12A0",), - 0x809F135C:("func_809F135C",), - 0x809F1404:("func_809F1404",), - 0x809F1430:("func_809F1430",), - 0x809F1464:("func_809F1464",), - 0x809F14AC:("func_809F14AC",), - 0x809F1550:("func_809F1550",), + 0x809F1050:("Boss05_LilyPadWithHead_OverrideLimbDraw",), + 0x809F1170:("Boss05_LilyPad_PostLimbDraw",), + 0x809F1284:("Boss05_Head_OverrideLimbDraw",), + 0x809F12A0:("Boss05_Head_PostLimbDraw",), + 0x809F135C:("Boss05_Head_TransformLimbDraw",), + 0x809F1404:("Boss05_LilyPad_OverrideLimbDraw",), + 0x809F1430:("Boss05_FallingHeadLilyPad_OverrideLimbDraw",), + 0x809F1464:("Boss05_FallingHeadLilyPad_TransformLimbDraw",), + 0x809F14AC:("Boss05_Fragment_OverrideLimbDraw",), + 0x809F1550:("Boss05_Fragment_PostLimbDraw",), 0x809F159C:("Boss05_Draw",), 0x809F2120:("func_809F2120",), 0x809F2140:("func_809F2140",), @@ -13227,8 +13227,8 @@ 0x80B1FC7C:("EnPp_Dead",), 0x80B1FF20:("EnPp_Mask_SetupDetach",), 0x80B20030:("EnPp_Mask_Detach",), - 0x80B202B8:("EnPp_BodyPart_SetupMove",), - 0x80B203BC:("EnPp_BodyPart_Move",), + 0x80B202B8:("EnPp_Fragment_SetupMove",), + 0x80B203BC:("EnPp_Fragment_Move",), 0x80B20668:("EnPp_UpdateDamage",), 0x80B20B40:("EnPp_Update",), 0x80B20E6C:("EnPp_OverrideLimbDraw",), diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index 4ae8addaa8..e9cffb7360 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -10196,25 +10196,25 @@ 0x809F1A30:("D_809F1A30","UNK_TYPE1","",0x1), 0x809F1AD0:("D_809F1AD0","UNK_TYPE1","",0x1), 0x809F1B00:("D_809F1B00","UNK_TYPE1","",0x1), - 0x809F1B2C:("D_809F1B2C","UNK_TYPE1","",0x1), - 0x809F1B74:("D_809F1B74","UNK_TYPE1","",0x1), - 0x809F1B84:("D_809F1B84","UNK_TYPE1","",0x1), - 0x809F1BA8:("D_809F1BA8","UNK_TYPE1","",0x1), - 0x809F1BB8:("D_809F1BB8","UNK_TYPE1","",0x1), - 0x809F1BDC:("D_809F1BDC","UNK_TYPE1","",0x1), - 0x809F1BEC:("D_809F1BEC","UNK_TYPE1","",0x1), - 0x809F1BF0:("D_809F1BF0","UNK_TYPE1","",0x1), - 0x809F1BF4:("D_809F1BF4","UNK_TYPE1","",0x1), - 0x809F1C00:("D_809F1C00","UNK_TYPE1","",0x1), - 0x809F1C20:("D_809F1C20","UNK_TYPE1","",0x1), - 0x809F1C40:("Boss_05_InitVars","UNK_TYPE1","",0x1), - 0x809F1C60:("D_809F1C60","UNK_TYPE2","",0x2), - 0x809F1C8C:("D_809F1C8C","UNK_TYPE2","",0x2), - 0x809F1CB8:("D_809F1CB8","UNK_TYPE1","",0x1), - 0x809F1CC0:("D_809F1CC0","UNK_TYPE1","",0x1), - 0x809F1CC4:("D_809F1CC4","UNK_TYPE1","",0x1), - 0x809F1CD0:("D_809F1CD0","UNK_TYPE1","",0x1), - 0x809F1CDC:("D_809F1CDC","UNK_TYPE1","",0x1), + 0x809F1B2C:("sLilyPadJntSphElementsInit","ColliderJntSphElementInit","[2]",0x48), + 0x809F1B74:("sLilyPadJntSphInit","ColliderJntSphInit","",0x10), + 0x809F1B84:("sHeadJntSphElementsInit","ColliderJntSphElementInit","[1]",0x24), + 0x809F1BA8:("sHeadJntSphInit","ColliderJntSphInit","",0x10), + 0x809F1BB8:("sWalkingHeadJntSphElementsInit","ColliderJntSphElementInit","[1]",0x24), + 0x809F1BDC:("sWalkingHeadJntSphInit","ColliderJntSphInit","",0x10), + 0x809F1BEC:("sIcePrimColor","Color_RGBA8","",0x4), + 0x809F1BF0:("sIceEnvColor","Color_RGBA8","",0x4), + 0x809F1BF4:("sIceAccel","Vec3f","",0xC), + 0x809F1C00:("sLilyPadWithHeadDamageTable","DamageTable","",0x20), + 0x809F1C20:("sWalkingHeadDamageTable","DamageTable","",0x20), + 0x809F1C40:("Boss_05_InitVars","ActorInit","",0x20), + 0x809F1C60:("sWindUpLimbRot","Vec3s","[7]",0x2A), + 0x809F1C8C:("sLungeAttackLimbRot","Vec3s","[7]",0x2A), + 0x809F1CB8:("sLimbIndexToLimbRotIndex","s8","[12]",0xC), + 0x809F1CC4:("sHeadOffset","Vec3f","",0xC), + 0x809F1CD0:("sHeadColliderOffset","Vec3f","",0xC), + 0x809F1CDC:("sHeadOffset","Vec3f","",0xC), + 0x809F1CE8:("sFragmentIndexToLimbIndex","s32","[14]",0x38), 0x809F1D20:("D_809F1D20","f32","",0x4), 0x809F1D24:("D_809F1D24","f32","",0x4), 0x809F1D28:("D_809F1D28","f32","",0x4), @@ -10230,7 +10230,7 @@ 0x809F1DA4:("D_809F1DA4","f32","",0x4), 0x809F1DA8:("D_809F1DA8","f32","",0x4), 0x809F1DAC:("D_809F1DAC","f32","",0x4), - 0x809F2110:("D_809F2110","UNK_TYPE1","",0x1), + 0x809F2110:("sBioDekuBabaHeadColliderPos","Vec3f","",0xC), 0x809F4080:("D_809F4080","UNK_TYPE1","",0x1), 0x809F40A0:("Boss_06_InitVars","UNK_TYPE1","",0x1), 0x809F40C0:("D_809F40C0","UNK_TYPE1","",0x1), @@ -13708,7 +13708,7 @@ 0x80B1DB54:("D_80B1DB54","f32","",0x4), 0x80B1DB58:("D_80B1DB58","f32","",0x4), 0x80B1DB5C:("D_80B1DB5C","f32","",0x4), - 0x80B21620:("sCurrentDeadBodyPartIndex","UNK_TYPE4","",0x4), + 0x80B21620:("sCurrentFragmentIndex","UNK_TYPE4","",0x4), 0x80B21624:("sDamageTable","UNK_TYPE1","",0x1), 0x80B21644:("En_Pp_InitVars","UNK_TYPE1","",0x1), 0x80B21664:("sMaskColliderJntSphElementsInit","UNK_TYPE1","",0x1), diff --git a/undefined_syms.txt b/undefined_syms.txt index 4dc64eb0d2..61a689856a 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -196,21 +196,6 @@ D_060005C4 = 0x060005C4; D_06000A20 = 0x06000A20; D_06000040 = 0x06000040; -// ovl_Boss_05 - -D_060006A4 = 0x060006A4; -D_06000A5C = 0x06000A5C; -D_06000ABC = 0x06000ABC; -D_060024E0 = 0x060024E0; -D_06002F0C = 0x06002F0C; -D_06003448 = 0x06003448; -D_06006240 = 0x06006240; -D_06006378 = 0x06006378; -D_06006484 = 0x06006484; -D_06006E50 = 0x06006E50; -D_06007488 = 0x06007488; -D_06007908 = 0x06007908; - // ovl_Boss_07 D_06000194 = 0x06000194;