From 384115d2a07c8885a4702f3ec7fa7eac5a3879c1 Mon Sep 17 00:00:00 2001 From: Tharo <17233964+Thar0@users.noreply.github.com> Date: Fri, 21 Jun 2024 02:36:11 +0100 Subject: [PATCH] ovl_En_Death and ovl_En_Minideath (#1645) * En_Death new build system edition * Fix merge * Matched * Fix merge * Some names * Suggested changes * Further suggested changes * Add value comments to MinideathAction --- assets/xml/objects/object_death.xml | 102 +- linker_scripts/undefined_syms.ld | 21 - spec | 7 +- src/overlays/actors/ovl_En_Death/z_en_death.c | 1801 +++++++++++++++-- src/overlays/actors/ovl_En_Death/z_en_death.h | 50 +- .../actors/ovl_En_Minideath/z_en_minideath.c | 928 ++++++++- .../actors/ovl_En_Minideath/z_en_minideath.h | 39 +- tools/disasm/functions.txt | 72 +- 8 files changed, 2641 insertions(+), 379 deletions(-) diff --git a/assets/xml/objects/object_death.xml b/assets/xml/objects/object_death.xml index 7588c484a4..08ebdc3f0f 100644 --- a/assets/xml/objects/object_death.xml +++ b/assets/xml/objects/object_death.xml @@ -1,17 +1,17 @@  - - - - - - - + + + + + + + - - + + @@ -39,47 +39,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linker_scripts/undefined_syms.ld b/linker_scripts/undefined_syms.ld index 880773f840..d92b391d18 100644 --- a/linker_scripts/undefined_syms.ld +++ b/linker_scripts/undefined_syms.ld @@ -242,27 +242,6 @@ D_06004894 = 0x06004894; D_060086BC = 0x060086BC; D_0600C3E0 = 0x0600C3E0; -// ovl_En_Death - -D_06000E64 = 0x06000E64; -D_060015B4 = 0x060015B4; -D_06001834 = 0x06001834; -D_06001F80 = 0x06001F80; -D_06002DE8 = 0x06002DE8; -D_0600352C = 0x0600352C; -D_06003CAC = 0x06003CAC; -D_06006F88 = 0x06006F88; -D_060073D0 = 0x060073D0; -D_06009988 = 0x06009988; -D_06009BA0 = 0x06009BA0; -D_06009F10 = 0x06009F10; -D_0600AD08 = 0x0600AD08; -D_0600B284 = 0x0600B284; -D_0600B508 = 0x0600B508; -D_0600CB2C = 0x0600CB2C; -D_0600CB84 = 0x0600CB84; -D_0600CBC0 = 0x0600CBC0; - // ovl_En_Knight D_060005A8 = 0x060005A8; diff --git a/spec b/spec index 2144cf439a..c9df4a8e1d 100644 --- a/spec +++ b/spec @@ -1046,17 +1046,14 @@ beginseg name "ovl_En_Death" compress include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Death/z_en_death.o" - include "$(BUILD_DIR)/data/ovl_En_Death/ovl_En_Death.data.o" - include "$(BUILD_DIR)/data/ovl_En_Death/ovl_En_Death.reloc.o" + include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Death/ovl_En_Death_reloc.o" endseg beginseg name "ovl_En_Minideath" compress include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Minideath/z_en_minideath.o" - include "$(BUILD_DIR)/data/ovl_En_Minideath/ovl_En_Minideath.data.o" - include "$(BUILD_DIR)/data/ovl_En_Minideath/ovl_En_Minideath.bss.o" - include "$(BUILD_DIR)/data/ovl_En_Minideath/ovl_En_Minideath.reloc.o" + include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Minideath/ovl_En_Minideath_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_En_Death/z_en_death.c b/src/overlays/actors/ovl_En_Death/z_en_death.c index 0eb9268f7d..2f501a35ee 100644 --- a/src/overlays/actors/ovl_En_Death/z_en_death.c +++ b/src/overlays/actors/ovl_En_Death/z_en_death.c @@ -6,38 +6,57 @@ #include "z_en_death.h" #include "z64rumble.h" +#include "overlays/actors/ovl_Arrow_Light/z_arrow_light.h" +#include "objects/gameplay_keep/gameplay_keep.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_10 | ACTOR_FLAG_20 | ACTOR_FLAG_IGNORE_QUAKE) #define THIS ((EnDeath*)thisx) -void EnDeath_Init(Actor* thisx, PlayState* play); +void EnDeath_Init(Actor* thisx, PlayState* play2); void EnDeath_Destroy(Actor* thisx, PlayState* play); void EnDeath_Update(Actor* thisx, PlayState* play); void EnDeath_Draw(Actor* thisx, PlayState* play); -void func_808C589C(EnDeath* this, PlayState* play); -void func_808C5AB8(EnDeath* this, PlayState* play); -void func_808C5CB4(EnDeath* this, PlayState* play); -void func_808C5E90(EnDeath* this, PlayState* play); -void func_808C6070(EnDeath* this, PlayState* play); -void func_808C64DC(EnDeath* this, PlayState* play); -void func_808C66A8(EnDeath* this, PlayState* play); -void func_808C682C(EnDeath* this, PlayState* play); -void func_808C692C(EnDeath* this, PlayState* play); -void func_808C6AB0(EnDeath* this, PlayState* play); -void func_808C6CDC(EnDeath* this, PlayState* play); -void func_808C6F6C(EnDeath* this, PlayState* play); -void func_808C72AC(EnDeath* this, PlayState* play); -void func_808C74F8(EnDeath* this, PlayState* play); -void func_808C7888(EnDeath* this, PlayState* play); -void func_808C7AAC(EnDeath* this, PlayState* play); -void func_808C7B88(EnDeath* this, PlayState* play); -void func_808C7C88(EnDeath* this, PlayState* play); -void func_808C7D34(EnDeath* this, PlayState* play); -void func_808C7DCC(EnDeath* this, PlayState* play); +void EnDeath_IntroCutscenePart1(EnDeath* this, PlayState* play); +void EnDeath_IntroCutscenePart2(EnDeath* this, PlayState* play); +void EnDeath_IntroCutscenePart3(EnDeath* this, PlayState* play); +void EnDeath_IntroCutscenePart4(EnDeath* this, PlayState* play); +void EnDeath_IntroCutscenePart5(EnDeath* this, PlayState* play); +void EnDeath_ApproachPlayer(EnDeath* this, PlayState* play); +void EnDeath_SwingAttack(EnDeath* this, PlayState* play); +void EnDeath_EndSwingAttack(EnDeath* this, PlayState* play); +void EnDeath_BlockProjectile(EnDeath* this, PlayState* play); +void EnDeath_SpinAttack(EnDeath* this, PlayState* play); +void EnDeath_Damaged(EnDeath* this, PlayState* play); +void EnDeath_DeathCutscenePart1(EnDeath* this, PlayState* play); +void EnDeath_DeathCutscenePart2(EnDeath* this, PlayState* play); +void EnDeath_DeathCutscenePart3(EnDeath* this, PlayState* play); +void EnDeath_DeathCutscenePart4(EnDeath* this, PlayState* play); +void EnDeath_Dead(EnDeath* this, PlayState* play); +void EnDeath_StartBatSwarm(EnDeath* this, PlayState* play); +void EnDeath_BatSwarm(EnDeath* this, PlayState* play); +void EnDeath_PlayCutscene(EnDeath* this, PlayState* play); +void EnDeath_BeginWithoutCutscene(EnDeath* this, PlayState* play); + +void EnDeath_SetupIntroCutscenePart2(EnDeath* this, PlayState* play); +void EnDeath_SetupIntroCutscenePart3(EnDeath* this); +void EnDeath_SetupIntroCutscenePart4(EnDeath* this, PlayState* play); +void EnDeath_SetupIntroCutscenePart5(EnDeath* this, PlayState* play); +void EnDeath_SetupApproachPlayer(EnDeath* this); +void EnDeath_SetupSwingAttack(EnDeath* this); +void EnDeath_SetupEndSwingAttack(EnDeath* this); +void EnDeath_SetupBlockProjectile(EnDeath* this); +void EnDeath_SetupSpinAttack(EnDeath* this); +void EnDeath_SetupDeathCutscenePart2(EnDeath* this, PlayState* play); +void EnDeath_SetupDeathCutscenePart3(EnDeath* this); +void EnDeath_SetupDead(EnDeath* this, PlayState* play); +void EnDeath_SetupStartBatSwarm(EnDeath* this); +void EnDeath_SetupBatSwarm(EnDeath* this); +void EnDeath_SetupPlayCutscene(EnDeath* this); +void EnDeath_SetupBeginWithoutCutscene(EnDeath* this); +void EnDeath_SetupDeathCutscenePart4(EnDeath* this); -#if 0 ActorInit En_Death_InitVars = { /**/ ACTOR_EN_DEATH, /**/ ACTORCAT_ENEMY, @@ -50,230 +69,1690 @@ ActorInit En_Death_InitVars = { /**/ EnDeath_Draw, }; -// static ColliderSphereInit sSphereInit = { -static ColliderSphereInit D_808C98E0 = { - { COLTYPE_HIT3, AT_NONE, AC_NONE | AC_TYPE_PLAYER, OC1_NONE, OC2_TYPE_1, COLSHAPE_SPHERE, }, - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, +static ColliderSphereInit sSphereInit = { + { + COLTYPE_HIT3, + AT_NONE, + AC_NONE | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_SPHERE, + }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, { 1, { { 0, 0, 0 }, 22 }, 100 }, }; -// static ColliderCylinderInit sCylinderInit = { -static ColliderCylinderInit D_808C990C = { - { COLTYPE_NONE, AT_NONE, AC_NONE, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, }, - { ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_NONE, OCELEM_ON, }, +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, { 35, 90, 20, { 0, 0, 0 } }, }; -// static ColliderTrisElementInit sTrisElementsInit[2] = { -static ColliderTrisElementInit D_808C9938[2] = { +static ColliderTrisElementInit sTrisElementsInit[2] = { { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x20 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x04, 0x20 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, }, { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x20 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x04, 0x20 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, }, }; -// static ColliderTrisInit sTrisInit = { -static ColliderTrisInit D_808C99B0 = { - { COLTYPE_METAL, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_HARD | AC_TYPE_PLAYER, OC1_NONE, OC2_TYPE_1, COLSHAPE_TRIS, }, - ARRAY_COUNT(sTrisElementsInit), D_808C9938, // sTrisElementsInit, +static ColliderTrisInit sTrisInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_TRIS, + }, + ARRAY_COUNT(sTrisElementsInit), + sTrisElementsInit, }; -// static ColliderQuadInit sQuadInit = { -static ColliderQuadInit D_808C99C0 = { - { COLTYPE_NONE, AT_NONE | AT_TYPE_ENEMY, AC_NONE, OC1_NONE, OC2_TYPE_2, COLSHAPE_QUAD, }, - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x04, 0x20 }, { 0x00000000, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, BUMP_NONE, OCELEM_NONE, }, +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_NONE | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x04, 0x20 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_NONE, + OCELEM_NONE, + }, { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, }; -// static DamageTable sDamageTable = { -static DamageTable D_808C9A10 = { - /* Deku Nut */ DMG_ENTRY(0, 0x0), - /* Deku Stick */ DMG_ENTRY(1, 0x0), - /* Horse trample */ DMG_ENTRY(0, 0x0), - /* Explosives */ DMG_ENTRY(1, 0xF), - /* Zora boomerang */ DMG_ENTRY(1, 0x0), - /* Normal arrow */ DMG_ENTRY(1, 0x0), - /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0), - /* Hookshot */ DMG_ENTRY(1, 0x0), - /* Goron punch */ DMG_ENTRY(1, 0x0), - /* Sword */ DMG_ENTRY(1, 0x0), - /* Goron pound */ DMG_ENTRY(0, 0x0), - /* Fire arrow */ DMG_ENTRY(1, 0x2), - /* Ice arrow */ DMG_ENTRY(1, 0x3), - /* Light arrow */ DMG_ENTRY(2, 0x4), - /* Goron spikes */ DMG_ENTRY(1, 0x0), - /* Deku spin */ DMG_ENTRY(0, 0x0), - /* Deku bubble */ DMG_ENTRY(1, 0x0), - /* Deku launch */ DMG_ENTRY(2, 0x0), - /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0x0), - /* Zora barrier */ DMG_ENTRY(0, 0x0), - /* Normal shield */ DMG_ENTRY(0, 0x0), - /* Light ray */ DMG_ENTRY(0, 0x0), - /* Thrown object */ DMG_ENTRY(1, 0x0), - /* Zora punch */ DMG_ENTRY(1, 0x0), - /* Spin attack */ DMG_ENTRY(1, 0x0), - /* 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), +typedef enum { + /* 0x0 */ DMGEFF_NONE = 0, + /* 0x2 */ DMGEFF_FIRE_ARROW = 2, + /* 0x3 */ DMGEFF_ICE_ARROW, + /* 0x4 */ DMGEFF_LIGHT_ARROW, + /* 0xF */ DMGEFF_EXPLOSIVES = 15 +} EnDeathDamageEffect; + +static DamageTable sDamageTable = { + /* Deku Nut */ DMG_ENTRY(0, DMGEFF_NONE), + /* Deku Stick */ DMG_ENTRY(1, DMGEFF_NONE), + /* Horse trample */ DMG_ENTRY(0, DMGEFF_NONE), + /* Explosives */ DMG_ENTRY(1, DMGEFF_EXPLOSIVES), + /* Zora boomerang */ DMG_ENTRY(1, DMGEFF_NONE), + /* Normal arrow */ DMG_ENTRY(1, DMGEFF_NONE), + /* UNK_DMG_0x06 */ DMG_ENTRY(0, DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(1, DMGEFF_NONE), + /* Goron punch */ DMG_ENTRY(1, DMGEFF_NONE), + /* Sword */ DMG_ENTRY(1, DMGEFF_NONE), + /* Goron pound */ DMG_ENTRY(0, DMGEFF_NONE), + /* Fire arrow */ DMG_ENTRY(1, DMGEFF_FIRE_ARROW), + /* Ice arrow */ DMG_ENTRY(1, DMGEFF_ICE_ARROW), + /* Light arrow */ DMG_ENTRY(2, DMGEFF_LIGHT_ARROW), + /* Goron spikes */ DMG_ENTRY(1, DMGEFF_NONE), + /* Deku spin */ DMG_ENTRY(0, DMGEFF_NONE), + /* Deku bubble */ DMG_ENTRY(1, DMGEFF_NONE), + /* Deku launch */ DMG_ENTRY(2, DMGEFF_NONE), + /* UNK_DMG_0x12 */ DMG_ENTRY(0, DMGEFF_NONE), + /* Zora barrier */ DMG_ENTRY(0, DMGEFF_NONE), + /* Normal shield */ DMG_ENTRY(0, DMGEFF_NONE), + /* Light ray */ DMG_ENTRY(0, DMGEFF_NONE), + /* Thrown object */ DMG_ENTRY(1, DMGEFF_NONE), + /* Zora punch */ DMG_ENTRY(1, DMGEFF_NONE), + /* Spin attack */ DMG_ENTRY(1, DMGEFF_NONE), + /* Sword beam */ DMG_ENTRY(0, DMGEFF_NONE), + /* Normal Roll */ DMG_ENTRY(0, DMGEFF_NONE), + /* UNK_DMG_0x1B */ DMG_ENTRY(0, DMGEFF_NONE), + /* UNK_DMG_0x1C */ DMG_ENTRY(0, DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, DMGEFF_NONE), + /* UNK_DMG_0x1E */ DMG_ENTRY(0, DMGEFF_NONE), + /* Powder Keg */ DMG_ENTRY(1, DMGEFF_EXPLOSIVES), }; -// sColChkInfoInit -static CollisionCheckInfoInit2 D_808C9A30 = { 20, 28, 90, 20, 100 }; +static CollisionCheckInfoInit2 sColChkInfoInit = { 20, 28, 90, 20, 100 }; -// static InitChainEntry sInitChain[] = { -static InitChainEntry D_808C9A60[] = { +static EffectBlureInit2 sBlureInit = { + 0, 8, 0, { 100, 50, 100, 200 }, { 100, 0, 0, 64 }, { 100, 0, 0, 20 }, { 50, 0, 0, 0 }, 8, + 0, 2, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, +}; + +static InitChainEntry sInitChain[] = { ICHAIN_VEC3F(scale, 0, ICHAIN_CONTINUE), ICHAIN_S8(hintId, TATL_HINT_ID_GOMESS, ICHAIN_CONTINUE), ICHAIN_F32(targetArrowOffset, 6000, ICHAIN_CONTINUE), ICHAIN_U8(targetMode, TARGET_MODE_5, ICHAIN_STOP), }; -#endif +void EnDeath_Init(Actor* thisx, PlayState* play2) { + EnDeath* this = THIS; + PlayState* play = play2; + f32 yOffset = 15.0f; + s16 yRot = 0; + s32 i; -extern ColliderSphereInit D_808C98E0; -extern ColliderCylinderInit D_808C990C; -extern ColliderTrisElementInit D_808C9938[2]; -extern ColliderTrisInit D_808C99B0; -extern ColliderQuadInit D_808C99C0; -extern DamageTable D_808C9A10; -extern CollisionCheckInfoInit2 D_808C9A30; -extern InitChainEntry D_808C9A60[]; + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 5500.0f, ActorShadow_DrawCircle, 80.0f); -extern UNK_TYPE D_06000E64; -extern UNK_TYPE D_060015B4; -extern UNK_TYPE D_06001834; -extern UNK_TYPE D_06001F80; -extern UNK_TYPE D_06002DE8; -extern UNK_TYPE D_0600352C; -extern UNK_TYPE D_06003CAC; -extern UNK_TYPE D_06006F88; -extern UNK_TYPE D_060073D0; -extern UNK_TYPE D_06009988; -extern UNK_TYPE D_06009F10; -extern UNK_TYPE D_0600B284; -extern UNK_TYPE D_0600B508; -extern UNK_TYPE D_0600CB2C; + SkelAnime_InitFlex(play, &this->skelAnime, &gGomessSkel, &gGomessFloatAnim, this->jointTable, this->morphTable, + GOMESS_LIMB_MAX); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/EnDeath_Init.s") + Collider_InitAndSetSphere(play, &this->coreCollider, &this->actor, &sSphereInit); + Collider_InitAndSetCylinder(play, &this->bodyCollider, &this->actor, &sCylinderInit); + Collider_InitAndSetQuad(play, &this->weaponCollider, &this->actor, &sQuadInit); + Collider_InitAndSetTris(play, &this->weaponSpinningCollider, &this->actor, &sTrisInit, + this->weaponSpinningColliderElements); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/EnDeath_Destroy.s") + this->coreCollider.dim.worldSphere.radius = this->coreCollider.dim.modelSphere.radius; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5310.s") + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5394.s") + Effect_Add(play, &this->effectIndex, EFFECT_BLURE2, 0, 0, &sBlureInit); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5428.s") + if (!CHECK_EVENTINF(EVENTINF_INTRO_CS_WATCHED_GOMESS)) { + this->actor.world.pos.y += 400.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C54F0.s") + // Spawn each bat as a child + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i] = (EnMinideath*)Actor_SpawnAsChild( + &play->actorCtx, &this->actor, play, ACTOR_EN_MINIDEATH, this->actor.world.pos.x, + this->actor.world.pos.y + yOffset, this->actor.world.pos.z, 0, yRot, 0, i); + if (this->miniDeaths[i] == NULL) { + Actor_Kill(&this->actor); + } + yRot += 0x3A00; + yOffset += 4.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C566C.s") + // Make the ith bat a child of the (i+1)th bat + for (i = 0; i < ARRAY_COUNT(this->miniDeaths) - 1; i++) { + this->miniDeaths[i]->actor.child = &this->miniDeaths[i + 1]->actor; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C571C.s") + this->bodyMatAnim = Lib_SegmentedToVirtual(&gGomessBodyMatAnim); + this->coreMatAnim = Lib_SegmentedToVirtual(&gGomessCoreMatAnim); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C589C.s") + if (CHECK_EVENTINF(EVENTINF_INTRO_CS_WATCHED_GOMESS)) { + // Watched intro cutscene + this->holdsScythe = true; + Actor_SetScale(&this->actor, 0.01f); + this->matAnimStep = 23; + this->actor.params = 20; + this->scytheScale = 1.0f; + EnDeath_SetupBeginWithoutCutscene(this); + } else { + // Start intro cutscene + this->inEarlyIntro = true; + this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + EnDeath_SetupPlayCutscene(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C597C.s") +void EnDeath_Destroy(Actor* thisx, PlayState* play) { + EnDeath* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5AB8.s") + Collider_DestroySphere(play, &this->coreCollider); + Collider_DestroyCylinder(play, &this->bodyCollider); + Collider_DestroyQuad(play, &this->weaponCollider); + Collider_DestroyTris(play, &this->weaponSpinningCollider); + AudioSfx_StopByPos(&this->scytheScreenPos); + Effect_Destroy(play, this->effectIndex); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5C0C.s") +void EnDeath_DimLights(PlayState* play) { + EnvLightSettings* lightSettings; + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5CB4.s") + play->envCtx.lightSettingOverride = 20; + play->envCtx.lightSetting = 20; + play->envCtx.prevLightSetting = 20; + play->envCtx.lightBlend = 1.0f; + lightSettings = &play->envCtx.lightSettingsList[20]; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5D6C.s") + for (i = 0; i < 3; i++) { + lightSettings->light1Dir[i] = play->envCtx.lightSettings.light1Dir[i]; + lightSettings->light2Dir[i] = play->envCtx.lightSettings.light2Dir[i]; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5E90.s") +void EnDeath_Float(EnDeath* this) { + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_SHARP_FLOAT - SFX_FLAG); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C5F58.s") + if (this->floatTimer == 0) { + this->floatTimer = 40; + } + this->floatTimer--; + this->actor.world.pos.y = this->actor.home.pos.y + (1.0f - Math_CosS(this->floatTimer * 0x666)) * 7.5f; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6070.s") +s32 EnDeath_ProjectileApproaching(EnDeath* this, PlayState* play) { + Actor* projectileActor = + func_800BC270(play, &this->actor, 80.0f, 0x10000 | 0x2000 | 0x1000 | 0x800 | 0x80 | 0x20 | 0x10); + s16 angle; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C645C.s") + if (projectileActor != NULL) { + angle = Actor_WorldYawTowardActor(&this->actor, projectileActor) - this->actor.shape.rot.y; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C64DC.s") + if (ABS_ALT(angle) < 0x2000) { + angle = Actor_WorldPitchTowardPoint(projectileActor, &this->actor.focus.pos) - projectileActor->world.rot.x; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6620.s") + if (ABS_ALT(angle) < 0x3000) { + return true; + } + } + } + return false; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C66A8.s") +void EnDeath_UpdateSpinAttackTris(EnDeath* this) { + f32 sinRotY = Math_SinS(this->actor.shape.rot.y); + f32 cosRotY = Math_CosS(this->actor.shape.rot.y); + Vec3f p3; + Vec3f p2; + Vec3f p1; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C67C8.s") + p1.x = this->actor.world.pos.x - 70.0f * cosRotY + 75.0f * sinRotY; + p1.y = this->actor.world.pos.y + -10.0f; + p1.z = this->actor.world.pos.z + 70.0f * sinRotY + 75.0f * cosRotY; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C682C.s") + p2.x = this->actor.world.pos.x + 70.0f * cosRotY + 65.0f * sinRotY; + p2.y = p1.y + 150.0f; + p2.z = this->actor.world.pos.z - 70.0f * sinRotY + 65.0f * cosRotY; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C68B8.s") + p3.x = p1.x; + p3.x += -10.0f * sinRotY; + p3.y = p2.y; + p3.z = p1.z; + p3.z += -10.0f * cosRotY; + Collider_SetTrisVertices(&this->weaponSpinningCollider, 0, &p1, &p2, &p3); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C692C.s") + p3.x = p2.x; + p3.x += 10.0f * sinRotY; + p3.y = p1.y; + p3.z = p2.z; + p3.z += 10.0f * cosRotY; + Collider_SetTrisVertices(&this->weaponSpinningCollider, 1, &p1, &p3, &p2); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6A08.s") +f32 EnDeath_UpdateCoreVelocityAndRotation(EnDeath* this) { + f32 tmp; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6AB0.s") + this->coreVelocity = this->actor.world.pos.y - this->actor.home.pos.y; + this->coreVelocity = CLAMP_MIN(this->coreVelocity, 0.0f); + this->coreVelocity *= 0.02f; + this->coreVelocity = SQ(this->coreVelocity) * 3.0f + 80.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6C5C.s") + tmp = 22.5f / (this->coreVelocity * (2 * M_PIf)); + this->coreRotation += TRUNCF_BINANG(0x10000 * tmp); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6CDC.s") +void EnDeath_SetupIntroCutscenePart1(EnDeath* this, PlayState* play) { + Player* player = GET_PLAYER(play); + Vec3f eye; + Vec3f at; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6D40.s") + this->actor.shape.rot.y = this->actor.shape.rot.y - 0x657A; + this->actionTimer = 60; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C6F6C.s") + eye.x = (Math_SinS(this->actor.home.rot.y + 0x98) * 542.0f) + this->actor.world.pos.x; + eye.z = (Math_CosS(this->actor.home.rot.y + 0x98) * 542.0f) + this->actor.world.pos.z; + eye.y = this->actor.home.pos.y + 3.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C70D8.s") + at.x = this->actor.world.pos.x; + at.z = this->actor.world.pos.z; + at.y = this->actor.world.pos.y - 116.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C72AC.s") + player->actor.world.pos.x = Math_SinS(this->actor.home.rot.y - 0x370) * 463.0f + this->actor.world.pos.x; + player->actor.world.pos.z = Math_CosS(this->actor.home.rot.y - 0x370) * 463.0f + this->actor.world.pos.z; + player->actor.shape.rot.y = Actor_WorldYawTowardActor(&player->actor, &this->actor); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C74A4.s") + Play_SetCameraAtEye(play, this->camId, &at, &eye); + Play_SetCameraFov(play, this->camId, 77.0f); + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_21); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C74F8.s") + this->actionFunc = EnDeath_IntroCutscenePart1; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7800.s") +void EnDeath_IntroCutscenePart1(EnDeath* this, PlayState* play) { + Player* player = GET_PLAYER(play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7888.s") + player->actor.world.pos.x = Math_SinS(this->actor.home.rot.y - 0x370) * 463.0f + this->actor.world.pos.x; + player->actor.world.pos.z = Math_CosS(this->actor.home.rot.y - 0x370) * 463.0f + this->actor.world.pos.z; + player->actor.shape.rot.y = Actor_WorldYawTowardActor(&player->actor, &this->actor); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7A30.s") + if (this->actionTimer == 25) { + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_81); + } + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnDeath_SetupIntroCutscenePart2(this, play); + } + EnDeath_UpdateCoreVelocityAndRotation(this); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7AAC.s") +void EnDeath_SetupIntroCutscenePart2(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7AEC.s") + EnDeath_DimLights(play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7B88.s") + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_INTRO_1; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7C04.s") + this->actionTimer = 50; + this->camEyeTarget.x = Math_SinS(this->actor.home.rot.y) * 140.0f + this->actor.world.pos.x; + this->camEyeTarget.z = Math_CosS(this->actor.home.rot.y) * 140.0f + this->actor.world.pos.z; + this->camEyeTarget.y = this->actor.home.pos.y + 20.0f; + this->camAtTarget.x = this->actor.world.pos.x; + this->camAtTarget.z = this->actor.world.pos.z; + this->camAtTarget.y = this->actor.home.pos.y + 50.0f; + this->camEyeSpeed = Math_Vec3f_DistXYZ(&camera->eye, &this->camEyeTarget) * 0.0225f; + this->camAtSpeed = Math_Vec3f_DistXYZ(&camera->at, &this->camAtTarget) * 0.0225f; + this->actionFunc = EnDeath_IntroCutscenePart2; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7C88.s") +void EnDeath_IntroCutscenePart2(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7CFC.s") + if (this->actionTimer == 42) { + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_4); + } else if (this->actionTimer == 27) { + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_123); + } + if (this->actionTimer <= 0) { + if (this->actor.world.pos.y < (this->actor.home.pos.y + 400.0f) - 225.0f) { + play->envCtx.lightSettingOverride = 26; + } + Math_Vec3f_StepTo(&camera->eye, &this->camEyeTarget, this->camEyeSpeed); + Math_Vec3f_StepTo(&camera->at, &this->camAtTarget, this->camAtSpeed); + Play_SetCameraAtEye(play, this->camId, &camera->at, &camera->eye); + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 9.0f) != 0) { + this->actionTimer--; + if (this->actionTimer == -20) { + EnDeath_SetupIntroCutscenePart3(this); + } + } + } else { + this->actionTimer--; + } + EnDeath_UpdateCoreVelocityAndRotation(this); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7D34.s") +void EnDeath_SetupIntroCutscenePart3(EnDeath* this) { + SET_EVENTINF(EVENTINF_INTRO_CS_WATCHED_GOMESS); + Animation_Change(&this->skelAnime, &object_death_Anim_00B284, 0.0f, 0.0f, + Animation_GetLastFrame(&object_death_Anim_00B284), ANIMMODE_ONCE, 0.0f); + this->actor.scale.y = 0.01f; + this->actor.shape.rot.y += 0x777F; + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_APPEAR); + this->actionFunc = EnDeath_IntroCutscenePart3; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7DB8.s") +void EnDeath_IntroCutscenePart3(EnDeath* this, PlayState* play) { + s32 stepDone = Math_StepToF(&this->actor.scale.x, 0.01f, 0.0005f); + f32 temp; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7DCC.s") + this->actor.scale.z = this->actor.scale.x; + temp = 0.01f - this->actor.scale.x; + if (temp > 0.0025f) { + temp = 0.0025f; + } + this->actor.shape.rot.y += TRUNCF_BINANG(temp * 0x44C000); + if (stepDone) { + this->inEarlyIntro = false; + EnDeath_SetupIntroCutscenePart4(this, play); + } + EnDeath_UpdateCoreVelocityAndRotation(this); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7E24.s") +void EnDeath_SetupIntroCutscenePart4(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); + f32 invAnimDuration = 1.0f / Animation_GetLastFrame(&object_death_Anim_00B284); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C7EDC.s") + this->actionTimer = 10; + this->skelAnime.playSpeed = 1.0f; + this->actor.shape.rot.y = this->actor.home.rot.y; + this->camEyeTarget.x = Math_SinS(this->actor.home.rot.y) * 50.0f + this->actor.world.pos.x; + this->camEyeTarget.z = Math_CosS(this->actor.home.rot.y) * 50.0f + this->actor.world.pos.z; + this->camEyeTarget.y = this->actor.home.pos.y + 95.0f; + this->camAtTarget.x = this->actor.world.pos.x; + this->camAtTarget.z = this->actor.world.pos.z; + this->camAtTarget.y = this->actor.world.pos.y + 100.0f; + this->camEyeSpeed = Math_Vec3f_DistXYZ(&camera->eye, &this->camEyeTarget) * invAnimDuration; + this->camAtSpeed = Math_Vec3f_DistXYZ(&camera->at, &this->camAtTarget) * invAnimDuration; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/EnDeath_Update.s") + this->actionFunc = EnDeath_IntroCutscenePart4; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C84A4.s") +void EnDeath_IntroCutscenePart4(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C8690.s") + if (this->matAnimStep < 23) { + this->matAnimStep++; + } + Math_Vec3f_StepTo(&camera->eye, &this->camEyeTarget, this->camEyeSpeed); + Math_Vec3f_StepTo(&camera->at, &this->camAtTarget, this->camAtSpeed); + Play_SetCameraAtEye(play, this->camId, &camera->at, &camera->eye); + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnDeath_SetupIntroCutscenePart5(this, play); + } + } + EnDeath_UpdateCoreVelocityAndRotation(this); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C882C.s") +void EnDeath_SetupIntroCutscenePart5(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C8D18.s") + Animation_PlayOnce(&this->skelAnime, &object_death_Anim_00CB2C); + this->matAnimStep = 23; + Audio_PlayBgm_StorePrevBgm(NA_BGM_MINI_BOSS); + this->camEyeTarget.x = Math_SinS(this->actor.home.rot.y) * 230.0f + this->actor.world.pos.x; + this->camEyeTarget.z = Math_CosS(this->actor.home.rot.y) * 230.0f + this->actor.world.pos.z; + this->camEyeTarget.y = this->actor.home.pos.y + 40.0f; + this->camAtTarget.y = this->actor.world.pos.y + 85.0f; + this->camEyeSpeed = Math_Vec3f_DistXYZ(&camera->eye, &this->camEyeTarget) * (1.0f / 7.0f); + this->camAtSpeed = Math_Vec3f_DistXYZ(&camera->at, &this->camAtTarget) * (1.0f / 7.0f); + Audio_PlaySfx_AtPos(&this->scytheScreenPos, NA_SE_EN_DEATH_VOICE); + this->actionFunc = EnDeath_IntroCutscenePart5; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C9160.s") +void EnDeath_IntroCutscenePart5(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); + f32 scale; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C9220.s") + if (Animation_OnFrame(&this->skelAnime, 12.0f)) { + play->envCtx.lightSettingOverride = 27; + } else if (Animation_OnFrame(&this->skelAnime, 25.0f)) { + play->envCtx.lightSettingOverride = 26; + } else if (Animation_OnFrame(&this->skelAnime, 38.0f)) { + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/func_808C9340.s") + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_INTRO_3; + } + } else if (Animation_OnFrame(&this->skelAnime, 14.0f)) { + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Death/EnDeath_Draw.s") + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_INTRO_2; + this->miniDeaths[i]->actor.world.pos.x = + (this->miniDeaths[i]->actor.world.pos.x - this->actor.world.pos.x) * 1.8f + this->actor.world.pos.x; + this->miniDeaths[i]->actor.world.pos.z = + (this->miniDeaths[i]->actor.world.pos.z - this->actor.world.pos.z) * 1.8f + this->actor.world.pos.z; + this->miniDeaths[i]->actor.world.pos.y = + (this->miniDeaths[i]->actor.world.pos.y - this->actor.world.pos.y) * 1.7f + this->actor.world.pos.y; + } + this->holdsScythe = true; + this->scytheScale = 1.0f; + } + Math_Vec3f_StepTo(&camera->eye, &this->camEyeTarget, this->camEyeSpeed); + Math_Vec3f_StepTo(&camera->at, &this->camAtTarget, this->camAtSpeed); + Math_StepToF(&camera->fov, 60.0f, 2.4285715f); + Play_SetCameraFov(play, this->camId, camera->fov); + Play_SetCameraAtEye(play, this->camId, &camera->at, &camera->eye); + + scale = CLAMP(this->skelAnime.curFrame, 10.0f, 14.0f) - 10.0f; + this->scytheScale = scale * 0.25f; + if (scale < 14.0f) { + EnDeath_UpdateCoreVelocityAndRotation(this); + } + if (SkelAnime_Update(&this->skelAnime)) { + CutsceneManager_Stop(this->actor.csId); + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_END); + this->actor.flags |= ACTOR_FLAG_TARGETABLE; + this->coreCollider.base.acFlags |= AC_ON; + EnDeath_SetupApproachPlayer(this); + } +} + +void EnDeath_SetupApproachPlayer(EnDeath* this) { + Animation_MorphToLoop(&this->skelAnime, &gGomessFloatAnim, 10.0f); + + this->actor.speed = 1.5f; + if (this->actionFunc == EnDeath_EndSwingAttack || this->actionFunc == EnDeath_SpinAttack) { + this->actionTimer = 140; + } else { + this->actionTimer = 100; + } + this->actionFunc = EnDeath_ApproachPlayer; +} + +void EnDeath_ApproachPlayer(EnDeath* this, PlayState* play) { + if (play->envCtx.lightSettingOverride == 20) { + play->envCtx.lightSettingOverride = 26; + } + EnDeath_Float(this); + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 8, 0x1000, 0x100); + + if (this->actionTimer > 0) { + this->actionTimer--; + } + if (this->actor.params < 5 && Rand_ZeroOne() < 0.4f && EnDeath_ProjectileApproaching(this, play)) { + EnDeath_SetupBlockProjectile(this); + } else if (this->actionTimer < 100 && this->actor.xzDistToPlayer < 200.0f) { + EnDeath_SetupSwingAttack(this); + } else if (this->actionTimer == 0) { + if (this->actor.params >= 5) { + EnDeath_SetupStartBatSwarm(this); + } else { + EnDeath_SetupSpinAttack(this); + } + } +} + +void EnDeath_SetupSwingAttack(EnDeath* this) { + Animation_Change(&this->skelAnime, &gGomessScytheSwingAnim, 1.0f, 0.0f, 10.0f, ANIMMODE_ONCE, -3.0f); + this->actionTimer = 0; + this->floatTimer = 0; + this->actor.speed = 8.0f; + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_ATTACK); + this->actionFunc = EnDeath_SwingAttack; +} + +void EnDeath_SwingAttack(EnDeath* this, PlayState* play) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 4, 0x1000, 0x200); + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 3.0f); + this->actor.shape.rot.x = TRUNCF_BINANG((100.0f - this->actor.xzDistToPlayer) * 0.01f * 0x400); + this->actor.shape.rot.x = CLAMP_MIN(this->actor.shape.rot.x, 0); + + if (this->actionTimer > 0) { + this->actionTimer++; + if (this->actionTimer == 3) { + EnDeath_SetupEndSwingAttack(this); + } + } else if (SkelAnime_Update(&this->skelAnime) && this->actor.xzDistToPlayer < 100.0f) { + Audio_PlaySfx_AtPos(&this->scytheScreenPos, NA_SE_EN_DEATH_SCYTHE); + this->actionTimer++; + this->actor.speed = 0.0f; + } +} + +void EnDeath_SetupEndSwingAttack(EnDeath* this) { + this->skelAnime.endFrame = Animation_GetLastFrame(&gGomessScytheSwingAnim); + this->actionTimer = 0; + this->weaponColliderLastUpdateTime = -1; + this->unk_18C = true; + this->actionFunc = EnDeath_EndSwingAttack; + this->actor.speed = 0.0f; +} + +void EnDeath_EndSwingAttack(EnDeath* this, PlayState* play) { + this->actionTimer++; + if (this->skelAnime.curFrame > 20.0f) { + this->unk_18C = false; + this->weaponCollider.base.atFlags &= ~AT_ON; + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x200); + } + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.shape.rot.x = 0; + EnDeath_SetupApproachPlayer(this); + } +} + +void EnDeath_SetupBlockProjectile(EnDeath* this) { + Animation_MorphToLoop(&this->skelAnime, &gGomessScytheSpinAnim, -3.0f); + this->weaponColliderLastUpdateTime = 31; + this->actionTimer = 30; + this->actor.speed = 0.0f; + EnDeath_UpdateSpinAttackTris(this); + this->unk_18C = true; + this->numScytheAfterImages = -3; + this->actionFunc = EnDeath_BlockProjectile; +} + +void EnDeath_BlockProjectile(EnDeath* this, PlayState* play) { + EnDeath_Float(this); + + this->actionTimer--; + if (this->numScytheAfterImages < 7) { + this->numScytheAfterImages++; + } + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlaySfx_AtPos(&this->scytheScreenPos, NA_SE_EN_DEATH_ROLL); + } + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 8, 0x1000, 0x100); + EnDeath_UpdateSpinAttackTris(this); + if (this->actionTimer == 0) { + if (this->actor.xzDistToPlayer > 200.0f) { + EnDeath_SetupSpinAttack(this); + } else { + EnDeath_SetupApproachPlayer(this); + } + } +} + +void EnDeath_SetupSpinAttack(EnDeath* this) { + if (this->actionFunc != EnDeath_BlockProjectile) { + Animation_MorphToLoop(&this->skelAnime, &gGomessScytheSpinAnim, -3.0f); + this->numScytheAfterImages = -3; + this->actionTimer = 0; + this->weaponColliderLastUpdateTime = -1; + this->unk_18C = true; + this->actor.speed = 0.0f; + } else { + // Starting straight out of blocking a projectile, already spinning so start moving immediately + this->actionTimer = 10; + this->weaponColliderLastUpdateTime = 9; + this->actor.speed = 10.0f; + } + EnDeath_UpdateSpinAttackTris(this); + this->actionFunc = EnDeath_SpinAttack; +} + +void EnDeath_SpinAttack(EnDeath* this, PlayState* play) { + EnDeath_Float(this); + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlaySfx_AtPos(&this->scytheScreenPos, NA_SE_EN_DEATH_ROLL); + } + + if (this->numScytheAfterImages < 7) { + this->numScytheAfterImages++; + } + + this->actionTimer++; + if (this->actionTimer < 10) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 8, 0x1000, 0x100); + } else if (this->actionTimer == 10) { + this->actor.speed = 10.0f; + } + + EnDeath_UpdateSpinAttackTris(this); + + Math_ScaledStepToS(&this->cloakUpperRotationModifier, (s32)(Math_SinS(this->actionTimer * 0x2000) * 0x800) + 0x3000, + 0x1000); + Math_ScaledStepToS(&this->cloakLowerRotationModifier, (s32)(Math_SinS(this->actionTimer * 0x2000 - 0x8000) * 0x800), + 0x1000); + + if ((this->actor.bgCheckFlags & BGCHECKFLAG_WALL) || (this->weaponCollider.base.atFlags & AT_HIT) || + (this->weaponSpinningCollider.base.atFlags & AT_HIT)) { + this->unk_18C = false; + this->weaponCollider.base.atFlags &= ~(AT_ON | AT_HIT); + this->weaponSpinningCollider.base.atFlags &= ~AT_HIT; + EnDeath_SetupApproachPlayer(this); + } +} + +void EnDeath_SetupDamaged(EnDeath* this) { + Animation_PlayOnce(&this->skelAnime, &gGomessDamagedAnim); + this->actor.speed = 10.0f; + func_800BE568(&this->actor, &this->coreCollider); + Actor_SetColorFilter(&this->actor, COLORFILTER_COLORFLAG_RED, 255, COLORFILTER_BUFFLAG_OPA, 15); + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_DAMAGE); + this->actionFunc = EnDeath_Damaged; +} + +void EnDeath_Damaged(EnDeath* this, PlayState* play) { + EnDeath_Float(this); + Math_StepToF(&this->actor.speed, 0.0f, 0.5f); + if (SkelAnime_Update(&this->skelAnime)) { + this->coreCollider.base.acFlags |= AC_ON; + EnDeath_SetupSpinAttack(this); + } +} + +void EnDeath_SetupDeathCutscenePart1(EnDeath* this, PlayState* play) { + Vec3f eye; + Vec3f at; + s32 i; + + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_IGNORE_QUAKE); + Animation_PlayOnce(&this->skelAnime, &gGomessBeginDeathAnim); + + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_DEATH_1; + } + + this->coreGuarded = false; + this->coreCollider.base.acFlags &= ~AC_ON; + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 30); + this->actionTimer = 35; + this->actor.world.pos.y = this->actor.home.pos.y; + this->actor.speed = 0.0f; + this->camEyeTarget.x = Math_SinS(this->actor.shape.rot.y + 0x900) * 79.0f + this->actor.world.pos.x; + this->camEyeTarget.z = Math_CosS(this->actor.shape.rot.y + 0x900) * 79.0f + this->actor.world.pos.z; + this->camEyeTarget.y = this->actor.home.pos.y + 63.0f; + at.x = this->actor.world.pos.x; + at.y = this->actor.world.pos.y + 100.0f; + at.z = this->actor.world.pos.z; + eye.x = Math_SinS(this->actor.shape.rot.y + 0x900) * 179.0f + this->actor.world.pos.x; + eye.z = Math_CosS(this->actor.shape.rot.y + 0x900) * 179.0f + this->actor.world.pos.z; + eye.y = this->actor.home.pos.y + 30.0f; + Play_SetCameraAtEye(play, this->camId, &at, &eye); + this->camEyeSpeed = Math_Vec3f_DistXYZ(&eye, &this->camEyeTarget) * 0.05f; + this->actor.shape.rot.y += 0x2000; + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_WAIT); + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_DEAD); + this->actionFunc = EnDeath_DeathCutscenePart1; +} + +void EnDeath_DeathCutscenePart1(EnDeath* this, PlayState* play) { + Camera* camera = Play_GetCamera(play, this->camId); + f32 distToTarget = Math_Vec3f_StepTo(&camera->eye, &this->camEyeTarget, this->camEyeSpeed); + + Play_SetCameraAtEye(play, this->camId, &camera->at, &camera->eye); + + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.shape.rot.y += 0x2000; + Animation_PlayLoop(&this->skelAnime, &gGomessDeathAnim); + } else if (this->skelAnime.animation == &gGomessBeginDeathAnim) { + this->actor.shape.rot.y += 0x2000; + if (this->skelAnime.curFrame > 3.0f) { + Matrix_Put(&this->scytheMtxF); + Matrix_RotateXS(-0x1000, MTXMODE_APPLY); + Matrix_Get(&this->scytheMtxF); + this->holdsScythe = false; + this->scytheMtxF.yw += 18.0f; + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_SCYTHE); + } + } + + if (distToTarget < 0.1f) { + if (this->actionTimer == 35) { + Audio_PlaySfx_AtPosWithVolumeTransition(&this->scytheScreenPos, NA_SE_EN_DEATH_SCYTHE_LEV, 0x41); + } + if (this->actionTimer > 0) { + this->actionTimer--; + } + if (this->actionTimer == 0) { + EnDeath_SetupDeathCutscenePart2(this, play); + } + } +} + +void EnDeath_SetupDeathCutscenePart2(EnDeath* this, PlayState* play) { + EnvLightSettings* lightSettings1; + EnvLightSettings* lightSettings2; + Player* player = GET_PLAYER(play); + Vec3f eye; + Vec3f at; + f32 sinRotY; + f32 cosRotY; + s32 i; + + Animation_PlayLoop(&this->skelAnime, &gGomessDeathAnim); + this->actionTimer = 30; + player->actor.shape.rot.y = Actor_WorldYawTowardPoint(&player->actor, &this->actor.home.pos) + 0x1000; + this->actor.shape.rot.y = player->actor.shape.rot.y + 0x6000; + sinRotY = Math_SinS(player->actor.shape.rot.y); + cosRotY = Math_CosS(player->actor.shape.rot.y); + this->actor.world.pos.x = player->actor.world.pos.x + (260.0f * sinRotY); + this->actor.world.pos.z = player->actor.world.pos.z + (260.0f * cosRotY); + this->actor.world.pos.y = this->actor.home.pos.y + 15.0f; + eye.x = (Math_SinS((s16)(player->actor.shape.rot.y - 0x2500)) * 182.0f) + this->actor.world.pos.x; + eye.z = (Math_CosS((s16)(player->actor.shape.rot.y - 0x2500)) * 182.0f) + this->actor.world.pos.z; + eye.y = this->actor.world.pos.y - 13.0f; + at.x = player->actor.world.pos.x + (120.0f * sinRotY); + at.y = player->actor.world.pos.y + 90.0f; + at.z = player->actor.world.pos.z + (120.0f * cosRotY); + Play_SetCameraAtEye(play, this->camId, &at, &eye); + + lightSettings1 = &play->envCtx.lightSettingsList[20]; + lightSettings2 = &play->envCtx.lightSettingsList[21]; + + for (i = 0; i < 3; i++) { + lightSettings1->light1Dir[i] = lightSettings2->light1Dir[i]; + lightSettings1->light2Dir[i] = lightSettings2->light2Dir[i]; + } + + this->actionFunc = EnDeath_DeathCutscenePart2; +} + +void EnDeath_DeathCutscenePart2(EnDeath* this, PlayState* play) { + f32 sin; + f32 cos; + s16 camYaw; + + SkelAnime_Update(&this->skelAnime); + + this->actionTimer--; + if (this->actionTimer >= 0 && this->actionTimer < 3) { + camYaw = Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000; + sin = Math_SinS(camYaw); + cos = Math_CosS(camYaw); + Matrix_Translate(this->actor.world.pos.x + (83.0f * sin) + (-38.0f * cos), + this->actor.world.pos.y + 53.0f + 15.0f * this->actionTimer, + this->actor.world.pos.z + (83.0f * cos) - (-38.0f * sin), MTXMODE_NEW); + Matrix_RotateYS(camYaw - 0x3300, MTXMODE_APPLY); + Matrix_RotateXS(0x1100 - this->actionTimer * 0x1800, MTXMODE_APPLY); + Matrix_RotateZS(-0xA00, MTXMODE_APPLY); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + Matrix_Get(&this->scytheMtxF); + if (this->actionTimer == 0) { + Camera_AddQuake(GET_ACTIVE_CAM(play), 2, 4, 6); + Rumble_Request(this->actor.xyzDistToPlayerSq, 180, 20, 100); + AudioSfx_StopByPosAndId(&this->scytheScreenPos, NA_SE_EN_DEATH_SCYTHE_LEV); + Audio_PlaySfx_AtPos(&this->scytheScreenPos, NA_SE_EN_DEATH_SCYTHE_ONGND); + } + } + if (this->actionTimer < -25) { + play->envCtx.lightSettingOverride = 20; + EnDeath_SetupDeathCutscenePart3(this); + } +} + +void EnDeath_SetupDeathCutscenePart3(EnDeath* this) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_DEATH_2; + } + + this->actionTimer = 0; + this->actionFunc = EnDeath_DeathCutscenePart3; +} + +void EnDeath_DeathCutscenePart3(EnDeath* this, PlayState* play) { + static Vec3f sSparkleVel = { 0.0f, -1.5f, 0.0f }; + static Vec3f sSparkleAccel = { 0.0f, -0.2f, 0.0f }; + static Color_RGBA8 sSparklePrimColor = { 0, 0, 0, 0 }; + static Color_RGBA8 sSparkleEnvColor = { 40, 40, 40, 0 }; + static s16 sDeleteLimbFrameNumbers[GOMESS_LIMB_MAX] = { + 30, 30, 10, 2, 12, 17, 17, 17, 17, 17, 17, 17, 14, 10, 12, 9, 9, 12, 9, 9, 30, 30, + }; + s32 i; + s32 j; + u8* flameAlpha; + Vec3f* sparklePos; + s32 pad; + s32 pad1; + f32 sin; + f32 cos; + f32 x; + s32 alpha; + + sparklePos = this->sparklePositions; + flameAlpha = this->flameAlphas; + for (i = 0; (i ^ 0) < ARRAY_COUNT(this->sparklePositions); i++) { //! FAKE + sparklePos->y += 1.7f; + alpha = *flameAlpha - 30; + *flameAlpha = CLAMP_MIN(alpha, 0); + sparklePos++; + flameAlpha++; + } + + alpha = -this->actionTimer * 14 + 252; + this->actor.shape.shadowAlpha = CLAMP_MIN(alpha, 0); + + if (this->actionTimer < 18) { + sparklePos = &this->sparklePositions[(this->actionTimer * 7) % ARRAY_COUNT(this->sparklePositions)]; + flameAlpha = &this->flameAlphas[(this->actionTimer * 7) % ARRAY_COUNT(this->flameAlphas)]; + + sin = Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000); + cos = Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000); + + for (j = 0; j < 7; j++) { + x = Rand_CenteredFloat(100.0f) - 20.0f; + sparklePos->x = this->actor.world.pos.x + 30.0f * sin + x * cos; + sparklePos->y = (Rand_ZeroFloat(20.0f) + this->actor.world.pos.y + 4.0f * this->actionTimer) - 30.0f; + sparklePos->z = this->actor.world.pos.z + 30.0f * cos - x * sin; + *flameAlpha = 255 - j * 4; + EffectSsKirakira_SpawnSmall(play, sparklePos, &sSparkleVel, &sSparkleAccel, &sSparklePrimColor, + &sSparkleEnvColor); + sparklePos++; + flameAlpha++; + } + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_COMMON_EXTINCT_LEV - SFX_FLAG); + } + + this->actionTimer++; + + for (i = 0; i < ARRAY_COUNT(this->noDrawLimbs); i++) { + if (this->actionTimer == sDeleteLimbFrameNumbers[i]) { + this->noDrawLimbs[i] = true; + } + } + + if (this->actionTimer == 26) { + EnDeath_SetupDeathCutscenePart4(this); + } +} + +void EnDeath_SetupDeathCutscenePart4(EnDeath* this) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->flameAlphas); i++) { + this->flameAlphas[i] = 0; + } + + Math_Vec3f_Copy(&this->actor.world.pos, &this->actor.focus.pos); + this->actor.bgCheckFlags &= ~BGCHECKFLAG_GROUND; + this->actionTimer = 0; + this->actionFunc = EnDeath_DeathCutscenePart4; + this->actor.gravity = -1.0f; + this->actor.shape.yOffset = 0.0f; +} + +void EnDeath_DeathCutscenePart4(EnDeath* this, PlayState* play) { + static Vec3f sSparkleAccel = { 0.0f, -1.0f, 0.0f }; + static Color_RGBA8 sSparklePrimColor = { 0, 200, 100, 0 }; + static Color_RGBA8 sSparkleEnvColor = { 0, 100, 0, 0 }; + Vec3f sparkleVel; + s32 i; + + if (this->actionTimer == 0) { + if ((this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) || this->actor.floorHeight == BGCHECK_Y_MIN) { + Actor_SetScale(&this->actor, 0.0f); + this->actionTimer++; + + for (i = 0; i < 40; i++) { + sSparkleAccel.y = -0.2f; + sparkleVel.x = Rand_CenteredFloat(4.0f); + sparkleVel.z = Rand_CenteredFloat(4.0f); + sparkleVel.y = Rand_ZeroFloat(2.0f) + 3.0f; + EffectSsKirakira_SpawnSmall(play, &this->actor.world.pos, &sparkleVel, &sSparkleAccel, + &sSparklePrimColor, &sSparkleEnvColor); + } + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_HEARTBREAK); + } + } else { + this->actionTimer++; + if (this->actionTimer == 20) { + EnDeath_SetupDead(this, play); + } + } +} + +void EnDeath_SetupDead(EnDeath* this, PlayState* play) { + CutsceneManager_Stop(this->actor.csId); + Player_SetCsAction(play, &this->actor, PLAYER_CSACTION_END); + Flags_SetClearTemp(play, play->roomCtx.curRoom.num); + this->actionTimer = 255; + play->envCtx.lightSettingOverride = 255; + this->actionFunc = EnDeath_Dead; +} + +void EnDeath_Dead(EnDeath* this, PlayState* play) { + if (this->actionTimer > 0) { + this->actionTimer -= 5; + if (this->actionTimer <= 0) { + this->actionTimer = 0; + Actor_Kill(&this->actor); + } + } +} + +void EnDeath_SetupStartBatSwarm(EnDeath* this) { + s32 i; + + Actor_PlaySfx(&this->actor, NA_SE_EN_DEATH_VOICE); + Animation_MorphToPlayOnce(&this->skelAnime, &gGomessBatSwarmStartAnim, 5.0f); + this->actor.speed = 0.0f; + + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_START_SWARM; + } + + this->floatTimer = 0; + this->actionFunc = EnDeath_StartBatSwarm; +} + +void EnDeath_StartBatSwarm(EnDeath* this, PlayState* play) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 8, 0x1000, 0x100); + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 5.0f); + + if (SkelAnime_Update(&this->skelAnime)) { + play->envCtx.lightSettingOverride = 27; + EnDeath_SetupBatSwarm(this); + } +} + +void EnDeath_SetupBatSwarm(EnDeath* this) { + s32 i; + + Animation_PlayLoop(&this->skelAnime, &gGomessBatSwarmAnim); + + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_SWARM; + } + + this->actionFunc = EnDeath_BatSwarm; + this->actor.speed = 0.0f; +} + +void EnDeath_BatSwarm(EnDeath* this, PlayState* play) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 8, 0x1000, 0x100); + SkelAnime_Update(&this->skelAnime); + + if (this->actor.params >= 5) { + play->envCtx.lightSettingOverride = 26; + EnDeath_SetupApproachPlayer(this); + } +} + +void EnDeath_SetupPlayCutscene(EnDeath* this) { + CutsceneManager_Queue(this->actor.csId); + this->actionFunc = EnDeath_PlayCutscene; +} + +void EnDeath_PlayCutscene(EnDeath* this, PlayState* play) { + // Wait for it's turn to play + if (CutsceneManager_IsNext(this->actor.csId)) { + // Start playing + CutsceneManager_Start(this->actor.csId, &this->actor); + this->camId = CutsceneManager_GetCurrentSubCamId(this->actor.csId); + if (this->actor.colChkInfo.health == 0) { + // Death cutscene + EnDeath_SetupDeathCutscenePart1(this, play); + } else { + // Intro cutscene + EnDeath_SetupIntroCutscenePart1(this, play); + } + } else { + CutsceneManager_Queue(this->actor.csId); + } +} + +void EnDeath_SetupBeginWithoutCutscene(EnDeath* this) { + this->actionFunc = EnDeath_BeginWithoutCutscene; +} + +void EnDeath_BeginWithoutCutscene(EnDeath* this, PlayState* play) { + if (!Play_InCsMode(play)) { + EnDeath_DimLights(play); + this->coreCollider.base.acFlags |= AC_ON; + Audio_PlayBgm_StorePrevBgm(NA_BGM_MINI_BOSS); + EnDeath_SetupApproachPlayer(this); + } +} + +void EnDeath_UpdateCore(EnDeath* this, PlayState* play) { + if (this->coreGuarded) { + // Core falling to the ground + this->corePos.y += this->coreVelocity; + this->coreVelocity -= 1.0f; + this->coreRotation += 0x1800; + if (this->corePos.y < this->actor.floorHeight) { + // Hit the floor + this->corePos.y = this->actor.floorHeight; + func_800B3030(play, &this->corePos, &gZeroVec3f, &gZeroVec3f, 100, 0, 0); + SoundSource_PlaySfxAtFixedWorldPos(play, &this->corePos, 11, NA_SE_EN_EXTINCT); + this->coreGuarded = false; + } + } +} + +void EnDeath_UpdateDamage(EnDeath* this, PlayState* play) { + s32 i; + + if (this->explosiveDamageTimer > 0) { + this->explosiveDamageTimer--; + } + if (this->coreCollider.base.acFlags & AC_HIT) { + this->coreCollider.base.acFlags &= ~AC_HIT; + + if (this->actor.params >= 5) { + this->coreGuarded = true; + this->coreVelocity = -1.0f; + this->coreRotation = this->actor.shape.rot.y; + Math_Vec3s_ToVec3f(&this->corePos, &this->coreCollider.dim.worldSphere.center); + SoundSource_PlaySfxAtFixedWorldPos(play, &this->corePos, 30, NA_SE_EN_FFLY_DEAD); + + if (this->actor.colChkInfo.damageEffect == DMGEFF_LIGHT_ARROW && + this->actionFunc != EnDeath_StartBatSwarm) { + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_SCATTER; + } + play->envCtx.lightSettingOverride = 28; + + this->lightArrowDamageTimer = 20; + if (this->actionFunc == EnDeath_ApproachPlayer) { + this->actionTimer = 100; + } + } else if (this->actor.colChkInfo.damageEffect == DMGEFF_EXPLOSIVES) { + this->explosiveDamageTimer = 10; + } + } else if (this->actor.colChkInfo.damageEffect != DMGEFF_EXPLOSIVES || this->explosiveDamageTimer == 0) { + this->unk_18C = false; + this->weaponCollider.base.atFlags &= ~AT_ON; + this->coreCollider.base.acFlags &= ~AC_ON; + + if (this->actor.colChkInfo.damageEffect == DMGEFF_LIGHT_ARROW) { + this->dmgEffectAlpha = 3.0f; + this->dmgEffectScale = 0.8f; + this->dmgEffect = ACTOR_DRAW_DMGEFF_LIGHT_ORBS; + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->coreCollider.info.bumper.hitPos.x, + this->coreCollider.info.bumper.hitPos.y, this->coreCollider.info.bumper.hitPos.z, 0, 0, 0, + 4); + } + if (play->envCtx.lightSettingOverride == 27) { + play->envCtx.lightSettingOverride = 26; + } + this->actor.shape.rot.x = 0; + + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + Audio_RestorePrevBgm(); // Stop miniboss BGM + EnDeath_SetupPlayCutscene(this); + } else { + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + this->miniDeaths[i]->actor.params = MINIDEATH_ACTION_RETURN; + } + EnDeath_SetupDamaged(this); + } + } + } +} + +void EnDeath_Update(Actor* thisx, PlayState* play) { + EnDeath* this = THIS; + ArrowLight* lightArrow; + s32 pad; + + if (this->lightArrowDamageTimer > 0) { + this->lightArrowDamageTimer--; + + lightArrow = (ArrowLight*)SubS_FindActor(play, NULL, ACTORCAT_ITEMACTION, ACTOR_ARROW_LIGHT); + if (lightArrow != NULL) { + lightArrow->screenFillIntensity = -100.0f; + } + + if (this->lightArrowDamageTimer == 0) { + play->envCtx.lightSettingOverride = 26; + } + } + + EnDeath_UpdateDamage(this, play); + EnDeath_UpdateCore(this, play); + if (this->actionFunc != EnDeath_SpinAttack) { + Math_ScaledStepToS(&this->cloakUpperRotationModifier, 0, 0x800); + Math_ScaledStepToS(&this->cloakLowerRotationModifier, 0, 0x800); + } + + this->actionFunc(this, play); + + if (this->actionFunc != EnDeath_Damaged) { + this->actor.world.rot.y = this->actor.shape.rot.y; + } + Actor_MoveWithGravity(&this->actor); + + Actor_UpdateBgCheckInfo(play, &this->actor, 40.0f, (this->actionFunc == EnDeath_SpinAttack) ? 50.0f : 100.0f, 40.0f, + UPDBGCHECKINFO_FLAG_4 | UPDBGCHECKINFO_FLAG_2 | UPDBGCHECKINFO_FLAG_1); + + this->bodyCollider.dim.pos.x = this->actor.world.pos.x + Math_SinS(this->actor.shape.rot.y) * 3.0f; + this->bodyCollider.dim.pos.z = this->actor.world.pos.z + Math_CosS(this->actor.shape.rot.y) * 3.0f; + this->bodyCollider.dim.pos.y = this->actor.world.pos.y; + + if (this->actor.params < 5) { + this->coreCollider.dim.worldSphere.radius = this->coreCollider.dim.modelSphere.radius + 5; + } else { + this->coreCollider.dim.worldSphere.radius = this->coreCollider.dim.modelSphere.radius; + } + + if (this->actionFunc != EnDeath_Dead) { + CollisionCheck_SetOC(play, &play->colChkCtx, &this->bodyCollider.base); + } + if (this->coreCollider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(play, &play->colChkCtx, &this->coreCollider.base); + } + if (this->weaponCollider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->weaponCollider.base); + } + if (this->actionFunc == EnDeath_BlockProjectile || this->actionFunc == EnDeath_SpinAttack) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->weaponSpinningCollider.base); + CollisionCheck_SetAC(play, &play->colChkCtx, &this->weaponSpinningCollider.base); + } + + if (this->dmgEffectAlpha > 0.0f) { + if (this->dmgEffect != ACTOR_DRAW_DMGEFF_FROZEN_NO_SFX) { + Math_StepToF(&this->dmgEffectAlpha, 0.0f, 0.05f); + this->dmgEffectScale = (this->dmgEffectAlpha + 1.0f) * 0.4f; + + this->dmgEffectScale = CLAMP_MAX(this->dmgEffectScale, 0.8f); + } else if (Math_StepToF(&this->dmgEffectSteamScale, 0.8f, 0.02f) == 0) { + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EV_ICE_FREEZE - SFX_FLAG); + } + } +} + +void EnDeath_DrawScytheSpinning(EnDeath* this, PlayState* play) { + s32 i; + Gfx* gfx; + s32 pad; + + func_800B8118(&this->actor, play, 0); + Scene_SetRenderModeXlu(play, 1, 2); + + OPEN_DISPS(play->state.gfxCtx); + gfx = POLY_XLU_DISP; + + for (i = 1; i < this->numScytheAfterImages; i++) { + gDPPipeSync(gfx++); + gDPSetEnvColor(gfx++, 30, 30, 0, 255 - i * 35); + + Matrix_Put(&this->scytheMtxF); + Matrix_RotateXS(i * 0x2100, MTXMODE_APPLY); + + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, gGomessScytheHandleDL); + + Matrix_Translate(0.0f, -1084.0f, 7012.0f, MTXMODE_APPLY); + Matrix_RotateZYX(-0x4000, 0, -0x4000, MTXMODE_APPLY); + + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, gGomessScytheBladeDL); + } + + POLY_XLU_DISP = gfx; + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnDeath_DrawScythe(EnDeath* this, PlayState* play) { + Gfx* gfx; + s32 pad; + + if (this->actionFunc == EnDeath_Dead) { + func_800B8118(&this->actor, play, 0); + Scene_SetRenderModeXlu(play, 1, 2); + } + + OPEN_DISPS(play->state.gfxCtx); + + if (this->actionFunc == EnDeath_Dead) { + gfx = POLY_XLU_DISP; + + gDPPipeSync(gfx++); + gDPSetEnvColor(gfx++, 30, 30, 0, this->actionTimer); + } else { + gfx = POLY_OPA_DISP; + } + + Matrix_Put(&this->scytheMtxF); + Matrix_Scale(this->scytheScale, this->scytheScale, this->scytheScale, MTXMODE_APPLY); + + gSPMatrix(&gfx[0], Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(&gfx[1], gGomessScytheHandleDL); + + Matrix_Translate(0.0f, -1084.0f, 7012.0f, MTXMODE_APPLY); + Matrix_RotateZYX(-0x4000, 0, -0x4000, MTXMODE_APPLY); + + gSPMatrix(&gfx[2], Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(&gfx[3], gGomessScytheBladeDL); + + Matrix_MultZero(&this->scytheWorldPos); + + if (this->actionFunc == EnDeath_Dead) { + POLY_XLU_DISP = &gfx[4]; + } else { + POLY_OPA_DISP = &gfx[4]; + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnDeath_DrawBats(EnDeath* this, PlayState* play) { + // Each bat is animated by cycling through these display lists + static Gfx* sMinideathDLs[MINIDEATH_ANIM_LENGTH] = { + gGomessBatFrame1DL, gGomessBatFrame2DL, gGomessBatFrame3DL, gGomessBatFrame4DL, gGomessBatFrame5DL, + gGomessBatFrame6DL, gGomessBatFrame7DL, gGomessBatFrame8DL, gGomessBatFrame9DL, + }; + EnMinideath* miniDeath; + MtxF* cmf; + f32 scale2; + MiniDeathEffect* effect; + s32 j; + f32 scale; + Gfx* gfx; + s32 i; + Vec3f quakeOffset; + + OPEN_DISPS(play->state.gfxCtx); + gfx = POLY_OPA_DISP; + + gSPDisplayList(gfx++, gSetupDLs[SETUPDL_25]); + gSPDisplayList(gfx++, gGomessBatMaterialDL); + + cmf = Matrix_GetCurrent(); + + if (this->actionFunc == EnDeath_Dead) { + scale2 = this->actionTimer * 0.000035f; + scale = 0.007f; + if (scale2 > 0.007f) { + scale2 = 0.007f; + scale = 0.007f; + } + } else { + scale2 = 0.007f; + scale = 0.007f; + } + + if (this->actor.flags & ACTOR_FLAG_IGNORE_QUAKE) { + Math_Vec3f_Copy(&quakeOffset, &play->mainCamera.quakeOffset); + } else { + Math_Vec3f_Copy(&quakeOffset, &gZeroVec3f); + } + + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + if (this->actionFunc == EnDeath_BeginWithoutCutscene || + CHECK_FLAG_ALL(this->miniDeaths[i]->actor.flags, ACTOR_FLAG_40)) { + miniDeath = this->miniDeaths[i]; + + Matrix_RotateZYX(miniDeath->actor.shape.rot.x, miniDeath->actor.shape.rot.y, 0, MTXMODE_NEW); + Matrix_Scale(scale2, scale2, scale2, MTXMODE_APPLY); + + for (effect = miniDeath->effects, j = 0; j < MINIDEATH_NUM_EFFECTS; j++, effect++) { + if (effect->state == 0) { + cmf->mf[3][0] = effect->pos.x + quakeOffset.x; + cmf->mf[3][1] = effect->pos.y + quakeOffset.y; + cmf->mf[3][2] = effect->pos.z + quakeOffset.z; + + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, sMinideathDLs[effect->animFrame]); + } + } + + for (effect = miniDeath->effects, j = 0; j < MINIDEATH_NUM_EFFECTS; j++, effect++) { + if (effect->state == 1) { + Matrix_RotateZYX(0x4000, effect->angle.y, 0, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + cmf->mf[3][0] = effect->pos.x + quakeOffset.x; + cmf->mf[3][1] = effect->pos.y + quakeOffset.y; + cmf->mf[3][2] = effect->pos.z + quakeOffset.z; + + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, sMinideathDLs[effect->animFrame]); + } + } + } + } + + if (this->inEarlyIntro) { + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + miniDeath = this->miniDeaths[i]; + + Matrix_RotateZYX(miniDeath->actor.shape.rot.x, miniDeath->actor.shape.rot.y, 0, MTXMODE_NEW); + Matrix_Scale(scale2, scale2, scale2, MTXMODE_APPLY); + + for (effect = miniDeath->effects, j = 0; j < MINIDEATH_NUM_EFFECTS; j++, effect++) { + cmf->mf[3][0] = miniDeath->actor.world.pos.x - effect->vel.x; + cmf->mf[3][1] = miniDeath->actor.world.pos.y + (20.0f - effect->vel.y); + cmf->mf[3][2] = miniDeath->actor.world.pos.z - effect->vel.z; + + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, sMinideathDLs[effect->animFrame]); + } + } + } + + if (this->coreGuarded) { + Matrix_RotateZYX(0x4000, this->coreRotation, 0, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + cmf->mf[3][0] = this->corePos.x + quakeOffset.x; + cmf->mf[3][1] = this->corePos.y + quakeOffset.y; + cmf->mf[3][2] = this->corePos.z + quakeOffset.z; + + gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, sMinideathDLs[0]); + } + + POLY_OPA_DISP = gfx; + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnDeath_DrawFlames(EnDeath* this, PlayState* play2) { + PlayState* play = play2; + s32 i; + MtxF* cmf; + s32 numFlames; + u8* flameAlpha; + Vec3f* sparklePos; + MiniDeathEffect* effect; + u8 alpha; + s32 j; + + numFlames = this->actionTimer * 7; + if (numFlames > ARRAY_COUNT(this->flameAlphas)) { + numFlames = ARRAY_COUNT(this->flameAlphas); + } + + OPEN_DISPS(play->state.gfxCtx); + + gDPSetEnvColor(POLY_XLU_DISP++, 230, 135, 25, 0); + + Matrix_Put(&play->billboardMtxF); + Matrix_Scale(0.00405f, 0.003f, 0.00405f, MTXMODE_APPLY); + + cmf = Matrix_GetCurrent(); + + sparklePos = this->sparklePositions; + flameAlpha = this->flameAlphas; + for (i = 0; i < numFlames; i++) { + if (*flameAlpha > 0) { + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 165, 255, 215, *flameAlpha); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(play->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + ((play->gameplayFrames + i * 10) * -20) & 0x1FF, 32, 128)); + + cmf->mf[3][0] = sparklePos->x; + cmf->mf[3][1] = sparklePos->y; + cmf->mf[3][2] = sparklePos->z; + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + sparklePos++; + flameAlpha++; + } + + Matrix_Put(&play->billboardMtxF); + Matrix_Scale(0.00405f, 0.003f, 0.00405f, MTXMODE_APPLY); + + if (this->actionFunc == EnDeath_Dead) { + alpha = this->actionTimer; + } else { + alpha = 255; + } + + for (i = 0; i < ARRAY_COUNT(this->miniDeaths); i++) { + if (CHECK_FLAG_ALL(this->miniDeaths[i]->actor.flags, ACTOR_FLAG_40)) { + for (effect = this->miniDeaths[i]->effects, j = 0; j < MINIDEATH_NUM_EFFECTS; j++, effect++) { + cmf->mf[3][0] = effect->pos.x; + cmf->mf[3][1] = effect->pos.y - 12.0f; + cmf->mf[3][2] = effect->pos.z; + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 165, 255, 215, alpha); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(play->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + ((play->gameplayFrames + ((i + j) * 10)) * -20) & 511, 32, 128)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + } + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnDeath_DrawCore(EnDeath* this, PlayState* play) { + s32 pad; + Gfx* gfx; + + OPEN_DISPS(play->state.gfxCtx); + gfx = POLY_OPA_DISP; + + Matrix_ReplaceRotation(&play->billboardMtxF); + + gSPMatrix(&gfx[0], Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(&gfx[1], gGomessCoreDL); + + if (this->actor.params >= 5) { + // bats in front of the core + gSPDisplayList(&gfx[2], gGomessBatsGuardingCoreDL); + POLY_OPA_DISP = &gfx[3]; + } else { + POLY_OPA_DISP = &gfx[2]; + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +s32 EnDeath_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + EnDeath* this = THIS; + + if (this->noDrawLimbs[limbIndex] == true) { + *dList = NULL; + } else if (limbIndex == GOMESS_LIMB_CLOAK_LOWER && this->actionFunc == EnDeath_DeathCutscenePart3) { + if (this->actionTimer - 5 > 0) { + f32 scale = 1.0f - 0.1f * (this->actionTimer - 5); + + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + } + } + + if (limbIndex == GOMESS_LIMB_SCYTHE_HANDLE || limbIndex == GOMESS_LIMB_SCYTHE_BLADE) { + if (!this->holdsScythe) { + *dList = NULL; + } + } else if (limbIndex == GOMESS_LIMB_CLOAK_UPPER) { + rot->z += this->cloakUpperRotationModifier; + } else if (limbIndex == GOMESS_LIMB_CLOAK_LOWER) { + rot->z += this->cloakLowerRotationModifier; + } + return 0; +} + +void EnDeath_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + static Vec3f sWeaponColliderOffsetEndSwing = { -1000.0f, 12000.0f, 0.0f }; + static Vec3f sWeaponColliderOffsetOther = { -2000.0f, -2500.0f, 0.0f }; + static Vec3f sDamageEffectOffsets[5] = { + { 1000.0f, 2000.0f, 0.0f }, { 1500.0f, 1000.0f, 4000.0f }, { 1500.0f, 1000.0f, -4000.0f }, + { 4000.0f, 4000.0f, 2000.0f }, { 4000.0f, 4000.0f, -2000.0f }, + }; + static s8 sLimbToBodyParts[GOMESS_LIMB_MAX] = { + -1, -1, -1, 12, -1, 0, -1, -1, -1, -1, -1, -1, -1, 7, 1, 2, 3, 4, 5, 6, -1, -1, + }; + EnDeath* this = THIS; + s8 index; + + if (limbIndex == GOMESS_LIMB_SCYTHE_BLADE) { + Matrix_MultZero(&this->scytheWorldPos); + + if ((this->weaponCollider.base.atFlags & AT_ON) && this->weaponColliderLastUpdateTime != this->actionTimer) { + Vec3f quad0; + Vec3f quad1; + Vec3f pos1; + Vec3f pos2; + + Math_Vec3f_Copy(&quad0, &this->weaponCollider.dim.quad[0]); + Math_Vec3f_Copy(&quad1, &this->weaponCollider.dim.quad[1]); + + if (this->actionFunc == EnDeath_EndSwingAttack) { + Matrix_MultVecX(6000.0f, &pos1); + Matrix_MultVec3f(&sWeaponColliderOffsetEndSwing, &pos2); + } else { + Matrix_MultVecY(5000.0f, &pos2); + Matrix_MultVec3f(&sWeaponColliderOffsetOther, &pos1); + } + + Collider_SetQuadVertices(&this->weaponCollider, &pos2, &pos1, &quad0, &quad1); + EffectBlure_AddVertex(Effect_GetByIndex(this->effectIndex), &pos1, &pos2); + + this->weaponColliderLastUpdateTime = this->actionTimer; + } else if (this->unk_18C) { + if (this->actionFunc == EnDeath_EndSwingAttack) { + Matrix_MultVecX(6000.0f, &this->weaponCollider.dim.quad[1]); + Matrix_MultVec3f(&sWeaponColliderOffsetEndSwing, &this->weaponCollider.dim.quad[0]); + } else { + Matrix_MultVecY(5000.0f, &this->weaponCollider.dim.quad[0]); + Matrix_MultVec3f(&sWeaponColliderOffsetOther, &this->weaponCollider.dim.quad[1]); + } + this->unk_18C = false; + this->weaponCollider.base.atFlags |= AT_ON; + this->weaponColliderLastUpdateTime = this->actionTimer; + } + } else if (limbIndex == GOMESS_LIMB_CORE_POS && this->actionFunc != EnDeath_Dead) { + Matrix_Push(); + EnDeath_DrawCore(this, play); + Matrix_Pop(); + Matrix_MultZero(&this->actor.focus.pos); + + this->coreCollider.dim.worldSphere.center.x = this->actor.focus.pos.x; + this->coreCollider.dim.worldSphere.center.y = this->actor.focus.pos.y; + this->coreCollider.dim.worldSphere.center.z = this->actor.focus.pos.z; + + if (this->actor.params < 5) { + this->coreCollider.dim.worldSphere.center.y = this->actor.focus.pos.y + 5.0f; + } + } else if (limbIndex == GOMESS_LIMB_SCYTHE_HANDLE) { + if (!(this->actionFunc != EnDeath_SpinAttack && this->actionFunc != EnDeath_BlockProjectile && + this->actionFunc != EnDeath_IntroCutscenePart5) || + (this->actionFunc == EnDeath_DeathCutscenePart1 && this->holdsScythe == true)) { + Matrix_Get(&this->scytheMtxF); + } + } + + index = sLimbToBodyParts[limbIndex]; + if (index != -1) { + if (index < 7) { + // get matrix translation as-is, occupies slots 0, 1, 2, 3, 4, 5, 6 + Matrix_MultZero(&this->bodyPartsPos[index]); + } else if (index == 7) { + // 5 points using 5 offsets, occupies slots 7, 8, 9, 10, 11 + s32 i; + Vec3f* offset; + Vec3f* effPos; + + for (effPos = &this->bodyPartsPos[index], offset = &sDamageEffectOffsets[0], i = 0; + i != ARRAY_COUNT(sDamageEffectOffsets); i++, offset++, effPos++) { + Matrix_MultVec3f(offset, effPos); + } + } else if (index == 12) { + // get matrix translation as-is and a point offset by -2000 from it, occupies slots 12, 13 + Matrix_MultZero(&this->bodyPartsPos[index]); + Matrix_MultVecY(-2000.0f, &this->bodyPartsPos[index + 1]); + } + } +} + +void EnDeath_Draw(Actor* thisx, PlayState* play) { + EnDeath* this = THIS; + s32 pad; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + func_800B8050(&this->actor, play, 0); + AnimatedMat_DrawStepOpa(play, this->bodyMatAnim, this->matAnimStep); + AnimatedMat_DrawOpa(play, this->coreMatAnim); + Scene_SetRenderModeXlu(play, 0, 1); + + gDPSetEnvColor(POLY_OPA_DISP++, 30, 30, 0, 255); + + if (this->actionFunc != EnDeath_Dead && this->actionFunc != EnDeath_DeathCutscenePart4) { + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDeath_OverrideLimbDraw, EnDeath_PostLimbDraw, &this->actor); + } + if (this->actionFunc == EnDeath_DeathCutscenePart4) { + EnDeath_DrawCore(this, play); + } + if (!this->holdsScythe) { + EnDeath_DrawScythe(this, play); + } + if (this->actor.colorFilterTimer != 0) { + func_800AE5A0(play); + } + Actor_DrawDamageEffects(play, &this->actor, this->bodyPartsPos, ARRAY_COUNT(this->bodyPartsPos), + this->dmgEffectScale, this->dmgEffectSteamScale, this->dmgEffectAlpha, this->dmgEffect); + if (this->actionFunc == EnDeath_SpinAttack || this->actionFunc == EnDeath_BlockProjectile) { + EnDeath_DrawScytheSpinning(this, play); + } + EnDeath_DrawBats(this, play); + if (this->actionFunc == EnDeath_Dead || this->actionFunc == EnDeath_DeathCutscenePart4 || + (this->actionFunc == EnDeath_DeathCutscenePart3 && this->actionTimer > 0)) { + EnDeath_DrawFlames(this, play); + } + SkinMatrix_Vec3fMtxFMultXYZ(&play->viewProjectionMtxF, &this->scytheWorldPos, &this->scytheScreenPos); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Death/z_en_death.h b/src/overlays/actors/ovl_En_Death/z_en_death.h index 16863cf836..ea33ea2ad5 100644 --- a/src/overlays/actors/ovl_En_Death/z_en_death.h +++ b/src/overlays/actors/ovl_En_Death/z_en_death.h @@ -2,6 +2,8 @@ #define Z_EN_DEATH_H #include "global.h" +#include "overlays/actors/ovl_En_Minideath/z_en_minideath.h" +#include "objects/object_death/object_death.h" struct EnDeath; @@ -9,9 +11,53 @@ typedef void (*EnDeathActionFunc)(struct EnDeath*, PlayState*); typedef struct EnDeath { /* 0x000 */ Actor actor; - /* 0x144 */ char unk_144[0x44]; + /* 0x144 */ SkelAnime skelAnime; /* 0x188 */ EnDeathActionFunc actionFunc; - /* 0x18C */ char unk_18C[0x7AC]; + /* 0x18C */ u8 unk_18C; + /* 0x18D */ u8 coreGuarded; + /* 0x18E */ u8 holdsScythe; + /* 0x18F */ u8 dmgEffect; + /* 0x190 */ s8 numScytheAfterImages; + /* 0x191 */ u8 matAnimStep; // animated material color step? + /* 0x192 */ u8 inEarlyIntro; + /* 0x193 */ u8 noDrawLimbs[GOMESS_LIMB_MAX]; + /* 0x1A9 */ u8 flameAlphas[56]; + /* 0x1E2 */ Vec3s jointTable[GOMESS_LIMB_MAX]; + /* 0x266 */ Vec3s morphTable[GOMESS_LIMB_MAX]; + /* 0x2EA */ s16 floatTimer; + /* 0x2EC */ s16 weaponColliderLastUpdateTime; + /* 0x2EE */ s16 actionTimer; + /* 0x2F0 */ UNK_TYPE2 unk_2F0; + /* 0x2F2 */ s16 cloakUpperRotationModifier; + /* 0x2F4 */ s16 cloakLowerRotationModifier; + /* 0x2F6 */ s16 coreRotation; + /* 0x2F8 */ s16 explosiveDamageTimer; + /* 0x2FA */ s16 camId; + /* 0x2FC */ s16 lightArrowDamageTimer; + /* 0x300 */ s32 effectIndex; + /* 0x304 */ f32 scytheScale; + /* 0x308 */ f32 coreVelocity; + /* 0x30C */ f32 camEyeSpeed; + /* 0x310 */ f32 camAtSpeed; + /* 0x314 */ f32 dmgEffectScale; + /* 0x318 */ f32 dmgEffectSteamScale; + /* 0x31C */ f32 dmgEffectAlpha; + /* 0x320 */ Vec3f corePos; + /* 0x32C */ Vec3f scytheWorldPos; + /* 0x338 */ Vec3f scytheScreenPos; + /* 0x344 */ Vec3f camEyeTarget; + /* 0x350 */ Vec3f camAtTarget; + /* 0x35C */ Vec3f bodyPartsPos[14]; + /* 0x404 */ Vec3f sparklePositions[56]; + /* 0x6A4 */ MtxF scytheMtxF; + /* 0x6E4 */ EnMinideath* miniDeaths[20]; + /* 0x734 */ AnimatedMaterial* bodyMatAnim; + /* 0x738 */ AnimatedMaterial* coreMatAnim; + /* 0x73C */ ColliderCylinder bodyCollider; + /* 0x788 */ ColliderQuad weaponCollider; + /* 0x808 */ ColliderSphere coreCollider; + /* 0x860 */ ColliderTris weaponSpinningCollider; + /* 0x880 */ ColliderTrisElement weaponSpinningColliderElements[2]; } EnDeath; // size = 0x938 #endif // Z_EN_DEATH_H diff --git a/src/overlays/actors/ovl_En_Minideath/z_en_minideath.c b/src/overlays/actors/ovl_En_Minideath/z_en_minideath.c index c8e0bdd42e..2d4411e457 100644 --- a/src/overlays/actors/ovl_En_Minideath/z_en_minideath.c +++ b/src/overlays/actors/ovl_En_Minideath/z_en_minideath.c @@ -5,6 +5,8 @@ */ #include "z_en_minideath.h" +#include "overlays/actors/ovl_En_Death/z_en_death.h" +#include "objects/object_death/object_death.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_10) @@ -14,21 +16,29 @@ void EnMinideath_Init(Actor* thisx, PlayState* play); void EnMinideath_Destroy(Actor* thisx, PlayState* play); void EnMinideath_Update(Actor* thisx, PlayState* play); -void func_808CA860(EnMinideath* this, PlayState* play); -void func_808CA8F4(EnMinideath* this, PlayState* play); -void func_808CAAEC(EnMinideath* this, PlayState* play); -void func_808CABB0(EnMinideath* this, PlayState* play); -void func_808CACD8(EnMinideath* this, PlayState* play); -void func_808CAE18(EnMinideath* this, PlayState* play); -void func_808CAF68(EnMinideath* this, PlayState* play); -void func_808CB094(EnMinideath* this, PlayState* play); -void func_808CB22C(EnMinideath* this, PlayState* play); -void func_808CB454(EnMinideath* this, PlayState* play); -void func_808CB59C(EnMinideath* this, PlayState* play); -void func_808CB6D4(EnMinideath* this, PlayState* play); -void func_808CB7CC(EnMinideath* this, PlayState* play); +void EnMinideath_PreBattle(EnMinideath* this, PlayState* play); +void EnMinideath_Intro1(EnMinideath* this, PlayState* play); +void EnMinideath_Intro2(EnMinideath* this, PlayState* play); +void EnMinideath_CrowdParent(EnMinideath* this, PlayState* play); +void EnMinideath_Scatter(EnMinideath* this, PlayState* play); +void EnMinideath_Return(EnMinideath* this, PlayState* play); +void EnMinideath_Intro3(EnMinideath* this, PlayState* play); +void EnMinideath_StartSwarm(EnMinideath* this, PlayState* play); +void EnMinideath_SwarmFollower(EnMinideath* this, PlayState* play); +void EnMinideath_SwarmLeader(EnMinideath* this, PlayState* play); +void EnMinideath_Death1(EnMinideath* this, PlayState* play); +void EnMinideath_Die(EnMinideath* this, PlayState* play); +void EnMinideath_Dead(EnMinideath* this, PlayState* play); + +void EnMinideath_RandomizeEffects(EnMinideath* this); +void EnMinideath_SetupPreBattle(EnMinideath* this); +void EnMinideath_SetupIntro1(EnMinideath* this); +void EnMinideath_SetupIntro2(EnMinideath* this); +void EnMinideath_SetupCrowdParent(EnMinideath* this); +void EnMinideath_SetupReturn(EnMinideath* this); +void EnMinideath_SetupIntro3(EnMinideath* this); +void EnMinideath_SetupDead(EnMinideath* this); -#if 0 ActorInit En_Minideath_InitVars = { /**/ ACTOR_EN_MINIDEATH, /**/ ACTORCAT_ENEMY, @@ -41,147 +51,861 @@ ActorInit En_Minideath_InitVars = { /**/ NULL, }; -// static ColliderJntSphElementInit sJntSphElementsInit[3] = { -static ColliderJntSphElementInit D_808CBF50[3] = { +static ColliderJntSphElementInit sJntSphElementsInit[3] = { { - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x00, 0x04 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, { 1, { { 0, 0, 0 }, 15 }, 100 }, }, { - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x00, 0x04 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, { 1, { { 0, 0, 0 }, 15 }, 100 }, }, { - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x00, 0x04 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, { 1, { { 0, 0, 0 }, 15 }, 100 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_808CBFBC = { - { COLTYPE_NONE, AT_NONE | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_NONE | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_808CBF50, // sJntSphElementsInit, +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInit), + sJntSphElementsInit, }; -// static DamageTable sDamageTable = { -static DamageTable D_808CBFCC = { - /* Deku Nut */ DMG_ENTRY(0, 0x1), - /* Deku Stick */ DMG_ENTRY(1, 0x0), - /* Horse trample */ DMG_ENTRY(1, 0x0), - /* Explosives */ DMG_ENTRY(1, 0x0), - /* Zora boomerang */ DMG_ENTRY(1, 0x0), - /* Normal arrow */ DMG_ENTRY(1, 0x0), - /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0), - /* Hookshot */ DMG_ENTRY(1, 0x0), - /* Goron punch */ DMG_ENTRY(1, 0x0), - /* Sword */ DMG_ENTRY(1, 0x0), - /* Goron pound */ DMG_ENTRY(0, 0x0), - /* Fire arrow */ DMG_ENTRY(1, 0x2), - /* Ice arrow */ DMG_ENTRY(1, 0x3), - /* Light arrow */ DMG_ENTRY(2, 0x4), - /* Goron spikes */ DMG_ENTRY(1, 0x0), - /* Deku spin */ DMG_ENTRY(1, 0x0), - /* Deku bubble */ DMG_ENTRY(1, 0x0), - /* Deku launch */ DMG_ENTRY(2, 0x0), - /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0x1), - /* Zora barrier */ DMG_ENTRY(1, 0x5), - /* Normal shield */ DMG_ENTRY(0, 0x0), - /* Light ray */ DMG_ENTRY(0, 0x0), - /* Thrown object */ DMG_ENTRY(1, 0x0), - /* Zora punch */ DMG_ENTRY(1, 0x0), - /* Spin attack */ DMG_ENTRY(1, 0x0), - /* 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, 0x0), +typedef enum { + /* 0 */ DMGEFF_NONE, + /* 1 */ DMGEFF_STUN, + /* 2 */ DMGEFF_FIRE_ARROW, + /* 3 */ DMGEFF_ICE_ARROW, + /* 4 */ DMGEFF_LIGHT_ARROW, + /* 5 */ DMGEFF_ZORA_BARRIER +} EnMinideathDamageEffect; + +static DamageTable sDamageTable = { + /* Deku Nut */ DMG_ENTRY(0, DMGEFF_STUN), + /* Deku Stick */ DMG_ENTRY(1, DMGEFF_NONE), + /* Horse trample */ DMG_ENTRY(1, DMGEFF_NONE), + /* Explosives */ DMG_ENTRY(1, DMGEFF_NONE), + /* Zora boomerang */ DMG_ENTRY(1, DMGEFF_NONE), + /* Normal arrow */ DMG_ENTRY(1, DMGEFF_NONE), + /* UNK_DMG_0x06 */ DMG_ENTRY(0, DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(1, DMGEFF_NONE), + /* Goron punch */ DMG_ENTRY(1, DMGEFF_NONE), + /* Sword */ DMG_ENTRY(1, DMGEFF_NONE), + /* Goron pound */ DMG_ENTRY(0, DMGEFF_NONE), + /* Fire arrow */ DMG_ENTRY(1, DMGEFF_FIRE_ARROW), + /* Ice arrow */ DMG_ENTRY(1, DMGEFF_ICE_ARROW), + /* Light arrow */ DMG_ENTRY(2, DMGEFF_LIGHT_ARROW), + /* Goron spikes */ DMG_ENTRY(1, DMGEFF_NONE), + /* Deku spin */ DMG_ENTRY(1, DMGEFF_NONE), + /* Deku bubble */ DMG_ENTRY(1, DMGEFF_NONE), + /* Deku launch */ DMG_ENTRY(2, DMGEFF_NONE), + /* UNK_DMG_0x12 */ DMG_ENTRY(0, DMGEFF_STUN), + /* Zora barrier */ DMG_ENTRY(1, DMGEFF_ZORA_BARRIER), + /* Normal shield */ DMG_ENTRY(0, DMGEFF_NONE), + /* Light ray */ DMG_ENTRY(0, DMGEFF_NONE), + /* Thrown object */ DMG_ENTRY(1, DMGEFF_NONE), + /* Zora punch */ DMG_ENTRY(1, DMGEFF_NONE), + /* Spin attack */ DMG_ENTRY(1, DMGEFF_NONE), + /* Sword beam */ DMG_ENTRY(0, DMGEFF_NONE), + /* Normal Roll */ DMG_ENTRY(0, DMGEFF_NONE), + /* UNK_DMG_0x1B */ DMG_ENTRY(0, DMGEFF_NONE), + /* UNK_DMG_0x1C */ DMG_ENTRY(0, DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, DMGEFF_NONE), + /* UNK_DMG_0x1E */ DMG_ENTRY(0, DMGEFF_NONE), + /* Powder Keg */ DMG_ENTRY(1, DMGEFF_NONE), }; -// sColChkInfoInit -static CollisionCheckInfoInit D_808CBFEC = { 1, 15, 30, 10 }; +static CollisionCheckInfoInit sColChkInfoInit = { 1, 15, 30, 10 }; -// static InitChainEntry sInitChain[] = { -static InitChainEntry D_808CBFF4[] = { +static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 100, ICHAIN_STOP), }; -#endif +static s32 sItemDropTimer; +static s32 sNumSwarmHits; +static s32 sScatterTimer; +static s32 sPlayedDeathSfx; -extern ColliderJntSphElementInit D_808CBF50[3]; -extern ColliderJntSphInit D_808CBFBC; -extern DamageTable D_808CBFCC; -extern CollisionCheckInfoInit D_808CBFEC; -extern InitChainEntry D_808CBFF4[]; +void EnMinideath_Init(Actor* thisx, PlayState* play) { + EnMinideath* this = THIS; + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/EnMinideath_Init.s") + Actor_ProcessInitChain(thisx, sInitChain); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/EnMinideath_Destroy.s") + this->initialHeightDiffFromParent = thisx->world.pos.y - thisx->parent->world.pos.y; + this->spawnShapeYaw = thisx->shape.rot.y; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA268.s") + thisx->shape.rot.y = thisx->parent->shape.rot.y; + thisx->world.rot.y = thisx->parent->shape.rot.y; + thisx->flags &= ~ACTOR_FLAG_TARGETABLE; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA308.s") + Collider_InitAndSetJntSph(play, &this->collider, thisx, &sJntSphInit, this->colliderElements); + CollisionCheck_SetInfo(&thisx->colChkInfo, &sDamageTable, &sColChkInfoInit); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA34C.s") + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++) { + this->collider.elements[i].dim.worldSphere.radius = this->collider.elements[i].dim.modelSphere.radius; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA458.s") + EnMinideath_RandomizeEffects(this); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA7D4.s") + this->number = thisx->params; + thisx->params = MINIDEATH_ACTION_CONTINUE; + this->crowdState = false; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA860.s") + if (CHECK_EVENTINF(EVENTINF_INTRO_CS_WATCHED_GOMESS)) { + this->collider.base.atFlags |= AT_ON; + this->crowdState = true; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA8E0.s") + EnMinideath_CrowdParent(this, play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CA8F4.s") + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++) { + Math_Vec3f_Sum(&thisx->world.pos, &this->effects[i].vel, &this->effects[i].pos); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAAC8.s") + EnMinideath_SetupCrowdParent(this); + } else { + EnMinideath_SetupPreBattle(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAAEC.s") +void EnMinideath_Destroy(Actor* thisx, PlayState* play) { + EnMinideath* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAB90.s") + Collider_DestroyJntSph(play, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CABB0.s") +void EnMinideath_DropCollectible(Vec3f* spawnPos, PlayState* play) { + if (sItemDropTimer == 0 && Rand_ZeroOne() < 0.3f) { + if (Rand_ZeroOne() < 0.5f) { + Item_DropCollectible(play, spawnPos, ITEM00_ARROWS_10); + } else { + Item_DropCollectible(play, spawnPos, ITEM00_MAGIC_JAR_BIG); + } + sItemDropTimer = 800; // wait 40 seconds before next drop + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAC54.s") +void EnMinideath_UpdateCrowdState(EnMinideath* this, s32 crowdParent) { + // If the state is changed to the same as before, don't update the count + if (this->crowdState != crowdParent) { + this->crowdState = crowdParent; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CACD8.s") + // Gomess params stores the number of crowding bats + if (crowdParent == true) { + this->actor.parent->params++; + } else { + this->actor.parent->params--; + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAE00.s") +void EnMinideath_RandomizeEffects(EnMinideath* this) { + MiniDeathEffect* effect; + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAE18.s") + for (effect = this->effects, i = 0; i < MINIDEATH_NUM_EFFECTS; i++, effect++) { + effect->vel.x = Rand_CenteredFloat(80.0f); + effect->vel.y = Rand_ZeroFloat(40.0f); + effect->vel.z = Rand_CenteredFloat(80.0f); + effect->animFrame = (s32)Rand_ZeroFloat(9.0f) % MINIDEATH_ANIM_LENGTH; + effect->angle.x = (s32)Rand_Next() >> 17; + effect->angle.y = (s32)Rand_Next() >> 16; + effect->state = 0; + effect->timer = 0; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAF08.s") +void EnMinideath_UpdateEffects(EnMinideath* this, PlayState* play) { + s16 angle; + MiniDeathEffect* effect; + f32 phi_fv0; + f32 phi_fa0; + s32 i; + s32 phi_s7; + s32 phi_s3; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CAF68.s") + phi_s3 = 0; + phi_s7 = 0; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB07C.s") + effect = this->effects; + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++, effect++) { + if (effect->state == 0) { + effect->vel.x += 3.0f * Math_CosS(effect->angle.x) * Math_SinS(effect->angle.y); + effect->vel.y += 3.0f * Math_SinS(effect->angle.x); + effect->vel.z += 3.0f * Math_CosS(effect->angle.x) * Math_CosS(effect->angle.y); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB094.s") + if (this->actionFunc == EnMinideath_PreBattle) { + phi_fv0 = 100.0f; + phi_fa0 = SQ(phi_fv0); + } else if (this->actionFunc == EnMinideath_StartSwarm || this->actionFunc == EnMinideath_SwarmLeader) { + phi_fv0 = 20.0f; + phi_fa0 = SQ(20.0f); + } else { + phi_fv0 = 40.0f; + phi_fa0 = SQ(40.0f); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB0C8.s") + if (effect->vel.y < 0.0f || phi_fv0 < effect->vel.y || phi_fa0 < SQ(effect->vel.x) + SQ(effect->vel.z)) { + angle = Math_Atan2S_XY(sqrtf(SQ(effect->vel.x) + SQ(effect->vel.z)), effect->vel.y); + effect->angle.x = -1 * angle + ((s32)Rand_Next() >> 19); + angle = Math_Atan2S_XY(effect->vel.z, effect->vel.x); + effect->angle.y = 0x8000 + angle + ((s32)Rand_Next() >> 19); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB22C.s") + phi_s3++; + } else if (effect->state == 1) { + effect->pos.y += effect->vel.y; + effect->angle.y += 0x1800; + effect->vel.y -= 1.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB418.s") + if (effect->pos.y < this->actor.parent->home.pos.y) { + effect->pos.y = this->actor.parent->home.pos.y; + func_800B3030(play, &effect->pos, &gZeroVec3f, &gZeroVec3f, 100, 0, 0); + SoundSource_PlaySfxAtFixedWorldPos(play, &effect->pos, 11, NA_SE_EN_EXTINCT); + EnMinideath_DropCollectible(&effect->pos, play); + effect->state = 2; + } + } else if (effect->state == 2) { + if (effect->timer > 0) { + effect->timer--; + } else if (this->actionFunc == EnMinideath_CrowdParent) { + Math_Vec3f_Diff(&this->actor.parent->focus.pos, &this->actor.world.pos, &effect->vel); + effect->state = 0; + this->collider.elements[i].info.bumperFlags |= BUMP_ON; + this->collider.elements[i].info.toucherFlags |= TOUCH_ON; + phi_s7 = 1; + phi_s3++; + } + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB454.s") + if (phi_s7 && phi_s3 > 1) { + EnMinideath_UpdateCrowdState(this, true); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB524.s") +void EnMinideath_SetupPreBattle(EnMinideath* this) { + this->actor.speed = 6.0f; + this->actor.world.pos.x = Rand_CenteredFloat(600.0f) + this->actor.parent->world.pos.x; + this->actor.world.pos.z = Rand_CenteredFloat(600.0f) + this->actor.parent->world.pos.z; + this->actor.shape.rot.y = (s32)Rand_Next() >> 16; + this->actor.shape.rot.x = 0; + this->moveDirection.y = this->actor.shape.rot.y; + this->actionFunc = EnMinideath_PreBattle; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB59C.s") +void EnMinideath_PreBattle(EnMinideath* this, PlayState* play) { + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->moveDirection.y, 0x480) && + (this->actor.bgCheckFlags & BGCHECKFLAG_WALL)) { + // Fly away from walls + this->moveDirection.y = ((s32)Rand_Next() >> 18) + this->actor.wallYaw; + } + if (this->actor.params == MINIDEATH_ACTION_INTRO_1) { + EnMinideath_SetupIntro1(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB6A8.s") +void EnMinideath_SetupIntro1(EnMinideath* this) { + this->actionFunc = EnMinideath_Intro1; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB6D4.s") +void EnMinideath_Intro1(EnMinideath* this, PlayState* play) { + EnDeath* parent; + Vec3f sp38; + s32 phi_v0; + s16 angle; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB7B8.s") + Math_StepToF(&this->actor.speed, 15.0f, 0.5f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB7CC.s") + if (this->actor.child == NULL) { + phi_v0 = 0; + angle = 0; + } else if (this->number == 6) { + phi_v0 = 1; + angle = 0x5555; + } else if (this->number == 12) { + phi_v0 = 2; + angle = -0x5556; + } else { + phi_v0 = -1; + Math_Vec3f_Copy(&sp38, &this->actor.child->world.pos); + angle = 0; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB810.s") + if (phi_v0 != -1) { + parent = (EnDeath*)this->actor.parent; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/func_808CB8F4.s") + angle += parent->coreRotation; + sp38.x = this->actor.parent->world.pos.x + Math_SinS(angle) * parent->coreVelocity; + sp38.z = this->actor.parent->world.pos.z + Math_CosS(angle) * parent->coreVelocity; + sp38.y = this->actor.parent->world.pos.y + phi_v0 * 20; + } + Math_SmoothStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardPoint(&this->actor, &sp38), 2, 0x1000, 0x100); + if (this->actor.parent->scale.z > 0.0f) { + sp38.y = this->actor.parent->world.pos.y + this->initialHeightDiffFromParent; + } + Math_SmoothStepToS(&this->actor.shape.rot.x, Actor_WorldPitchTowardPoint(&this->actor, &sp38), 2, 0x1000, 0x100); + if (this->actor.params == MINIDEATH_ACTION_INTRO_2) { + EnMinideath_SetupIntro2(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Minideath/EnMinideath_Update.s") +void EnMinideath_SetupIntro2(EnMinideath* this) { + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->actionFunc = EnMinideath_Intro2; + this->actor.speed = 5.0f; +} + +void EnMinideath_Intro2(EnMinideath* this, PlayState* play) { + Actor_WorldDistXYZToPoint(&this->actor, &this->actor.parent->focus.pos); + Math_ScaledStepToS(&this->actor.shape.rot.y, + Actor_WorldYawTowardPoint(&this->actor, &this->actor.parent->focus.pos), 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.x, + Actor_WorldPitchTowardPoint(&this->actor, &this->actor.parent->focus.pos), 0x800); + if (this->actor.params == MINIDEATH_ACTION_INTRO_3) { + this->collider.base.atFlags |= AT_ON; + EnMinideath_SetupIntro3(this); + } +} + +void EnMinideath_SetupCrowdParent(EnMinideath* this) { + this->actor.shape.rot.x = 0; + this->actionFunc = EnMinideath_CrowdParent; + this->actor.speed = 0.0f; +} + +void EnMinideath_CrowdParent(EnMinideath* this, PlayState* play) { + s16 yaw = this->spawnShapeYaw + this->actor.parent->shape.rot.y; + + this->actor.world.pos.x = this->actor.parent->world.pos.x + Math_SinS(yaw) * 60.0f; + this->actor.world.pos.z = this->actor.parent->world.pos.z + Math_CosS(yaw) * 60.0f; + this->actor.world.pos.y = this->actor.parent->world.pos.y + this->initialHeightDiffFromParent; + this->actor.shape.rot.y = this->actor.parent->shape.rot.y; +} + +void EnMinideath_SetupScatter(EnMinideath* this) { + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->actor.speed = 8.0f; + func_800BE33C(&this->actor.parent->world.pos, &this->actor.world.pos, &this->moveDirection, false); + if (sScatterTimer != 0) { + this->timer = sScatterTimer; + } else { + this->timer = 200; + } + EnMinideath_UpdateCrowdState(this, false); + this->actionFunc = EnMinideath_Scatter; +} + +void EnMinideath_Scatter(EnMinideath* this, PlayState* play) { + f32 y; + f32 parentY; + + Math_StepToF(&this->actor.speed, 6.0f, 0.5f); + + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->moveDirection.y, 0x480) && + (this->actor.bgCheckFlags & BGCHECKFLAG_WALL)) { + // Fly away from walls + this->moveDirection.y = ((s32)Rand_Next() >> 18) + this->actor.wallYaw; + } + + if (Math_ScaledStepToS(&this->actor.shape.rot.x, this->moveDirection.x, 0x480)) { + y = this->actor.world.pos.y; + parentY = this->actor.parent->home.pos.y; + + if (y < parentY + 50.0f) { + this->moveDirection.x = -0x800 - (Rand_Next() >> 20); + } else if (y > parentY + 200.0f) { + this->moveDirection.x = 0x800 + (Rand_Next() >> 20); + } + } + + if (this->timer == 0) { + EnMinideath_SetupReturn(this); + } else { + this->timer--; + } +} + +void EnMinideath_SetupReturn(EnMinideath* this) { + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->actionFunc = EnMinideath_Return; +} + +void EnMinideath_Return(EnMinideath* this, PlayState* play) { + s32 pad; + f32 distToParent = Actor_WorldDistXYZToPoint(&this->actor, &this->actor.parent->focus.pos); + f32 velocityFactor = distToParent * (1.0f / 60.0f); + + if (velocityFactor > 1.0f) { + velocityFactor = 1.0f; + } + Math_StepToF(&this->actor.speed, 6.0f * velocityFactor, 0.5f); + Math_ScaledStepToS(&this->actor.shape.rot.y, + Actor_WorldYawTowardPoint(&this->actor, &this->actor.parent->focus.pos), 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.x, + Actor_WorldPitchTowardPoint(&this->actor, &this->actor.parent->focus.pos), 0x800); + if (distToParent < 30.0f) { + EnMinideath_SetupIntro3(this); + } +} + +void EnMinideath_SetupIntro3(EnMinideath* this) { + s32 i; + + this->actor.speed = 0.0f; + EnMinideath_UpdateCrowdState(this, true); + + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++) { + this->effects[i].timer = 0; + } + this->actionFunc = EnMinideath_Intro3; +} + +void EnMinideath_Intro3(EnMinideath* this, PlayState* play) { + Vec3f target; + f32 dist; + s16 sp26; + s32 stepDone; + + stepDone = + Math_ScaledStepToS(&this->actor.shape.rot.y, this->spawnShapeYaw + this->actor.parent->shape.rot.y, 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x800); + sp26 = this->spawnShapeYaw + this->actor.parent->shape.rot.y; + target.x = this->actor.parent->world.pos.x + Math_SinS(sp26) * 60.0f; + target.z = this->actor.parent->world.pos.z + Math_CosS(sp26) * 60.0f; + target.y = this->actor.parent->world.pos.y + this->initialHeightDiffFromParent; + + dist = Math_Vec3f_StepTo(&this->actor.world.pos, &target, 10.0f); + if (stepDone && dist < 20.0f) { + EnMinideath_SetupCrowdParent(this); + } +} + +void EnMinideath_SetupStartSwarm(EnMinideath* this) { + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->actionFunc = EnMinideath_StartSwarm; +} + +void EnMinideath_StartSwarm(EnMinideath* this, PlayState* play) { + Math_Vec3f_StepTo(&this->actor.world.pos, &this->actor.parent->focus.pos, 5.0f); +} + +void EnMinideath_SetupSwarmFollower(EnMinideath* this, PlayState* play) { + s32 phi_a2; + s32 phi_v1; + s32 phi_a1; + + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->actor.speed = 8.0f; + + if (this->number >= 10) { + phi_v1 = 19 - this->number; + phi_a1 = 1; + phi_a2 = 0; + } else { + phi_v1 = 9 - this->number; + phi_a1 = -1; + phi_a2 = 0x200; + } + this->actor.shape.rot.y = this->actor.parent->shape.rot.y + 0x4000 * phi_a1 + 0x2000 * phi_v1 * phi_a1; + this->actor.shape.rot.x = 0xC00 + phi_a2 - 0x400 * phi_v1; + + this->actor.world.pos.x = this->actor.parent->focus.pos.x + Math_SinS(this->actor.shape.rot.y) * 60.0f; + this->actor.world.pos.z = this->actor.parent->focus.pos.z + Math_CosS(this->actor.shape.rot.y) * 60.0f; + this->actor.world.pos.y = this->actor.parent->focus.pos.y - Math_SinS(this->actor.shape.rot.x) * 60.0f; + + EnMinideath_RandomizeEffects(this); + this->timer = 100; + EnMinideath_UpdateCrowdState(this, false); + + sNumSwarmHits = 0; + + if (this->actor.child == NULL || this->number == 9) { + this->targetActor = &GET_PLAYER(play)->actor; + } else { + this->targetActor = this->actor.child; + } + this->actionFunc = EnMinideath_SwarmFollower; +} + +void EnMinideath_SwarmFollower(EnMinideath* this, PlayState* play) { + Vec3f targetFocus; + s16 yawOffset = 0; + + Math_StepToF(&this->actor.speed, 6.0f, 0.2f); + if (this->timer > 0) { + this->timer--; + } + + if (this->targetActor->id != ACTOR_EN_MINIDEATH) { + if (this->timer == 0) { + this->targetActor = this->actor.parent; + } + Math_Vec3f_Copy(&targetFocus, &this->targetActor->focus.pos); + if (Actor_WorldDistXYZToPoint(&this->actor, &targetFocus) > 200.0f) { + if (this->number < 10) { + yawOffset = -0x1C00; + } else { + yawOffset = 0x1C00; + } + targetFocus.y += 50.0f; + } + } else { + if (((EnMinideath*)this->targetActor)->actionFunc != EnMinideath_SwarmFollower) { + Math_Vec3f_Copy(&targetFocus, &this->actor.parent->focus.pos); + } else { + Math_Vec3f_Copy(&targetFocus, &this->targetActor->focus.pos); + } + } + Math_SmoothStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardPoint(&this->actor, &targetFocus) + yawOffset, 5, + 0xC00, 0x80); + Math_SmoothStepToS(&this->actor.shape.rot.x, Actor_WorldPitchTowardPoint(&this->actor, &targetFocus), 5, 0xC00, + 0x80); + if ((this->collider.base.atFlags & AT_HIT) && this->timer > 0) { + sNumSwarmHits++; + } + if (this->timer == 0 && Actor_WorldDistXYZToPoint(&this->actor, &this->actor.parent->focus.pos) < 50.0f) { + EnMinideath_SetupIntro3(this); + } +} + +void EnMinideath_SetupSwarmLeader(EnMinideath* this) { + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->timer = 10; + EnMinideath_RandomizeEffects(this); + this->actionFunc = EnMinideath_SwarmLeader; +} + +void EnMinideath_SwarmLeader(EnMinideath* this, PlayState* play) { + Vec3f target; + + if (this->timer > 0) { + this->timer--; + } + target.x = (Math_SinS(this->actor.parent->shape.rot.y) * 20.0f) + this->actor.parent->focus.pos.x; + target.z = (Math_CosS(this->actor.parent->shape.rot.y) * 20.0f) + this->actor.parent->focus.pos.z; + target.y = this->actor.parent->focus.pos.y; + Math_Vec3f_StepTo(&this->actor.world.pos, &target, 5.0f); + if (this->timer == 0 && ((EnMinideath*)this->actor.child)->actionFunc != EnMinideath_SwarmFollower) { + EnMinideath_SetupCrowdParent(this); + } +} + +void EnMinideath_SetupDeath1(EnMinideath* this) { + s32 i; + + this->actor.params = MINIDEATH_ACTION_CONTINUE; + EnMinideath_UpdateCrowdState(this, false); + this->collider.base.atFlags &= ~AT_ON; + + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++) { + this->effects[i].state = 0; + } + + this->moveDirection.y = this->actor.shape.rot.y; + this->moveDirection.x = this->actor.shape.rot.x; + this->actionFunc = EnMinideath_Death1; +} + +void EnMinideath_Death1(EnMinideath* this, PlayState* play) { + f32 y; + f32 parentY; + + Math_StepToF(&this->actor.speed, 5.0f, 0.2f); + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->moveDirection.y, 0x480) && + (this->actor.bgCheckFlags & BGCHECKFLAG_WALL)) { + // Fly away from walls + this->moveDirection.y = ((s32)Rand_Next() >> 18) + this->actor.wallYaw; + } + + if (Math_ScaledStepToS(&this->actor.shape.rot.x, this->moveDirection.x, 0x480)) { + y = this->actor.world.pos.y; + parentY = this->actor.parent->home.pos.y; + if (y < parentY + 50.0f) { + this->moveDirection.x = -0x800 - (Rand_Next() >> 20); + } else if (y > parentY + 200.0f) { + this->moveDirection.x = 0x800 + (Rand_Next() >> 20); + } + } +} + +void EnMinideath_SetupDie(EnMinideath* this) { + this->actor.params = MINIDEATH_ACTION_CONTINUE; + this->moveDirection.x = 0x4000; + this->actionFunc = EnMinideath_Die; + this->actor.gravity = -0.5f; +} + +void EnMinideath_Die(EnMinideath* this, PlayState* play) { + MiniDeathEffect* effect; + s32 i; + s32 stepsComplete = 0; + + Math_StepToF(&this->actor.speed, 0.0f, 0.2f); + Math_ScaledStepToS(&this->actor.shape.rot.x, this->moveDirection.x, 0x480); + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_COMMON_EXTINCT_LEV - SFX_FLAG); + + if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) { + this->actor.speed = 0.0f; + + for (effect = this->effects, i = 0; i < MINIDEATH_NUM_EFFECTS; i++, effect++) { + if (Math_StepToF(&effect->vel.y, 0.0f, 7.0f)) { + stepsComplete++; + } + } + if (stepsComplete == MINIDEATH_NUM_EFFECTS) { + EnMinideath_SetupDead(this); + } + } +} + +void EnMinideath_SetupDead(EnMinideath* this) { + this->actionFunc = EnMinideath_Dead; +} + +void EnMinideath_Dead(EnMinideath* this, PlayState* play) { + if (this->actor.parent->update == NULL) { + Actor_Kill(&this->actor); + } else { + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_COMMON_EXTINCT_LEV - SFX_FLAG); + } +} + +void EnMinideath_SetNextAction(EnMinideath* this, PlayState* play) { + s16 action = this->actor.params; + + if (action == MINIDEATH_ACTION_DEATH_1) { + EnMinideath_SetupDeath1(this); + } else if (action == MINIDEATH_ACTION_RETURN) { + EnMinideath_SetupReturn(this); + } else if (action == MINIDEATH_ACTION_START_SWARM) { + EnMinideath_SetupStartSwarm(this); + } else if (action == MINIDEATH_ACTION_SWARM) { + if (this->number == 0 || this->number == 10) { + // Bats 0 and 10 set the general motion trajectory + EnMinideath_SetupSwarmLeader(this); + } else { + // These ones follow around 0 and 10 + EnMinideath_SetupSwarmFollower(this, play); + } + } else if (action == MINIDEATH_ACTION_SCATTER) { + EnMinideath_SetupScatter(this); + } else if (action == MINIDEATH_ACTION_DEATH_2) { + EnMinideath_SetupDie(this); + } +} + +void EnMinideath_UpdateDamage(EnMinideath* this, PlayState* play) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + + if (this->actor.colChkInfo.damageEffect == DMGEFF_STUN) { + if (this->actionFunc == EnMinideath_CrowdParent || this->actionFunc == EnMinideath_Intro3) { + sScatterTimer = 5; + } else if (this->actionFunc == EnMinideath_SwarmFollower) { + EnMinideath** miniDeaths = ((EnDeath*)this->actor.parent)->miniDeaths; + s32 i; + + for (i = this->number - 1; i >= 0; i--) { + EnMinideath* other = miniDeaths[i]; + + if (other->targetActor == &this->actor) { + other->targetActor = this->targetActor; + break; + } + } + + this->timer = 0; + this->targetActor = &GET_PLAYER(play)->actor; + } + } else { + s32 i; + s32 j; + s32 phi_a0; + + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++) { + if (this->collider.elements[i].info.bumperFlags & BUMP_HIT) { + this->collider.elements[i].info.bumperFlags &= ~(BUMP_ON | BUMP_HIT); + this->collider.elements[i].info.toucherFlags &= ~(TOUCH_ON | TOUCH_HIT); + this->effects[i].vel.y = -1.0f; + this->effects[i].state = 1; + this->effects[i].angle.y = this->actor.shape.rot.y; + this->effects[i].timer = 60; + + if ((this->actionFunc == EnMinideath_CrowdParent || this->actionFunc == EnMinideath_Intro3) && + this->actor.colChkInfo.damageEffect == DMGEFF_LIGHT_ARROW) { + sScatterTimer = 200; + } + } + } + + phi_a0 = 0; + for (j = 0; j < MINIDEATH_NUM_EFFECTS; j++) { + if (this->effects[j].state != 0) { + phi_a0++; + } + } + + if (phi_a0 > 1) { + EnMinideath_UpdateCrowdState(this, false); + } + + if (!sPlayedDeathSfx) { + Actor_PlaySfx(&this->actor, NA_SE_EN_FFLY_DEAD); + sPlayedDeathSfx = true; + } + } + } +} + +void EnMinideath_Update(Actor* thisx, PlayState* play) { + EnMinideath* this = THIS; + s32 pad; + ColliderJntSphElement* elem; + s32 temp; + s32 i; + MiniDeathEffect* effect; + + if (sItemDropTimer > 0) { + sItemDropTimer--; + } + + if (this->actionFunc != EnMinideath_Dead) { + for (i = 0; i < MINIDEATH_NUM_EFFECTS; i++) { + if (this->effects[i].state == 0) { + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EV_BAT_FLY - SFX_FLAG); + break; + } + } + } + + EnMinideath_UpdateDamage(this, play); + + EnMinideath_SetNextAction(this, play); + this->actionFunc(this, play); + + if (this->actionFunc != EnMinideath_Dead) { + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.world.rot.x = -this->actor.shape.rot.x; + + if (this->actionFunc == EnMinideath_Die) { + Actor_MoveWithGravity(&this->actor); + } else { + Actor_MoveWithoutGravity(&this->actor); + } + + if (this->actionFunc != EnMinideath_CrowdParent && this->actionFunc != EnMinideath_Intro3) { + Actor_UpdateBgCheckInfo(play, &this->actor, 30.0f, 50.0f, 80.0f, 7); + } + + if (this->actionFunc != EnMinideath_Die) { + EnMinideath_UpdateEffects(this, play); + } + + Actor_SetFocus(&this->actor, 0.0f); + + effect = this->effects; + elem = this->collider.elements; + for (i = 0; i != MINIDEATH_NUM_EFFECTS; i++) { + if (effect->state == 0) { + Math_Vec3f_Sum(&this->actor.world.pos, &effect->vel, &effect->pos); + + if (this->actionFunc != EnMinideath_Die) { + effect->animFrame++; + if (effect->animFrame == MINIDEATH_ANIM_LENGTH) { + effect->animFrame = 0; + } + } + + elem->dim.worldSphere.center.x = effect->pos.x; + elem->dim.worldSphere.center.y = effect->pos.y; + elem->dim.worldSphere.center.z = effect->pos.z; + } + effect++; + elem++; + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + this->attackTimer = 40; + Actor_PlaySfx(&this->actor, NA_SE_EN_FFLY_ATTACK); + } + if (this->collider.base.atFlags & AT_ON) { + temp = (this->actionFunc == EnMinideath_CrowdParent) ? (play->gameplayFrames % 4) : 4; + + if (temp == 4 || temp == (this->number & 3)) { + if (this->attackTimer == 0 && this->actor.parent->shape.rot.x == 0) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); + } + if (this->actionFunc != EnMinideath_StartSwarm) { + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + } + } + } + if (this->attackTimer > 0) { + this->attackTimer--; + } + if (this->number == 19) { + EnMinideath* other = NULL; + sPlayedDeathSfx = false; + + if (sScatterTimer != 0) { + do { + other = (EnMinideath*)SubS_FindActor(play, &other->actor, ACTORCAT_ENEMY, ACTOR_EN_MINIDEATH); + if (other != NULL) { + EnMinideath_SetupScatter(other); + other = (EnMinideath*)other->actor.next; + } + } while (other != NULL); + + play->envCtx.lightSettingOverride = 28; + + if (sScatterTimer == 5) { + ((EnDeath*)this->actor.parent)->lightArrowDamageTimer = 5; + } else { + ((EnDeath*)this->actor.parent)->lightArrowDamageTimer = 20; + } + sScatterTimer = 0; + } else if (sNumSwarmHits >= 5) { + do { + other = (EnMinideath*)SubS_FindActor(play, &other->actor, ACTORCAT_ENEMY, ACTOR_EN_MINIDEATH); + if (other != NULL) { + other->timer = 0; + other = (EnMinideath*)other->actor.next; + } + } while (other != NULL); + sNumSwarmHits = 0; + } + } + } +} diff --git a/src/overlays/actors/ovl_En_Minideath/z_en_minideath.h b/src/overlays/actors/ovl_En_Minideath/z_en_minideath.h index 0a08318abd..0e42ce7f02 100644 --- a/src/overlays/actors/ovl_En_Minideath/z_en_minideath.h +++ b/src/overlays/actors/ovl_En_Minideath/z_en_minideath.h @@ -7,10 +7,47 @@ struct EnMinideath; typedef void (*EnMinideathActionFunc)(struct EnMinideath*, PlayState*); +#define MINIDEATH_NUM_EFFECTS 3 + +// "animation" length +#define MINIDEATH_ANIM_LENGTH 9 + +typedef enum { + /* 0 */ MINIDEATH_ACTION_CONTINUE, // continue with current action until it is changed + /* 1 */ MINIDEATH_ACTION_SCATTER, // scatter after light arrows? + /* 2 */ MINIDEATH_ACTION_RETURN, // gomess damaged + /* 3 */ MINIDEATH_ACTION_START_SWARM, + /* 4 */ MINIDEATH_ACTION_SWARM, // chase player? + /* 5 */ MINIDEATH_ACTION_DEATH_2, // death cs part 3 + /* 6 */ MINIDEATH_ACTION_DEATH_1, // death cs part 1 + /* 7 */ MINIDEATH_ACTION_INTRO_1, // intro cs part 2 + /* 8 */ MINIDEATH_ACTION_INTRO_2, // intro cs part 5 (1) + /* 9 */ MINIDEATH_ACTION_INTRO_3 // intro cs part 5 (2) +} MinideathAction; + +typedef struct { + /* 0x00 */ u8 animFrame; + /* 0x01 */ u8 state; + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f vel; + /* 0x1C */ Vec3s angle; + /* 0x22 */ s16 timer; +} MiniDeathEffect; // size = 0x24 + typedef struct EnMinideath { /* 0x000 */ Actor actor; /* 0x144 */ EnMinideathActionFunc actionFunc; - /* 0x148 */ char unk_148[0x164]; + /* 0x148 */ u8 number; + /* 0x149 */ u8 crowdState; + /* 0x14A */ s16 timer; + /* 0x14C */ s16 spawnShapeYaw; + /* 0x14E */ s16 attackTimer; + /* 0x150 */ Vec3s moveDirection; + /* 0x158 */ f32 initialHeightDiffFromParent; + /* 0x15C */ Actor* targetActor; + /* 0x160 */ MiniDeathEffect effects[MINIDEATH_NUM_EFFECTS]; + /* 0x1CC */ ColliderJntSph collider; + /* 0x1EC */ ColliderJntSphElement colliderElements[MINIDEATH_NUM_EFFECTS]; } EnMinideath; // size = 0x2AC #endif // Z_EN_MINIDEATH_H diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 5ad93e571f..3b259bea77 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -6188,10 +6188,10 @@ 0x808C7888:("func_808C7888",), 0x808C7A30:("func_808C7A30",), 0x808C7AAC:("func_808C7AAC",), - 0x808C7AEC:("func_808C7AEC",), - 0x808C7B88:("func_808C7B88",), - 0x808C7C04:("func_808C7C04",), - 0x808C7C88:("func_808C7C88",), + 0x808C7AEC:("EnDeath_SetupStartBatSwarm",), + 0x808C7B88:("EnDeath_StartBatSwarm",), + 0x808C7C04:("EnDeath_SetupBatSwarm",), + 0x808C7C88:("EnDeath_BatSwarm",), 0x808C7CFC:("func_808C7CFC",), 0x808C7D34:("func_808C7D34",), 0x808C7DB8:("func_808C7DB8",), @@ -6209,38 +6209,38 @@ 0x808C96C8:("EnDeath_Draw",), 0x808CA0B0:("EnMinideath_Init",), 0x808CA23C:("EnMinideath_Destroy",), - 0x808CA268:("func_808CA268",), - 0x808CA308:("func_808CA308",), - 0x808CA34C:("func_808CA34C",), - 0x808CA458:("func_808CA458",), - 0x808CA7D4:("func_808CA7D4",), - 0x808CA860:("func_808CA860",), - 0x808CA8E0:("func_808CA8E0",), - 0x808CA8F4:("func_808CA8F4",), - 0x808CAAC8:("func_808CAAC8",), - 0x808CAAEC:("func_808CAAEC",), - 0x808CAB90:("func_808CAB90",), - 0x808CABB0:("func_808CABB0",), - 0x808CAC54:("func_808CAC54",), - 0x808CACD8:("func_808CACD8",), - 0x808CAE00:("func_808CAE00",), - 0x808CAE18:("func_808CAE18",), - 0x808CAF08:("func_808CAF08",), - 0x808CAF68:("func_808CAF68",), - 0x808CB07C:("func_808CB07C",), - 0x808CB094:("func_808CB094",), - 0x808CB0C8:("func_808CB0C8",), - 0x808CB22C:("func_808CB22C",), - 0x808CB418:("func_808CB418",), - 0x808CB454:("func_808CB454",), - 0x808CB524:("func_808CB524",), - 0x808CB59C:("func_808CB59C",), - 0x808CB6A8:("func_808CB6A8",), - 0x808CB6D4:("func_808CB6D4",), - 0x808CB7B8:("func_808CB7B8",), - 0x808CB7CC:("func_808CB7CC",), - 0x808CB810:("func_808CB810",), - 0x808CB8F4:("func_808CB8F4",), + 0x808CA268:("EnMinideath_DropCollectible",), + 0x808CA308:("EnMinideath_UpdateCrowdState",), + 0x808CA34C:("EnMinideath_RandomizeEffects",), + 0x808CA458:("EnMinideath_UpdateEffects",), + 0x808CA7D4:("EnMinideath_SetupPreBattle",), + 0x808CA860:("EnMinideath_PreBattle",), + 0x808CA8E0:("EnMinideath_SetupIntro1",), + 0x808CA8F4:("EnMinideath_Intro1",), + 0x808CAAC8:("EnMinideath_SetupIntro2",), + 0x808CAAEC:("EnMinideath_Intro2",), + 0x808CAB90:("EnMinideath_SetupCrowdParent",), + 0x808CABB0:("EnMinideath_CrowdParent",), + 0x808CAC54:("EnMinideath_SetupScatter",), + 0x808CACD8:("EnMinideath_Scatter",), + 0x808CAE00:("EnMinideath_SetupReturn",), + 0x808CAE18:("EnMinideath_Return",), + 0x808CAF08:("EnMinideath_SetupIntro3",), + 0x808CAF68:("EnMinideath_Intro3",), + 0x808CB07C:("EnMinideath_SetupStartSwarm",), + 0x808CB094:("EnMinideath_StartSwarm",), + 0x808CB0C8:("EnMinideath_SetupSwarmFollower",), + 0x808CB22C:("EnMinideath_SwarmFollower",), + 0x808CB418:("EnMinideath_SetupSwarmLeader",), + 0x808CB454:("EnMinideath_SwarmLeader",), + 0x808CB524:("EnMinideath_SetupDeath1",), + 0x808CB59C:("EnMinideath_Death1",), + 0x808CB6A8:("EnMinideath_SetupDie",), + 0x808CB6D4:("EnMinideath_Die",), + 0x808CB7B8:("EnMinideath_SetupDead",), + 0x808CB7CC:("EnMinideath_Dead",), + 0x808CB810:("EnMinideath_SetNextAction",), + 0x808CB8F4:("EnMinideath_UpdateDamage",), 0x808CBB18:("EnMinideath_Update",), 0x808CC260:("EnVm_Init",), 0x808CC3E0:("EnVm_Destroy",),