From 122a050d44cae24319ba6924dd0738254aef4120 Mon Sep 17 00:00:00 2001 From: petrie911 <69443847+petrie911@users.noreply.github.com> Date: Sat, 5 Nov 2022 22:16:55 -0500 Subject: [PATCH] EnEgol (Eyegore), object_eg, and related actors EnEstone and EnEgblock (#1041) * EnEgol (Eyegore), object_eg, and related actors EnEstone and EnEgblock Co-authored-by: petrie911 <69443847+petrie911@users.noreply.github.com> * merge fixes --- assets/xml/objects/object_eg.xml | 252 ++- spec | 9 +- .../actors/ovl_En_Egblock/z_en_egblock.c | 210 +- .../actors/ovl_En_Egblock/z_en_egblock.h | 35 +- src/overlays/actors/ovl_En_Egol/z_en_egol.c | 1691 +++++++++++++++-- src/overlays/actors/ovl_En_Egol/z_en_egol.h | 82 +- .../actors/ovl_En_Estone/z_en_estone.c | 265 ++- .../actors/ovl_En_Estone/z_en_estone.h | 25 +- tools/disasm/functions.txt | 98 +- 9 files changed, 2335 insertions(+), 332 deletions(-) diff --git a/assets/xml/objects/object_eg.xml b/assets/xml/objects/object_eg.xml index 9cda7f0a34..6aba85e623 100644 --- a/assets/xml/objects/object_eg.xml +++ b/assets/xml/objects/object_eg.xml @@ -1,106 +1,154 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec b/spec index 0b2dab096a..d923922359 100644 --- a/spec +++ b/spec @@ -2948,8 +2948,7 @@ beginseg name "ovl_En_Egol" compress include "build/src/overlays/actors/ovl_En_Egol/z_en_egol.o" - include "build/data/ovl_En_Egol/ovl_En_Egol.data.o" - include "build/data/ovl_En_Egol/ovl_En_Egol.reloc.o" + include "build/src/overlays/actors/ovl_En_Egol/ovl_En_Egol_reloc.o" endseg beginseg @@ -3011,8 +3010,7 @@ beginseg name "ovl_En_Estone" compress include "build/src/overlays/actors/ovl_En_Estone/z_en_estone.o" - include "build/data/ovl_En_Estone/ovl_En_Estone.data.o" - include "build/data/ovl_En_Estone/ovl_En_Estone.reloc.o" + include "build/src/overlays/actors/ovl_En_Estone/ovl_En_Estone_reloc.o" endseg beginseg @@ -3125,8 +3123,7 @@ beginseg name "ovl_En_Egblock" compress include "build/src/overlays/actors/ovl_En_Egblock/z_en_egblock.o" - include "build/data/ovl_En_Egblock/ovl_En_Egblock.data.o" - include "build/data/ovl_En_Egblock/ovl_En_Egblock.reloc.o" + include "build/src/overlays/actors/ovl_En_Egblock/ovl_En_Egblock_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_En_Egblock/z_en_egblock.c b/src/overlays/actors/ovl_En_Egblock/z_en_egblock.c index 2a1a976f5e..de26515fab 100644 --- a/src/overlays/actors/ovl_En_Egblock/z_en_egblock.c +++ b/src/overlays/actors/ovl_En_Egblock/z_en_egblock.c @@ -1,21 +1,34 @@ /* * File: z_en_egblock.c * Overlay: ovl_En_Egblock - * Description: Eyegore Block + * Description: Unused pillar. Eyegore can destroy it. */ #include "z_en_egblock.h" +#include "objects/object_eg/object_eg.h" #define FLAGS (ACTOR_FLAG_8000000) #define THIS ((EnEgblock*)thisx) +typedef enum { + /* 0 */ EGBLOCK_EFFECT_DEBRIS_SOLID, + /* 1 */ EGBLOCK_EFFECT_DEBRIS_FLAT, +} EnEgblockEffectType; + void EnEgblock_Init(Actor* thisx, PlayState* play); void EnEgblock_Destroy(Actor* thisx, PlayState* play); void EnEgblock_Update(Actor* thisx, PlayState* play); -void EnEgblock_Draw(Actor* thisx, PlayState* play); +void EnEgblock_Draw(Actor* thisx, PlayState* play2); + +void EnEgblock_Active(EnEgblock* this, PlayState* play); +void EnEgblock_DoNothing(EnEgblock* this, PlayState* play); +void EnEgblock_Inactive(EnEgblock* this, PlayState* play); + +void EnEgblock_SpawnEffect(EnEgblock* this, Vec3f* pos, s16 lifetime, s16 arg3); +void EnEgblock_UpdateEffects(EnEgblock* this, PlayState* play); +void EnEgblock_DrawEffects(EnEgblock* this, PlayState* play); -#if 0 ActorInit En_Egblock_InitVars = { ACTOR_EN_EGBLOCK, ACTORCAT_PROP, @@ -28,28 +41,191 @@ ActorInit En_Egblock_InitVars = { (ActorFunc)EnEgblock_Draw, }; -#endif +void EnEgblock_Init(Actor* thisx, PlayState* play) { + EnEgblock* this = THIS; + CollisionHeader* colHeader = NULL; + s32 pad; -extern UNK_TYPE D_06001698; -extern UNK_TYPE D_06001820; -extern UNK_TYPE D_06001BF8; + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&gEyegoreBlockCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, colHeader); + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; + this->inactive = false; + Actor_SetScale(&this->dyna.actor, 0.5f); + this->type = EGBLOCK_GET_TYPE(&this->dyna.actor); + this->paramF80 = EGBLOCK_GET_PARAM_F80(&this->dyna.actor); + this->param7F = EGBLOCK_GET_PARAM_7F(&this->dyna.actor); + if (this->type == EGBLOCK_TYPE_ACTIVE) { + this->dyna.actor.colChkInfo.health = 1; + this->actionFunc = EnEgblock_Active; + } else { + this->actionFunc = EnEgblock_DoNothing; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/EnEgblock_Init.s") +void EnEgblock_Destroy(Actor* thisx, PlayState* play) { + EnEgblock* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/EnEgblock_Destroy.s") + if (this->dyna.actor.colChkInfo.health == 1) { + DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/func_80ABA8A4.s") +void EnEgblock_Active(EnEgblock* this, PlayState* play) { + if (this->dyna.actor.colChkInfo.health <= 0) { + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/func_80ABA988.s") + //! @bug Egblock doesn't have an effect with index 2, so half of these spawns do nothing. + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + EnEgblock_SpawnEffect(this, &this->dyna.actor.world.pos, 30, (i & 1) + EGBLOCK_EFFECT_DEBRIS_FLAT); + } + DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); + Actor_SpawnFloorDustRing(play, &this->dyna.actor, &this->dyna.actor.world.pos, 30.0f, 30, 10.0f, 100, 30, true); + this->inactive = true; + this->timer = 50; + this->actionFunc = EnEgblock_Inactive; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/func_80ABA9B8.s") +void EnEgblock_Inactive(EnEgblock* this, PlayState* play) { + if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/EnEgblock_Update.s") +void EnEgblock_DoNothing(EnEgblock* this, PlayState* play) { +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/EnEgblock_Draw.s") +void EnEgblock_Update(Actor* thisx, PlayState* play) { + EnEgblock* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/func_80ABAAF4.s") + this->actionFunc(this, play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/func_80ABACB4.s") + DECR(this->timer); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egblock/func_80ABAE64.s") + EnEgblock_UpdateEffects(this, play); +} + +void EnEgblock_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnEgblock* this = THIS; + + func_8012C28C(play->state.gfxCtx); + + if (this->inactive != true) { + OPEN_DISPS(play->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x80, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + gSPDisplayList(POLY_OPA_DISP++, gEyegoreBlockDL); + + CLOSE_DISPS(play->state.gfxCtx); + } + + EnEgblock_DrawEffects(this, play); +} + +void EnEgblock_SpawnEffect(EnEgblock* this, Vec3f* pos, s16 lifetime, s16 arg3) { + EnEgblockEffect* effect = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (!effect->isActive) { + effect->isActive = true; + effect->pos = *pos; + effect->pos.x += randPlusMinusPoint5Scaled(50.0f); + effect->pos.z += randPlusMinusPoint5Scaled(50.0f); + effect->timer = lifetime; + effect->alpha = 255; + effect->type = arg3; + effect->accel.x = Rand_ZeroOne() - 0.5f; + effect->accel.y = -1.0f; + effect->accel.z = Rand_ZeroOne() - 0.5f; + effect->velocity.x = 2.0f * (Rand_ZeroOne() - 0.5f); + effect->velocity.y = 5.0f + (10.0f * Rand_ZeroOne()); + effect->velocity.z = 2.0f * (Rand_ZeroOne() - 0.5f); + effect->scale = 0.5f + (0.2f * Rand_ZeroFloat(1.0f)); + effect->rot.x = randPlusMinusPoint5Scaled(0x7530); + effect->rot.y = randPlusMinusPoint5Scaled(0x7530); + effect->rot.z = randPlusMinusPoint5Scaled(0x7530); + break; + } + } +} + +void EnEgblock_UpdateEffects(EnEgblock* this, PlayState* play) { + EnEgblockEffect* effect = this->effects; + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (effect->isActive) { + if (effect->type != EGBLOCK_EFFECT_DEBRIS_SOLID) { + effect->rot.x -= 0x64; + effect->rot.y -= 0x64; + effect->rot.z -= 0x64; + } + + effect->pos.x += effect->velocity.x; + effect->pos.y += effect->velocity.y; + effect->pos.z += effect->velocity.z; + + effect->velocity.x += effect->accel.x; + effect->velocity.y += effect->accel.y; + effect->velocity.z += effect->accel.z; + + if (effect->timer) { + effect->timer--; + } else { + effect->alpha -= 10; + if (effect->alpha < 10) { + effect->isActive = false; + } + } + } + } +} + +void EnEgblock_DrawEffects(EnEgblock* this, PlayState* play) { + GraphicsContext* gfxCtx = play->state.gfxCtx; + EnEgblockEffect* effect = this->effects; + s16 i; + + OPEN_DISPS(gfxCtx); + + func_8012C28C(play->state.gfxCtx); + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (effect->isActive) { + switch (effect->type) { + case EGBLOCK_EFFECT_DEBRIS_SOLID: + case EGBLOCK_EFFECT_DEBRIS_FLAT: + Matrix_Push(); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_RotateYS(effect->rot.y, MTXMODE_APPLY); + Matrix_RotateXS(effect->rot.x, MTXMODE_APPLY); + Matrix_RotateZS(effect->rot.z, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x80, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, effect->alpha); + + if (effect->type == EGBLOCK_EFFECT_DEBRIS_SOLID) { + gSPDisplayList(POLY_OPA_DISP++, gEyegoreEffectSolidDebrisDL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gEyegoreEffectFlatDebrisDL); + } + + Matrix_Pop(); + break; + + default: + break; + } + } + } + + CLOSE_DISPS(gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Egblock/z_en_egblock.h b/src/overlays/actors/ovl_En_Egblock/z_en_egblock.h index 88abf1efea..2d875c4d27 100644 --- a/src/overlays/actors/ovl_En_Egblock/z_en_egblock.h +++ b/src/overlays/actors/ovl_En_Egblock/z_en_egblock.h @@ -7,11 +7,40 @@ struct EnEgblock; typedef void (*EnEgblockActionFunc)(struct EnEgblock*, PlayState*); +#define EGBLOCK_GET_PARAM_7F(thisx) ((thisx)->params & 0x7F) +#define EGBLOCK_GET_PARAM_F80(thisx) (((thisx)->params >> 7) & 0x1F) +#define EGBLOCK_GET_TYPE(thisx) (((thisx)->params >> 12) & 0xF) + +#define EGBLOCK_PARAMS(type, param1, param2) ((((type) & 0xF) << 12) | (((param1) & 0x1F) << 7) | ((param2) & 0x7F)) + +#define EGBLOCK_TYPE_ACTIVE 0 + +#define EGBLOCK_EFFECT_COUNT 50 + +typedef struct { + /* 0x00 */ u8 isActive; + /* 0x04 */ Vec3f pos; + /* 0x10 */ s16 alpha; + /* 0x12 */ s16 timer; + /* 0x14 */ s16 type; + /* 0x18 */ Vec3f velocity; + /* 0x24 */ Vec3f accel; + /* 0x30 */ Vec3s rot; + /* 0x38 */ f32 scale; +} EnEgblockEffect; // size = 0x3C + typedef struct EnEgblock { - /* 0x000 */ Actor actor; - /* 0x144 */ char unk_144[0x5C]; + /* 0x000 */ DynaPolyActor dyna; + /* 0x15C */ UNK_TYPE1 unk_15C[0x44]; // SkelAnime? /* 0x1A0 */ EnEgblockActionFunc actionFunc; - /* 0x1A4 */ char unk_1A4[0xC10]; + /* 0x1A4 */ s16 type; + /* 0x1A6 */ s16 paramF80; + /* 0x1A8 */ s16 param7F; + /* 0x1AA */ s16 timer; + /* 0x1AC */ s16 inactive; + /* 0x1AE */ UNK_TYPE1 unk_1AE[2]; + /* 0x1B0 */ UNK_TYPE1 unk_1B0[0x4C]; // ColliderCylinder? + /* 0x1FC */ EnEgblockEffect effects[EGBLOCK_EFFECT_COUNT]; } EnEgblock; // size = 0xDB4 #endif // Z_EN_EGBLOCK_H diff --git a/src/overlays/actors/ovl_En_Egol/z_en_egol.c b/src/overlays/actors/ovl_En_Egol/z_en_egol.c index 6b81852491..7314974368 100644 --- a/src/overlays/actors/ovl_En_Egol/z_en_egol.c +++ b/src/overlays/actors/ovl_En_Egol/z_en_egol.c @@ -5,106 +5,292 @@ */ #include "z_en_egol.h" +#include "objects/object_eg/object_eg.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/actors/ovl_En_Estone/z_en_estone.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "overlays/effects/ovl_Effect_Ss_Hitmark/z_eff_ss_hitmark.h" #define FLAGS (ACTOR_FLAG_1 | ACTOR_FLAG_4 | ACTOR_FLAG_10 | ACTOR_FLAG_20 | ACTOR_FLAG_80000000) #define THIS ((EnEgol*)thisx) +typedef enum { + /* 0 */ EYEGORE_ACTION_WAIT, + /* 1 */ EYEGORE_ACTION_STAND, + /* 2 */ EYEGORE_ACTION_WALK, + /* 3 */ EYEGORE_ACTION_RETREAT, + /* 4 */ EYEGORE_ACTION_LASER, + /* 5 */ EYEGORE_ACTION_SLAM, + /* 6 */ EYEGORE_ACTION_PUNCH, + /* 7 */ EYEGORE_ACTION_SLAM_WAIT, + /* 8 */ EYEGORE_ACTION_STUNNED, + /* 9 */ EYEGORE_ACTION_STUN_END, + /* 10 */ EYEGORE_ACTION_SLAM_END, + /* 11 */ EYEGORE_ACTION_DAMAGED, + /* 12 */ EYEGORE_ACTION_STOP, + /* 13 */ EYEGORE_ACTION_DYING, + /* 14 */ EYEGORE_ACTION_DEAD +} EnEgolAction; + +typedef enum { + /* 0 */ EYEGORE_EFFECT_IMPACT, + /* 1 */ EYEGORE_EFFECT_PIECE_LARGE, + /* 2 */ EYEGORE_EFFECT_PIECE_SMALL, + /* 3 */ EYEGORE_EFFECT_DEBRIS, +} EnEgolEffectType; + +typedef enum { + /* 0 */ EYEGORE_ANIM_STAND, + /* 1 */ EYEGORE_ANIM_WALK, + /* 2 */ EYEGORE_ANIM_SLAM, + /* 3 */ EYEGORE_ANIM_SLAM_WAIT, + /* 4 */ EYEGORE_ANIM_SLAM_END, + /* 5 */ EYEGORE_ANIM_DAMAGED, + /* 6 */ EYEGORE_ANIM_DEATH, + /* 7 */ EYEGORE_ANIM_LASER, + /* 8 */ EYEGORE_ANIM_LASER_END, // unused + /* 9 */ EYEGORE_ANIM_STUNNED, + /* 10 */ EYEGORE_ANIM_STUN_END, + /* 11 */ EYEGORE_ANIM_RETREAT, + /* 12 */ EYEGORE_ANIM_SIT, + /* 13 */ EYEGORE_ANIM_LEFT_PUNCH, + /* 14 */ EYEGORE_ANIM_RIGHT_PUNCH, + /* 15 */ EYEGORE_ANIM_MAX, +} EnEgolAnimation; + +typedef enum { + /* 0 */ EYEGORE_LASER_OFF, + /* 1 */ EYEGORE_LASER_START, + /* 2 */ EYEGORE_LASER_CHARGING, + /* 3 */ EYEGORE_LASER_FIRE, + /* 7 */ EYEGORE_LASER_ON = 7, +} EnEgolLaserState; + void EnEgol_Init(Actor* thisx, PlayState* play); void EnEgol_Destroy(Actor* thisx, PlayState* play); void EnEgol_Update(Actor* thisx, PlayState* play); -void EnEgol_Draw(Actor* thisx, PlayState* play); +void EnEgol_Draw(Actor* thisx, PlayState* play2); -#if 0 -// static ColliderJntSphElementInit sJntSphElementsInit[6] = { -static ColliderJntSphElementInit D_80A80C64[6] = { +void EnEgol_SetupWait(EnEgol* this); +void EnEgol_Wait(EnEgol* this, PlayState* play); +void EnEgol_SetupStand(EnEgol* this); +void EnEgol_Stand(EnEgol* this, PlayState* play); +void EnEgol_SetupWalk(EnEgol* this); +void EnEgol_Walk(EnEgol* this, PlayState* play); +void EnEgol_SetupRetreat(EnEgol* this); +void EnEgol_Retreat(EnEgol* this, PlayState* play); +void EnEgol_SetupLaser(EnEgol* this); +void EnEgol_Laser(EnEgol* this, PlayState* play); +void EnEgol_SetupStop(EnEgol* this); +void EnEgol_Stop(EnEgol* this, PlayState* play); +void EnEgol_SetupSlam(EnEgol* this); +void EnEgol_Slam(EnEgol* this, PlayState* play); +void EnEgol_SetupPunch(EnEgol* this); +void EnEgol_Punch(EnEgol* this, PlayState* play); +void EnEgol_SetupSlamWait(EnEgol* this); +void EnEgol_SlamWait(EnEgol* this, PlayState* play); +void EnEgol_SetupStunned(EnEgol* this); +void EnEgol_Stunned(EnEgol* this, PlayState* play); +void EnEgol_SetupStunEnd(EnEgol* this); +void EnEgol_StunEnd(EnEgol* this, PlayState* play); +void EnEgol_SetupSlamEnd(EnEgol* this); +void EnEgol_SlamEnd(EnEgol* this, PlayState* play); +void EnEgol_SetupDamaged(EnEgol* this); +void EnEgol_Damaged(EnEgol* this, PlayState* play); +void EnEgol_StartDeathCutscene(EnEgol* this, PlayState* play); +void EnEgol_Death(EnEgol* this, PlayState* play); + +void EnEgol_SpawnEffect(EnEgol* this, Vec3f* pos, Vec3s* rot, s16 lifetime, f32 scale, s16 type); +void EnEgol_UpdateEffects(EnEgol* this, PlayState* play); +void EnEgol_DrawEffects(EnEgol* this, PlayState* play); + +static ColliderJntSphElementInit sEyeJntSphElementsInit[1] = { { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x10 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, - { 6, { { 0, 0, 0 }, 0 }, 1 }, - }, - { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x10 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, - { 9, { { 0, 0, 0 }, 0 }, 1 }, - }, - { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, - { 3, { { 0, 0, 0 }, 0 }, 1 }, - }, - { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, - { 4, { { 0, 0, 0 }, 0 }, 1 }, - }, - { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, - { 7, { { 0, 0, 0 }, 0 }, 1 }, - }, - { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, - { 23, { { 0, 0, 0 }, 0 }, 1 }, + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { EYEGORE_LIMB_EYE_COLLIDER, { { 0, 0, 0 }, 0 }, 1 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_80A80D3C = { - { COLTYPE_METAL, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_80A80C64, // sJntSphElementsInit, -}; - -// static ColliderJntSphElementInit sJntSphElementsInit[1] = { -static ColliderJntSphElementInit D_80A80C40[1] = { +static ColliderJntSphElementInit sBodySphElementsInit[6] = { { - { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, - { 14, { { 0, 0, 0 }, 0 }, 1 }, + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x04, 0x10 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { EYEGORE_LIMB_LEFT_HAND, { { 0, 0, 0 }, 0 }, 1 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x04, 0x10 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { EYEGORE_LIMB_RIGHT_HAND, { { 0, 0, 0 }, 0 }, 1 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { EYEGORE_LIMB_HEAD, { { 0, 0, 0 }, 0 }, 1 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { EYEGORE_LIMB_LEFT_SHOULDER, { { 0, 0, 0 }, 0 }, 1 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { EYEGORE_LIMB_RIGHT_SHOULDER, { { 0, 0, 0 }, 0 }, 1 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { EYEGORE_LIMB_FAULDS, { { 0, 0, 0 }, 0 }, 1 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_80A80D4C = { - { COLTYPE_NONE, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_80A80C40, // sJntSphElementsInit, +static ColliderJntSphInit sBodyJntSphInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sBodySphElementsInit), + sBodySphElementsInit, }; -// static ColliderQuadInit sQuadInit = { -static ColliderQuadInit D_80A80D5C = { - { COLTYPE_NONE, AT_ON | AT_TYPE_ENEMY, AC_NONE, OC1_NONE, OC2_NONE, COLSHAPE_QUAD, }, - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x07, 0x10 }, { 0x00000000, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, BUMP_NONE, OCELEM_NONE, }, +static ColliderJntSphInit sEyeJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sEyeJntSphElementsInit), + sEyeJntSphElementsInit, +}; + +static ColliderQuadInit sLaserQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x07, 0x10 }, + { 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_80A80DAC = { - /* Deku Nut */ DMG_ENTRY(0, 0xF), - /* Deku Stick */ DMG_ENTRY(0, 0xF), - /* Horse trample */ DMG_ENTRY(0, 0x0), - /* Explosives */ DMG_ENTRY(2, 0xE), - /* Zora boomerang */ DMG_ENTRY(1, 0xE), - /* Normal arrow */ DMG_ENTRY(1, 0xE), - /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0), - /* Hookshot */ DMG_ENTRY(1, 0xE), - /* Goron punch */ DMG_ENTRY(1, 0xE), - /* Sword */ DMG_ENTRY(0, 0xF), - /* Goron pound */ DMG_ENTRY(0, 0xF), - /* Fire arrow */ DMG_ENTRY(2, 0xE), - /* Ice arrow */ DMG_ENTRY(2, 0xE), - /* Light arrow */ DMG_ENTRY(2, 0x4), - /* Goron spikes */ DMG_ENTRY(1, 0xE), - /* Deku spin */ DMG_ENTRY(1, 0xE), - /* Deku bubble */ DMG_ENTRY(1, 0xE), - /* Deku launch */ DMG_ENTRY(2, 0xE), - /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0xF), - /* Zora barrier */ DMG_ENTRY(0, 0x0), - /* Normal shield */ DMG_ENTRY(0, 0x0), - /* Light ray */ DMG_ENTRY(0, 0xF), - /* Thrown object */ DMG_ENTRY(1, 0xE), - /* Zora punch */ DMG_ENTRY(1, 0xE), - /* Spin attack */ DMG_ENTRY(0, 0xF), - /* Sword beam */ DMG_ENTRY(0, 0x0), - /* Normal Roll */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1B */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1C */ DMG_ENTRY(0, 0x0), - /* Unblockable */ DMG_ENTRY(0, 0x0), - /* UNK_DMG_0x1E */ DMG_ENTRY(0, 0x0), - /* Powder Keg */ DMG_ENTRY(1, 0xF), +typedef enum { + /* 0x0 */ EYEGORE_DMGEFF_IMMUNE_0, + /* 0x4 */ EYEGORE_DMGEFF_LIGHT_ARROW = 4, + /* 0xE */ EYEGORE_DMGEFF_NONE = 0xE, + /* 0xF */ EYEGORE_DMGEFF_IMMUNE_F, +} EnEgolDamageEffect; + +static DamageTable sDamageTable = { + /* Deku Nut */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Deku Stick */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Horse trample */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Explosives */ DMG_ENTRY(2, EYEGORE_DMGEFF_NONE), + /* Zora boomerang */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Normal arrow */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* UNK_DMG_0x06 */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Hookshot */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Goron punch */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Sword */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Goron pound */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Fire arrow */ DMG_ENTRY(2, EYEGORE_DMGEFF_NONE), + /* Ice arrow */ DMG_ENTRY(2, EYEGORE_DMGEFF_NONE), + /* Light arrow */ DMG_ENTRY(2, EYEGORE_DMGEFF_LIGHT_ARROW), + /* Goron spikes */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Deku spin */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Deku bubble */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Deku launch */ DMG_ENTRY(2, EYEGORE_DMGEFF_NONE), + /* UNK_DMG_0x12 */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Zora barrier */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Normal shield */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Light ray */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Thrown object */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Zora punch */ DMG_ENTRY(1, EYEGORE_DMGEFF_NONE), + /* Spin attack */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_F), + /* Sword beam */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Normal Roll */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* UNK_DMG_0x1B */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* UNK_DMG_0x1C */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Unblockable */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* UNK_DMG_0x1E */ DMG_ENTRY(0, EYEGORE_DMGEFF_IMMUNE_0), + /* Powder Keg */ DMG_ENTRY(1, EYEGORE_DMGEFF_IMMUNE_F), +}; + +static Color_RGB8 sEyePrimColors[] = { + { 175, 255, 255 }, // light cyan + { 195, 245, 10 }, // lime green + { 255, 55, 205 }, // magenta +}; + +static Color_RGB8 sEyeEnvColors[] = { + { 255, 115, 155 }, // pink + { 40, 155, 155 }, // dark cyan + { 215, 255, 255 }, // light cyan +}; + +static Color_RGB8 sLightOrbColors[] = { + { 255, 255, 255 }, // white + { 255, 255, 255 }, // white }; ActorInit En_Egol_InitVars = { @@ -119,99 +305,1344 @@ ActorInit En_Egol_InitVars = { (ActorFunc)EnEgol_Draw, }; -#endif +void EnEgol_ChangeAnim(EnEgol* this, s32 animation) { + static AnimationHeader* sAnimations[EYEGORE_ANIM_MAX] = { + &gEyegoreStandAnim, &gEyegoreWalkAnim, &gEyegoreSlamAnim, + &gEyegoreSlamWaitAnim, &gEyegoreSlamEndAnim, &gEyegoreDamagedAnim, + &gEyegoreDeathAnim, &gEyegoreLaserAnim, &gEyegoreUnusedLaserEndAnim, + &gEyegoreStunnedAnim, &gEyegoreStunEndAnim, &gEyegoreRetreatAnim, + &gEyegoreSitAnim, &gEyegoreLeftPunchAnim, &gEyegoreRightPunchAnim, + }; + static u8 sAnimModes[EYEGORE_ANIM_MAX] = { + ANIMMODE_ONCE, ANIMMODE_LOOP, ANIMMODE_ONCE, ANIMMODE_ONCE, ANIMMODE_ONCE, + ANIMMODE_ONCE, ANIMMODE_ONCE, ANIMMODE_ONCE, ANIMMODE_ONCE, ANIMMODE_ONCE, + ANIMMODE_ONCE, ANIMMODE_LOOP, ANIMMODE_ONCE, ANIMMODE_ONCE, ANIMMODE_ONCE, + }; + this->animation = animation; + this->animEndFrame = Animation_GetLastFrame(sAnimations[this->animation]); + Animation_Change(&this->skelAnime, sAnimations[this->animation], 1.0f, 0.0f, this->animEndFrame, + sAnimModes[this->animation], 0.0f); +} -extern ColliderJntSphElementInit D_80A80C64[6]; -extern ColliderJntSphInit D_80A80D3C; -extern ColliderJntSphElementInit D_80A80C40[1]; -extern ColliderJntSphInit D_80A80D4C; -extern ColliderQuadInit D_80A80D5C; -extern DamageTable D_80A80DAC; +void EnEgol_FootstepEffects(EnEgol* this, PlayState* play, f32 leftFootFrame, f32 rightFootFrame) { + if (Animation_OnFrame(&this->skelAnime, leftFootFrame) || Animation_OnFrame(&this->skelAnime, rightFootFrame)) { + Vec3f spawnPos; + Player* player = GET_PLAYER(play); + s32 quakeSize; + s32 pad; -extern UNK_TYPE D_06000040; -extern UNK_TYPE D_060094E4; -extern UNK_TYPE D_0600EE4C; + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_EYEGOLE_WALK); + quakeSize = 4 - (s32)(fabsf(player->actor.world.pos.y - this->actor.world.pos.y) * 0.02f); + if (quakeSize > 4) { + quakeSize = 4; + } else if (quakeSize < 1) { + quakeSize = 1; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7C990.s") + if (player->stateFlags3 != PLAYER_STATE3_1000000) { + func_800BC848(&this->actor, play, quakeSize, 2); + } + if (Animation_OnFrame(&this->skelAnime, leftFootFrame)) { + Math_Vec3f_Copy(&spawnPos, &this->leftFootPos); + spawnPos.y = this->actor.floorHeight; + Actor_SpawnFloorDustRing(play, &this->actor, &spawnPos, 0.0f, 10, 6.0f, 50, 30, true); + } else { + Math_Vec3f_Copy(&spawnPos, &this->rightFootPos); + spawnPos.y = this->actor.floorHeight; + Actor_SpawnFloorDustRing(play, &this->actor, &spawnPos, 0.0f, 10, 6.0f, 50, 30, true); + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7CA18.s") +void EnEgol_DestroyBlocks(EnEgol* this, PlayState* play, Vec3f pos1, Vec3f pos2) { + Actor* prop = play->actorCtx.actorLists[ACTORCAT_PROP].first; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7CBC4.s") + while (prop != NULL) { + if (prop->id != ACTOR_EN_EGBLOCK) { + prop = prop->next; + continue; + } + if (prop->colChkInfo.health > 0) { + Vec3f blockTo1; + Vec3f blockTo2; + f32 dist1; + f32 dist2; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7CD08.s") + blockTo1.x = pos1.x - prop->world.pos.x; + blockTo1.y = pos1.y - prop->world.pos.y; + blockTo1.z = pos1.z - prop->world.pos.z; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/EnEgol_Init.s") + blockTo2.x = pos2.x - prop->world.pos.x; + blockTo2.y = pos2.y - prop->world.pos.y; + blockTo2.z = pos2.z - prop->world.pos.z; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/EnEgol_Destroy.s") + dist1 = sqrtf(SQXZ(blockTo1)); + dist2 = sqrtf(SQXZ(blockTo2)); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D140.s") + if ((dist1 < 40.0f) || (dist2 < 40.0f)) { + dist1 = sqrtf(SQ(blockTo1.y)); + dist2 = sqrtf(SQ(blockTo2.y)); + if ((dist1 < 40.0f) || (dist2 < 40.0f)) { + prop->colChkInfo.health = 0; + Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_WALL_BROKEN); + break; + } + } + } + prop = prop->next; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D168.s") +void EnEgol_GetWaypoint(EnEgol* this) { + if ((this->pathIndex != -1) && (this->path != NULL) && + !SubS_CopyPointFromPath(this->path, this->waypoint, &this->waypointPos)) { + Actor_Kill(&this->actor); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D1E4.s") +#define EYEGORE_SET_SPH_DIM(element, centerX, centerY, centerZ, radiusV, scaleV) \ + element.dim.modelSphere.radius = radiusV; \ + element.dim.scale = scaleV; \ + element.dim.modelSphere.center.x = centerX; \ + element.dim.modelSphere.center.y = centerY; \ + element.dim.modelSphere.center.z = centerZ -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D240.s") +void EnEgol_Init(Actor* thisx, PlayState* play) { + EnEgol* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D27C.s") + this->actor.gravity = -2.0f; + Actor_SetScale(&this->actor, 0.015f); + this->actor.colChkInfo.damageTable = &sDamageTable; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 60.0f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D2C4.s") + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.hintId = TATL_HINT_ID_EYEGORE; + this->actor.colChkInfo.health = 8; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D710.s") + SkelAnime_InitFlex(play, &this->skelAnime, &gEyegoreSkel, &gEyegoreStandAnim, this->jointTable, this->morphTable, + EYEGORE_LIMB_MAX); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7D780.s") + Collider_InitAndSetJntSph(play, &this->bodyCollider, &this->actor, &sBodyJntSphInit, this->bodyElements); + Collider_InitAndSetJntSph(play, &this->eyeCollider, &this->actor, &sEyeJntSphInit, this->eyeElements); + Collider_InitAndSetQuad(play, &this->laserCollider, &this->actor, &sLaserQuadInit); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7DAAC.s") + this->pathIndex = EYEGORE_GET_PATH(&this->actor); + if (this->pathIndex == 0x3F) { + this->pathIndex = -1; + Actor_Kill(&this->actor); + return; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7DAF0.s") + this->wakeupRange = this->actor.world.rot.x * 20.0f; + this->actor.world.rot.x = 0; + if (this->wakeupRange < 0.0f) { + this->wakeupRange = 200.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E2E8.s") + this->minLaserRange = this->actor.world.rot.z * 20.0f; + this->actor.world.rot.z = 0; + if (this->minLaserRange < 0.0f) { + this->minLaserRange = 200.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E344.s") + this->path = SubS_GetPathByIndex(play, this->pathIndex, 0x3F); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E478.s") + EYEGORE_SET_SPH_DIM(this->eyeCollider.elements[0], 500, 0, 0, 26, 1.0f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E4B4.s") + EYEGORE_SET_SPH_DIM(this->bodyCollider.elements[0], 1800, 100, 0, 20, 1.0f); + EYEGORE_SET_SPH_DIM(this->bodyCollider.elements[1], 1800, 100, 0, 20, 1.0f); + EYEGORE_SET_SPH_DIM(this->bodyCollider.elements[2], 1000, -500, 0, 35, 1.0f); + EYEGORE_SET_SPH_DIM(this->bodyCollider.elements[3], 200, 200, 0, 25, 1.0f); + EYEGORE_SET_SPH_DIM(this->bodyCollider.elements[4], 300, 200, 0, 25, 1.0f); + EYEGORE_SET_SPH_DIM(this->bodyCollider.elements[5], 2100, -300, 0, 37, 1.0f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E7EC.s") + this->switchFlag = EYEGORE_GET_SWITCH(&this->actor); + if (this->switchFlag == 0x7F) { + this->switchFlag = -1; + } + if ((this->switchFlag > -1) && Flags_GetSwitch(play, this->switchFlag)) { + Actor_Kill(&this->actor); + return; + } + EnEgol_SetupWait(this); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E820.s") +void EnEgol_Destroy(Actor* thisx, PlayState* play) { + EnEgol* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E924.s") + Collider_DestroyJntSph(play, &this->bodyCollider); + Collider_DestroyJntSph(play, &this->eyeCollider); + Collider_DestroyQuad(play, &this->laserCollider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E970.s") +void EnEgol_SetupWait(EnEgol* this) { + this->actor.flags |= ACTOR_FLAG_8000000; + this->action = EYEGORE_ACTION_WAIT; + this->actionFunc = EnEgol_Wait; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7E9D0.s") +void EnEgol_Wait(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + s16 angleToFacing = this->actor.shape.rot.y - this->actor.yawTowardsPlayer; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EA28.s") + if ((curFrame >= this->animEndFrame) && (ABS_ALT(angleToFacing) < 0x3000) && + (this->actor.xzDistToPlayer < this->wakeupRange)) { + EnEgol_SetupStand(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EA88.s") +void EnEgol_SetupStand(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_STAND); + this->actor.flags &= ~ACTOR_FLAG_8000000; + this->action = EYEGORE_ACTION_STAND; + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_EYEGOLE_STAND); + this->actionFunc = EnEgol_Stand; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EAD8.s") +void EnEgol_Stand(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EB14.s") + if (curFrame >= this->animEndFrame) { + EnEgol_SetupWalk(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EB54.s") +void EnEgol_SetupWalk(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_WALK); + this->laserCount = 0; + EnEgol_GetWaypoint(this); + this->action = EYEGORE_ACTION_WALK; + this->actionFunc = EnEgol_Walk; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EB90.s") +void EnEgol_Walk(EnEgol* this, PlayState* play) { + s16 angleToFacing = this->actor.world.rot.y - this->actor.yawTowardsPlayer; + f32 curFrame = this->skelAnime.curFrame; + Player* player = GET_PLAYER(play); + Vec3f spawnPos; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EBDC.s") + Math_SmoothStepToS(&this->headRot, 0, 1, 0x3E8, 0); + Math_Vec3f_Copy(&spawnPos, &gZeroVec3f); + if (curFrame >= this->animEndFrame) { + this->laserCount++; + if (this->laserCount > 0) { + this->laserCount = 0; + if (this->actor.xzDistToPlayer > this->minLaserRange) { + EnEgol_SetupLaser(this); + return; + } + } + } + if ((this->actor.world.pos.y - 50.0f) <= player->actor.world.pos.y) { + if (Animation_OnFrame(&this->skelAnime, 24.0f)) { + Math_Vec3f_Copy(&spawnPos, &this->rightFootPos); + spawnPos.y = this->actor.floorHeight + 2.0f; + EnEgol_SpawnEffect(this, &spawnPos, &gZeroVec3s, 100, 0.005f, EYEGORE_EFFECT_IMPACT); + } + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + Math_Vec3f_Copy(&spawnPos, &this->leftFootPos); + spawnPos.y = this->actor.floorHeight + 2.0f; + EnEgol_SpawnEffect(this, &spawnPos, &gZeroVec3s, 100, 0.005f, EYEGORE_EFFECT_IMPACT); + } + } + if ((ABS_ALT(angleToFacing) < 0x3000) && (this->actor.xzDistToPlayer < 100.0f)) { + EnEgol_SetupSlam(this); + } else { + s16 angleBehind; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EC84.s") + if (!((this->skelAnime.curFrame == 0.0f) || + ((9.0f <= this->skelAnime.curFrame) && (this->skelAnime.curFrame <= 15.0f)) || + ((24.0f <= this->skelAnime.curFrame) && (this->skelAnime.curFrame <= 29.0f)))) { + Math_ApproachF(&this->actor.world.pos.x, this->waypointPos.x, 0.5f, + fabsf(Math_SinS(this->actor.world.rot.y) * 4.0f)); + Math_ApproachF(&this->actor.world.pos.z, this->waypointPos.z, 0.5f, + fabsf(Math_CosS(this->actor.world.rot.y) * 4.0f)); + } + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos), 1, + 0x7D0, 0); + EnEgol_FootstepEffects(this, play, 9.0f, 24.0f); + angleBehind = this->actor.world.rot.y - this->actor.yawTowardsPlayer + 0x8000; + if (ABS_ALT(angleBehind) < 0x2000) { + this->waypoint--; + if (this->waypoint < 0) { + this->waypoint = 0; + } + EnEgol_SetupStop(this); + } else { + f32 dx = this->actor.world.pos.x - this->waypointPos.x; + f32 dz = this->actor.world.pos.z - this->waypointPos.z; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7ED14.s") + if (sqrtf(SQ(dx) + SQ(dz)) < 4.0f) { + if (this->path != NULL) { + this->waypoint++; + if (this->waypoint >= this->path->count) { + this->waypoint -= 2; + if (this->waypoint < 0) { + this->waypoint = 0; + } + EnEgol_SetupStop(this); + } + EnEgol_GetWaypoint(this); + } + } + if (this->actor.wallBgId != BGCHECK_SCENE) { + EnEgol_DestroyBlocks(this, play, this->rightFootPos, this->leftFootPos); + } + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7EFB8.s") +void EnEgol_SetupRetreat(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_RETREAT); + this->laserCount = 0; + EnEgol_GetWaypoint(this); + this->actor.world.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos); + this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + this->action = EYEGORE_ACTION_RETREAT; + this->actionFunc = EnEgol_Retreat; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/EnEgol_Update.s") +void EnEgol_Retreat(EnEgol* this, PlayState* play) { + s16 angleToFacing = this->actor.world.rot.y - this->actor.yawTowardsPlayer; + Vec3f toWaypoint; + Vec3f spawnPos; + Player* player = GET_PLAYER(play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7F8E8.s") + if ((ABS_ALT(angleToFacing) < 0x3000) && (fabsf(this->actor.world.pos.y - player->actor.world.pos.y) < 50.0f) && + (this->actor.xzDistToPlayer < 100.0f) && (player->invincibilityTimer == 0)) { + func_800B8D50(play, &this->actor, 2.0f, (s32)randPlusMinusPoint5Scaled(0x2000) + this->actor.world.rot.y, 5.0f, + 0x10); + } + Math_Vec3f_Copy(&spawnPos, &gZeroVec3f); + if ((this->actor.world.pos.y - 50.0f) <= player->actor.world.pos.y) { + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Math_Vec3f_Copy(&spawnPos, &this->rightFootPos); + spawnPos.y = this->actor.floorHeight + 2.0f; + EnEgol_SpawnEffect(this, &spawnPos, &gZeroVec3s, 100, 0.005f, EYEGORE_EFFECT_IMPACT); + } + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Math_Vec3f_Copy(&spawnPos, &this->leftFootPos); + spawnPos.y = this->actor.floorHeight + 2.0f; + EnEgol_SpawnEffect(this, &spawnPos, &gZeroVec3s, 100, 0.005f, EYEGORE_EFFECT_IMPACT); + } + } + Math_ApproachF(&this->actor.world.pos.x, this->waypointPos.x, 0.5f, + fabsf(Math_SinS(this->actor.world.rot.y) * 10.0f)); + Math_ApproachF(&this->actor.world.pos.z, this->waypointPos.z, 0.5f, + fabsf(Math_CosS(this->actor.world.rot.y) * 10.0f)); + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos), 1, 0x7D0, + 0); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y + 0x8000, 1, 0x7D0, 0); + EnEgol_FootstepEffects(this, play, 5.0f, 0.0f); + toWaypoint.x = this->actor.world.pos.x - this->waypointPos.x; + toWaypoint.z = this->actor.world.pos.z - this->waypointPos.z; + if (sqrtf(SQXZ(toWaypoint)) < 4.0f) { + this->waypoint--; + if (this->waypoint < 0) { + this->waypoint = 0; + EnEgol_SetupStop(this); + } + EnEgol_GetWaypoint(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A7FAFC.s") +void EnEgol_SetupLaser(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_LASER); + this->actionTimer = 0; + this->laserCount = 0; + this->action = EYEGORE_ACTION_LASER; + this->actionFunc = EnEgol_Laser; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/EnEgol_Draw.s") +void EnEgol_Laser(EnEgol* this, PlayState* play) { + static s16 sLaserAngles[3] = { 0x1F40, 0xBB8, 0x7D0 }; + s32 pad1; + Vec3f stonePos; + s32 pad2; + CollisionPoly* colPoly; + Vec3f hitPos; + s32 bgId; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A80508.s") + if ((ABS_ALT((s16)(this->actor.world.rot.y - this->actor.yawTowardsPlayer)) < 0x3000) && + (this->actor.xzDistToPlayer < 100.0f)) { + this->chargingLaser = false; + this->chargeLevel = 0; + this->actionTimer = 0; + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + EnEgol_SetupSlam(this); + } else if (this->actor.xzDistToPlayer < this->minLaserRange) { + this->chargingLaser = false; + this->chargeLevel = 0; + this->actionTimer = 0; + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + EnEgol_SetupWalk(this); + } else { + if (this->chargingLaser) { + switch (this->chargeLevel) { + case 0: + this->waitTimer = 10; + this->chargeLevel++; + break; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A80750.s") + case 1: + Math_ApproachF(&this->chargeLightScale, 8.0f, 0.5f, 2.5f); + if (this->waitTimer == 0) { + this->waitTimer = 10; + this->chargeLevel++; + } + break; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Egol/func_80A80904.s") + case 2: + Math_ApproachF(&this->chargeLightScale, 1.0f, 0.5f, 1.0f); + if (this->waitTimer == 0) { + this->chargeLevel = 0; + this->laserState = EYEGORE_LASER_FIRE; + this->chargingLaser = false; + this->chargeLightScale = 0.0f; + } + break; + + default: + break; + } + this->chargeLightRot += 0x7D0; + } + if ((this->laserState != EYEGORE_LASER_OFF) || (this->laserCount != 0)) { + Math_SmoothStepToS(&this->headRot, -0x2710, 5, 0x1F4, 5); + } else { + Math_SmoothStepToS(&this->headRot, 0, 5, 0x1F4, 5); + } + if (this->laserState == EYEGORE_LASER_OFF) { + if (this->laserCount >= 3) { + EnEgol_SetupWalk(this); + } else { + this->laserRot.x = sLaserAngles[this->laserCount]; + this->laserScaleTarget.z = 0.0f; + this->laserScale.z = 0.0f; + if (this->laserCount == 0) { + this->laserState = EYEGORE_LASER_START; + } else { + this->laserState = EYEGORE_LASER_FIRE; + this->laserScaleTarget.x = 0.03f; + this->laserScaleTarget.y = 0.03f; + } + this->laserCount++; + } + } else if (this->laserState >= EYEGORE_LASER_FIRE) { + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_EYEGOLE_BEAM - SFX_FLAG); + if (this->laserState != EYEGORE_LASER_OFF) { + EnEgol_DestroyBlocks(this, play, this->laserCollider.dim.quad[0], this->laserCollider.dim.quad[1]); + } + //! @bug this should check walls, too + if ((this->actionTimer == 0) && BgCheck_EntityLineTest1(&play->colCtx, &this->laserCollider.dim.quad[3], + &this->laserCollider.dim.quad[1], &hitPos, &colPoly, + false, true, false, true, &bgId)) { + Vec3s rotToNorm; + f32 nx; + f32 ny; + f32 nz; + s32 pad3; + s32 i; + s32 quakeSize; + Player* player = GET_PLAYER(play); + + nx = COLPOLY_GET_NORMAL(colPoly->normal.x); + ny = COLPOLY_GET_NORMAL(colPoly->normal.y); + nz = COLPOLY_GET_NORMAL(colPoly->normal.z); + + /*! @bug The following is supposed to calculate the rotation from vertical to the collision poly normal. + * However, the calculation is performed incorrectly. The correct calculation is + * rotToNorm.x = func_80086B30(nz, ny) * 0x8000 / M_PI; + * rotToNorm.z = func_80086B30(-nx, sqrtf(1.0f - SQ(nx))) * 0x8000 / M_PI; + */ + rotToNorm.x = -func_80086B30(-nz * ny, 1.0f) * 0x8000 / M_PI; + rotToNorm.z = func_80086B30(-nx * ny, 1.0f) * 0x8000 / M_PI; + + if ((this->actor.world.pos.y - 50.0f) <= player->actor.world.pos.y) { + EnEgol_SpawnEffect(this, &hitPos, &rotToNorm, 100, 0.02f, EYEGORE_EFFECT_IMPACT); + } + quakeSize = 4 - (s32)(fabsf(player->actor.world.pos.y - this->actor.world.pos.y) * 0.02f); + if (quakeSize > 4) { + quakeSize = 4; + } else if (quakeSize < 1) { + quakeSize = 1; + } + if (player->stateFlags3 != PLAYER_STATE3_1000000) { + func_800BC848(&this->actor, play, quakeSize, 2); + } + Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_EXPLOSION); + func_800B31BC(play, &hitPos, 40, -2, 255, 20); + func_800BBFB0(play, &hitPos, 6.0f, 2, 120, 20, true); + + if ((this->actor.world.pos.y - 50.0f <= player->actor.world.pos.y) && + (this->actor.floorBgId == BGCHECK_SCENE)) { + Math_Vec3f_Copy(&stonePos, &hitPos); + stonePos.x += Math_SinS(this->actor.world.rot.y) * 60.0f; + stonePos.z += Math_CosS(this->actor.world.rot.y) * 60.0f; + for (i = 0; i < 3; i++) { + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ESTONE, stonePos.x, stonePos.y, stonePos.z, 0, + this->actor.world.rot.y, 0, ENESTONE_TYPE_SMALL); + } + } + if (this->actor.world.pos.y - 50.0f <= player->actor.world.pos.y) { + for (i = 0; i < 10; i++) { + EnEgol_SpawnEffect(this, &hitPos, &gZeroVec3s, 30, (Rand_ZeroFloat(1.0f) * 0.1f) + 0.2f, + EYEGORE_EFFECT_DEBRIS); + } + } + this->actionTimer = 1; + } + if (this->actionTimer != 0) { + this->actionTimer++; + if (this->actionTimer >= 5) { + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + this->actionTimer = 0; + } + } + Math_ApproachF(&this->laserScale.x, this->laserScaleTarget.x, 0.5f, 0.5f); + Math_ApproachF(&this->laserScale.y, this->laserScaleTarget.y, 0.5f, 0.5f); + Math_ApproachF(&this->laserScale.z, this->laserScaleTarget.z, 0.5f, 0.5f); + } + } +} + +void EnEgol_SetupStop(EnEgol* this) { + if (!this->isRetreating) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_LASER); + } else { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_STUN_END); + } + this->action = EYEGORE_ACTION_STOP; + this->actionFunc = EnEgol_Stop; +} + +void EnEgol_Stop(EnEgol* this, PlayState* play) { + s32 isHome = false; + f32 curFrame = this->skelAnime.curFrame; + s16 angleToHome; + + EnEgol_FootstepEffects(this, play, 28.0f, 13.0f); + if (this->isRetreating) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 5, 0xBB8, 5); + this->actor.world.rot.y = this->actor.shape.rot.y; + angleToHome = this->actor.world.rot.y - this->actor.home.rot.y; + } + if (!this->isRetreating) { + isHome = true; + } else if (ABS_ALT(angleToHome) < 0x64) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.home.rot.y; + isHome = true; + } + if (isHome && (curFrame >= this->animEndFrame)) { + this->isRetreating ^= true; + if (!this->isRetreating) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_SIT); + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_EYEGOLE_SIT); + this->laserCount = 0; + this->actionFunc = EnEgol_Wait; + } else { + EnEgol_SetupRetreat(this); + } + } +} + +void EnEgol_SetupSlam(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_SLAM); + this->action = EYEGORE_ACTION_SLAM; + this->actionFunc = EnEgol_Slam; +} + +void EnEgol_Slam(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + Math_SmoothStepToS(&this->headRot, 0, 1, 0x3E8, 0); + if (Animation_OnFrame(&this->skelAnime, 17.0f)) { + s32 i; + s32 spawnCount; + Player* player = GET_PLAYER(play); + Vec3f spawnPos; + f32 dyToPlayer = fabsf(player->actor.world.pos.y - this->actor.world.pos.y); + s32 quakeSize; + + Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_EXPLOSION); + quakeSize = 4 - (s32)(dyToPlayer * 0.02f); + if (quakeSize > 4) { + quakeSize = 4; + } else if (quakeSize < 1) { + quakeSize = 1; + } + func_800BC848(&this->actor, play, quakeSize, 2); + if (this->actor.floorBgId == BGCHECK_SCENE) { + Math_Vec3f_Copy(&spawnPos, &this->actor.world.pos); + spawnPos.x += Math_SinS(this->actor.world.rot.y) * 60.0f; + spawnPos.y = this->actor.floorHeight; + spawnPos.z += Math_CosS(this->actor.world.rot.y) * 60.0f; + spawnCount = Rand_S16Offset(1, 3); + for (i = 0; i < spawnCount; i++) { + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ESTONE, spawnPos.x, spawnPos.y, spawnPos.z, 0, + this->actor.world.rot.y, 0, ENESTONE_TYPE_LARGE); + } + Actor_SpawnFloorDustRing(play, &this->actor, &spawnPos, 30.0f, 30, 10.0f, 100, 30, true); + Math_Vec3f_Copy(&spawnPos, &this->actor.world.pos); + spawnPos.x += Math_SinS(this->actor.world.rot.y) * 55.0f; + spawnPos.y = this->actor.floorHeight + 2.0f; + spawnPos.z += Math_CosS(this->actor.world.rot.y) * 55.0f; + EnEgol_SpawnEffect(this, &spawnPos, &gZeroVec3s, 100, 0.03f, EYEGORE_EFFECT_IMPACT); + } + EnEgol_DestroyBlocks(this, play, this->rightHandPos, this->leftHandPos); + } + if (this->animEndFrame <= curFrame) { + EnEgol_SetupSlamWait(this); + } else if ((this->skelAnime.curFrame <= 17.0f) && (this->skelAnime.curFrame >= 10.0f)) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->bodyCollider.base); + } +} + +void EnEgol_SetupPunch(EnEgol* this) { + this->bodyCollider.elements[0].dim.modelSphere.radius = 40; + this->bodyCollider.elements[1].dim.modelSphere.radius = 40; + this->hitPlayer = false; + this->action = EYEGORE_ACTION_PUNCH; + this->actionFunc = EnEgol_Punch; +} + +void EnEgol_Punch(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + Math_SmoothStepToS(&this->headRot, 0, 1, 0x3E8, 0); + if (curFrame >= this->animEndFrame) { + this->bodyCollider.elements[0].dim.modelSphere.radius = 20; + this->bodyCollider.elements[1].dim.modelSphere.radius = 20; + EnEgol_SetupWalk(this); + } else if (!this->hitPlayer) { + if ((this->bodyCollider.elements[0].info.toucherFlags & TOUCH_HIT) || + (this->bodyCollider.elements[1].info.toucherFlags & TOUCH_HIT)) { + this->hitPlayer = true; + func_800B8D50(play, &this->actor, 10.0f, this->actor.home.rot.y, 10.0f, 0); + } + if (!(this->bodyCollider.base.atFlags & AT_BOUNCED)) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->bodyCollider.base); + } + } +} + +void EnEgol_SetupSlamWait(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_SLAM_WAIT); + this->actionTimer = 0; + this->eyeCollider.elements[0].info.elemType = ELEMTYPE_UNK1; + this->action = EYEGORE_ACTION_SLAM_WAIT; + this->actionFunc = EnEgol_SlamWait; +} + +void EnEgol_SlamWait(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + if (curFrame >= this->animEndFrame) { + this->actionTimer++; + if (this->actionTimer > 20) { + this->eyeCollider.elements[0].info.elemType = ELEMTYPE_UNK2; + EnEgol_SetupSlamEnd(this); + } + } +} + +void EnEgol_SetupStunned(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_STUNNED); + this->actionTimer = 0; + this->eyeCollider.elements[0].info.elemType = ELEMTYPE_UNK1; + this->bodyCollider.elements[0].dim.modelSphere.radius = 0; + this->bodyCollider.elements[1].dim.modelSphere.radius = 0; + this->action = EYEGORE_ACTION_STUNNED; + this->actionFunc = EnEgol_Stunned; +} + +void EnEgol_Stunned(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + if (curFrame >= this->animEndFrame) { + this->actionTimer++; + if (this->actionTimer > 80) { + this->eyeCollider.elements->info.elemType = ELEMTYPE_UNK2; + EnEgol_SetupStunEnd(this); + } + } +} + +void EnEgol_SetupStunEnd(EnEgol* this) { + this->bodyCollider.elements[0].dim.modelSphere.radius = 20; + this->bodyCollider.elements[1].dim.modelSphere.radius = 20; + EnEgol_ChangeAnim(this, EYEGORE_ANIM_STUN_END); + this->action = EYEGORE_ACTION_STUN_END; + this->actionFunc = EnEgol_StunEnd; +} + +void EnEgol_StunEnd(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + if (curFrame >= this->animEndFrame) { + EnEgol_SetupWalk(this); + } +} + +void EnEgol_SetupSlamEnd(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_SLAM_END); + this->actionTimer = 0; + this->action = EYEGORE_ACTION_SLAM_END; + this->actionFunc = EnEgol_SlamEnd; +} + +void EnEgol_SlamEnd(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + if (curFrame >= this->animEndFrame) { + EnEgol_SetupWalk(this); + } +} + +void EnEgol_SetupDamaged(EnEgol* this) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_DAMAGED); + this->bodyCollider.elements[0].dim.modelSphere.radius = 20; + this->bodyCollider.elements[1].dim.modelSphere.radius = 20; + this->action = EYEGORE_ACTION_DAMAGED; + this->actionFunc = EnEgol_Damaged; +} + +void EnEgol_Damaged(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + + if (curFrame >= this->animEndFrame) { + if (this->actor.colChkInfo.health > 0) { + EnEgol_SetupWalk(this); + } else { + Enemy_StartFinishingBlow(play, &this->actor); + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_EYEGOLE_DEAD); + this->actor.flags |= ACTOR_FLAG_8000000; + this->actor.flags &= ~ACTOR_FLAG_1; + this->actor.flags |= ACTOR_FLAG_100000; + this->actionFunc = EnEgol_StartDeathCutscene; + } + } +} + +void EnEgol_StartDeathCutscene(EnEgol* this, PlayState* play) { + if (!ActorCutscene_GetCanPlayNext(this->actor.cutscene)) { + ActorCutscene_SetIntentToPlay(this->actor.cutscene); + } else { + ActorCutscene_StartAndSetUnkLinkFields(this->actor.cutscene, &this->actor); + this->subCamId = ActorCutscene_GetCurrentSubCamId(this->actor.cutscene); + this->subCamFovTarget = 60.0f; + EnEgol_ChangeAnim(this, EYEGORE_ANIM_DEATH); + this->action = EYEGORE_ACTION_DYING; + this->actionFunc = EnEgol_Death; + this->actor.shape.shadowScale = 0.0f; + } +} + +void EnEgol_Death(EnEgol* this, PlayState* play) { + f32 curFrame = this->skelAnime.curFrame; + Vec3f atOffset; + + this->subCamAt.x = 0.0f; + this->subCamAt.y = 60.0f; + this->subCamAt.z = 260.0f; + Math_Vec3f_Copy(&atOffset, &this->subCamAt); + OLib_DbCameraVec3fSum(&this->actor.world, &atOffset, &this->subCamAt, 1); + this->subCamEye.x = this->actor.world.pos.x; + this->subCamEye.y = this->actor.world.pos.y + 70.0f; + this->subCamEye.z = this->actor.world.pos.z; + Math_ApproachF(&this->subCamFov, this->subCamFovTarget, 0.3f, 10.0f); + Play_SetCameraAtEye(play, this->subCamId, &this->subCamEye, &this->subCamAt); + Play_SetCameraFov(play, this->subCamId, this->subCamFov); + if ((this->action == EYEGORE_ACTION_DEAD) && (this->waitTimer == 1)) { + if (this->switchFlag > -1) { + Flags_SetSwitch(play, this->switchFlag); + } + ActorCutscene_Stop(this->actor.cutscene); + Actor_Kill(&this->actor); + } else { + if (Animation_OnFrame(&this->skelAnime, 46.0f)) { + Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_EXPLOSION); + func_800BC848(&this->actor, play, 10, 5); + } + if ((curFrame >= this->animEndFrame) && (this->action != EYEGORE_ACTION_DEAD)) { + s32 i; + + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, CLEAR_TAG_SMALL_EXPLOSION); + this->waitTimer = 30; + Actor_PlaySfxAtPos(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + this->action = EYEGORE_ACTION_DEAD; + for (i = 0; i < 20; i++) { + EnEgol_SpawnEffect(this, &this->actor.world.pos, &gZeroVec3s, 10.0f + Rand_ZeroFloat(20.0f), + 0.01f + (0.02f * Rand_ZeroFloat(1.0f)), (i & 1) + EYEGORE_EFFECT_PIECE_LARGE); + } + } + } +} + +typedef enum { + /* 0 */ EYEGORE_HIT_NONE, + /* 1 */ EYEGORE_HIT_DAMAGE, + /* 2 */ EYEGORE_HIT_IMMUNE, // Makes hitmarks, but no reaction +} EyegoreHitReaction; + +void EnEgol_CollisionCheck(EnEgol* this, PlayState* play) { + s16 angleToFacing = this->actor.world.rot.y - this->actor.yawTowardsPlayer; + s32 reaction = EYEGORE_HIT_NONE; + + if ((this->action == EYEGORE_ACTION_LASER) && (this->laserCollider.base.atFlags & AT_BOUNCED)) { + this->laserCollider.base.atFlags &= ~AT_HIT; + this->laserCollider.base.atFlags &= ~AT_BOUNCED; + this->actionTimer = 0; + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + EnEgol_SetupWalk(this); + } + if (this->eyeCollider.elements[0].info.bumperFlags & BUMP_HIT) { + reaction = EYEGORE_HIT_IMMUNE; + switch (this->actor.colChkInfo.damageEffect) { + case EYEGORE_DMGEFF_LIGHT_ARROW: + if (ABS_ALT(angleToFacing) < 0x3000) { + if ((this->action == EYEGORE_ACTION_STUNNED) || (this->action == EYEGORE_ACTION_SLAM_WAIT)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.focus.pos.x, + this->actor.focus.pos.y, this->actor.focus.pos.z, 0, 0, 0, + CLEAR_TAG_LARGE_LIGHT_RAYS); + this->dmgEffectTimer = 20; + reaction = EYEGORE_HIT_DAMAGE; + } else if ((this->action >= EYEGORE_ACTION_WALK) && (this->action <= EYEGORE_ACTION_STUN_END) && + !this->isRetreating) { + reaction = EYEGORE_HIT_NONE; + this->chargingLaser = false; + this->chargeLevel = 0; + this->actionTimer = 0; + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + Actor_SetColorFilter(&this->actor, 0, 120, false, 40); + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_COMMON_FREEZE); + EnEgol_SetupStunned(this); + } + } + break; + + case EYEGORE_DMGEFF_NONE: + if ((this->action == EYEGORE_ACTION_SLAM_WAIT) || (this->action == EYEGORE_ACTION_STUNNED)) { + reaction = EYEGORE_HIT_DAMAGE; + } + break; + + default: + break; + } + } + if (reaction == EYEGORE_HIT_DAMAGE) { + Actor_ApplyDamage(&this->actor); + CollisionCheck_BlueBlood(play, NULL, &this->eyePos); + CollisionCheck_BlueBlood(play, NULL, &this->eyePos); + CollisionCheck_BlueBlood(play, NULL, &this->eyePos); + Actor_SetColorFilter(&this->actor, 0x4000, 255, false, 25); + Actor_PlaySfxAtPos(&this->actor, NA_SE_EN_EYEGOLE_DAMAGE); + EnEgol_SetupDamaged(this); + } else if (reaction == EYEGORE_HIT_IMMUNE) { + Vec3f hitPos; + + hitPos.x = this->eyeCollider.elements[0].info.bumper.hitPos.x; + hitPos.y = this->eyeCollider.elements[0].info.bumper.hitPos.y; + hitPos.z = this->eyeCollider.elements[0].info.bumper.hitPos.z; + Actor_PlaySfxAtPos(&this->actor, NA_SE_IT_SHIELD_BOUND); + EffectSsHitmark_SpawnFixedScale(play, EFFECT_HITMARK_METAL, &hitPos); + CollisionCheck_SpawnShieldParticlesMetal(play, &hitPos); + } +} + +void EnEgol_Update(Actor* thisx, PlayState* play) { + EnEgol* this = THIS; + Player* player = GET_PLAYER(play); + s32 pad; + + if (!((this->action == EYEGORE_ACTION_PUNCH) || (this->action == EYEGORE_ACTION_RETREAT) || + (this->action >= EYEGORE_ACTION_DYING))) { + s16 angleToFacing = this->actor.shape.rot.y - this->actor.yawTowardsPlayer; + s16 angleBehind = this->actor.world.rot.y - this->actor.yawTowardsPlayer + 0x8000; + + if ((ABS_ALT(angleToFacing) > 0x1888) && (ABS_ALT(angleBehind) > 0x2000) && + (this->actor.xzDistToPlayer < 100.0f)) { + if (angleToFacing < 0) { + if (this->animation != EYEGORE_ANIM_LEFT_PUNCH) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_LEFT_PUNCH); + } + } else if (this->animation != EYEGORE_ANIM_RIGHT_PUNCH) { + EnEgol_ChangeAnim(this, EYEGORE_ANIM_RIGHT_PUNCH); + } + this->chargingLaser = false; + this->chargeLevel = 0; + this->actionTimer = 0; + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + EnEgol_SetupPunch(this); + } + } + if (this->action != EYEGORE_ACTION_WAIT) { + SkelAnime_Update(&this->skelAnime); + } + if (this->laserState == EYEGORE_LASER_OFF) { + Math_ApproachZeroF(&this->laserLightScale, 0.5f, 0.5f); + } else if (this->laserState >= EYEGORE_LASER_FIRE) { + Math_ApproachF(&this->laserLightScale, 1.0f, 0.5f, 0.5f); + } + Actor_SetScale(&this->actor, 0.015f); + + if (!((this->action == EYEGORE_ACTION_STOP) || (this->action == EYEGORE_ACTION_RETREAT))) { + this->actor.shape.rot.y = this->actor.world.rot.y; + } + this->texScroll += 12; + DECR(this->waitTimer); + DECR(this->eyeShutTimer); + DECR(this->dmgEffectTimer); + + EnEgol_UpdateEffects(this, play); + this->actionFunc(this, play); + Math_Vec3f_Copy(&this->actor.focus.pos, &this->eyePos); + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + if (!((this->action == EYEGORE_ACTION_SLAM_WAIT) || (this->action == EYEGORE_ACTION_WAIT) || + (this->action == EYEGORE_ACTION_RETREAT) || (this->action == EYEGORE_ACTION_STAND) || + (this->action == EYEGORE_ACTION_LASER) || (this->action == EYEGORE_ACTION_STUNNED) || + (this->action == EYEGORE_ACTION_DAMAGED) || (this->action == EYEGORE_ACTION_DYING))) { + + this->pupilRot.x = Math_Vec3f_Pitch(&this->eyePos, &player->actor.world.pos); + this->pupilRot.y = -Math_Vec3f_Yaw(&this->eyePos, &player->actor.world.pos); + + this->pupilRot.y += this->actor.world.rot.y; + + if (this->pupilRot.x > 0x7D0) { + this->pupilRot.x = 0x7D0; + } else if (this->pupilRot.x < -0x7D0) { + this->pupilRot.x = -0x7D0; + } + if (this->pupilRot.y > 0x1770) { + this->pupilRot.y = 0x1770; + } else if (this->pupilRot.y < -0x1770) { + this->pupilRot.y = -0x1770; + } + } else { + this->pupilRot.x = this->pupilRot.y = 0; + } + if (this->eyeShutTimer == 0) { + if (((this->action == EYEGORE_ACTION_WALK) || (this->action == EYEGORE_ACTION_LASER)) && !this->isRetreating) { + Actor* projectile = func_800BC270(play, &this->actor, 80.0f, 0x138B0); + // last argument is DMG_ZORA_BOOMERANG | DMG_HOOKSHOT | DMG_NORMAL_ARROW | DMG_FIRE_ARROW | DMG_ICE_ARROW | + // DMG_LIGHT_ARROW | DMG_DEKU_BUBBLE + + if ((projectile != NULL) && !((projectile->id == ACTOR_EN_ARROW) && (projectile->params == ENARROW_5))) { + this->eyelidRotTarget = 0xFA0; + this->eyeShutTimer = 20; + if (this->action == EYEGORE_ACTION_LASER) { + this->chargingLaser = false; + this->chargeLevel = 0; + this->actionTimer = 0; + this->laserState = EYEGORE_LASER_OFF; + Math_Vec3f_Copy(&this->laserScale, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserScaleTarget, &gZeroVec3f); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[1], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[3], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[0], &this->laserBase); + Math_Vec3f_Copy(&this->laserCollider.dim.quad[2], &this->laserBase); + EnEgol_SetupWalk(this); + } + } + } else { + this->eyelidRotTarget = 0; + } + } else if (this->eyeShutTimer == 1) { + this->eyelidRotTarget = 0; + } + Math_SmoothStepToS(&this->eyelidRot, this->eyelidRotTarget, 1, 0x7D0, 0); + EnEgol_CollisionCheck(this, play); + Actor_MoveWithGravity(&this->actor); + Actor_UpdateBgCheckInfo(play, &this->actor, 20.0f, 50.0f, 50.0f, 0x1D); + if (this->action != EYEGORE_ACTION_DEAD) { + //! @bug This should be ||, not &&. As is, the check always succeeds. + if (!((this->action == EYEGORE_ACTION_DAMAGED) && (this->action == EYEGORE_ACTION_DYING))) { + CollisionCheck_SetAC(play, &play->colChkCtx, &this->eyeCollider.base); + CollisionCheck_SetAC(play, &play->colChkCtx, &this->bodyCollider.base); + } + CollisionCheck_SetOC(play, &play->colChkCtx, &this->bodyCollider.base); + } + if (this->laserState >= EYEGORE_LASER_ON) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->laserCollider.base); + } +} + +s32 EnEgol_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + EnEgol* this = THIS; + + if (limbIndex == EYEGORE_LIMB_HEAD) { + rot->z += this->headRot; + } + if (limbIndex == EYEGORE_LIMB_UPPER_EYELID) { + rot->z += this->eyelidRot; + } + if (limbIndex == EYEGORE_LIMB_LOWER_EYELID) { + rot->z += -1 * this->eyelidRot; + } + if (limbIndex == EYEGORE_LIMB_PUPIL) { + rot->y += this->pupilRot.y; + rot->z += this->pupilRot.x; + } + if (limbIndex == EYEGORE_LIMB_EYEBALL) { + s32 eyeColorIndex = 0; + + OPEN_DISPS(play->state.gfxCtx); + + if ((this->action == EYEGORE_ACTION_DAMAGED) || (this->action == EYEGORE_ACTION_DYING)) { + eyeColorIndex = 2; + } else if ((this->action == EYEGORE_ACTION_STUNNED) || (this->action == EYEGORE_ACTION_SLAM_WAIT)) { + eyeColorIndex = 1; + } + this->eyePrimColor.r = sEyePrimColors[eyeColorIndex].r; + this->eyePrimColor.g = sEyePrimColors[eyeColorIndex].g; + this->eyePrimColor.b = sEyePrimColors[eyeColorIndex].b; + this->eyeEnvColor.r = sEyeEnvColors[eyeColorIndex].r; + this->eyeEnvColor.g = sEyeEnvColors[eyeColorIndex].g; + this->eyeEnvColor.b = sEyeEnvColors[eyeColorIndex].b; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0xFF, (u8)this->eyePrimColor.r, (u8)this->eyePrimColor.g, + (u8)this->eyePrimColor.b, 255); + gDPSetEnvColor(POLY_OPA_DISP++, (u8)this->eyeEnvColor.r, (u8)this->eyeEnvColor.g, (u8)this->eyeEnvColor.b, 255); + gSPSegment(POLY_OPA_DISP++, 0x09, func_8012CB28(play->state.gfxCtx, 0, this->texScroll)); + + CLOSE_DISPS(play->state.gfxCtx); + } + return false; +} + +void EnEgol_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + EnEgol* this = THIS; + Vec3f footOffset = { 1000.0f, 0.0f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == EYEGORE_LIMB_LEFT_HAND) { + Matrix_MultVec3f(&gZeroVec3f, &this->leftHandPos); + } + if (limbIndex == EYEGORE_LIMB_RIGHT_HAND) { + Matrix_MultVec3f(&gZeroVec3f, &this->rightHandPos); + } + if (limbIndex == EYEGORE_LIMB_LASER_ATTACH) { + Matrix_MultVec3f(&gZeroVec3f, &this->eyePos); + } + if ((limbIndex == EYEGORE_LIMB_LASER_ATTACH) && (this->laserState >= EYEGORE_LASER_FIRE)) { + Vec3f laserLengthZ = { 0.0f, 0.0f, 0.0f }; + Vec3f laserVec; + f32 targetScale; + f32 targetHeight; + + Matrix_MultVec3f(&zeroVec, &this->laserBase); + this->laserCollider.dim.quad[3].x = + this->laserBase.x + Math_SinS(this->actor.world.rot.y + 0x4000) * this->laserScale.x * 77.0f; + this->laserCollider.dim.quad[3].y = this->laserBase.y; + this->laserCollider.dim.quad[3].z = + this->laserBase.z + Math_CosS(this->actor.world.rot.y + 0x4000) * this->laserScale.x * 77.0f; + this->laserCollider.dim.quad[2].x = + this->laserBase.x + Math_SinS(this->actor.world.rot.y + 0xC000) * this->laserScale.x * 77.0f; + this->laserCollider.dim.quad[2].y = this->laserBase.y; + this->laserCollider.dim.quad[2].z = + this->laserBase.z + Math_CosS(this->actor.world.rot.y + 0xC000) * this->laserScale.x * 77.0f; + + targetScale = this->laserScale.z; + targetHeight = this->laserCollider.dim.quad[3].y; + + while ((targetHeight + 20.0f > this->actor.floorHeight) && (targetScale < 10.0f)) { + Matrix_Push(); + Matrix_RotateYS(this->actor.world.rot.y, MTXMODE_NEW); + Matrix_RotateXS(this->laserRot.x, MTXMODE_APPLY); + laserLengthZ.z = targetScale * 700.0f; + Matrix_MultVec3f(&laserLengthZ, &laserVec); + Matrix_Pop(); + targetHeight = this->laserCollider.dim.quad[3].y + laserVec.y; + targetScale += 0.01f; + } + this->laserScaleTarget.z = targetScale; + Matrix_Push(); + Matrix_RotateYS(this->actor.world.rot.y, MTXMODE_NEW); + Matrix_RotateXS(this->laserRot.x, MTXMODE_APPLY); + laserLengthZ.z = this->laserScale.z * 700.0f; + Matrix_MultVec3f(&laserLengthZ, &laserVec); + Matrix_Pop(); + this->laserCollider.dim.quad[1].x = this->laserCollider.dim.quad[3].x + laserVec.x; + this->laserCollider.dim.quad[1].y = this->laserCollider.dim.quad[3].y + laserVec.y; + this->laserCollider.dim.quad[1].z = this->laserCollider.dim.quad[3].z + laserVec.z; + this->laserCollider.dim.quad[0].x = this->laserCollider.dim.quad[2].x + laserVec.x; + this->laserCollider.dim.quad[0].y = this->laserCollider.dim.quad[2].y + laserVec.y; + this->laserCollider.dim.quad[0].z = this->laserCollider.dim.quad[2].z + laserVec.z; + Collider_SetQuadVertices(&this->laserCollider, &this->laserCollider.dim.quad[0], + &this->laserCollider.dim.quad[1], &this->laserCollider.dim.quad[2], + &this->laserCollider.dim.quad[3]); + this->laserState = EYEGORE_LASER_ON; + } + if ((limbIndex == EYEGORE_LIMB_LASER_ATTACH) && (this->laserState == EYEGORE_LASER_START)) { + this->chargingLaser = true; + this->laserState = EYEGORE_LASER_CHARGING; + this->laserScaleTarget.x = 0.03f; + this->laserScaleTarget.y = 0.03f; + } + if (limbIndex == EYEGORE_LIMB_LEFT_FOOT) { + Matrix_MultVec3f(&footOffset, &this->leftFootPos); + } + if (limbIndex == EYEGORE_LIMB_RIGHT_FOOT) { + Matrix_MultVec3f(&footOffset, &this->rightFootPos); + } + if ((limbIndex == EYEGORE_LIMB_HEAD) || (limbIndex == EYEGORE_LIMB_LEFT_SHOULDER) || + (limbIndex == EYEGORE_LIMB_LEFT_ARM) || (limbIndex == EYEGORE_LIMB_LEFT_HAND) || + (limbIndex == EYEGORE_LIMB_RIGHT_SHOULDER) || (limbIndex == EYEGORE_LIMB_RIGHT_ARM) || + (limbIndex == EYEGORE_LIMB_RIGHT_HAND) || (limbIndex == EYEGORE_LIMB_UPPER_EYELID) || + (limbIndex == EYEGORE_LIMB_LOWER_EYELID) || (limbIndex == EYEGORE_LIMB_HIPS) || + (limbIndex == EYEGORE_LIMB_LEFT_SHIN) || (limbIndex == EYEGORE_LIMB_RIGHT_SHIN)) { + Matrix_MultZero(&this->limbPos[this->limbPosIndex]); + this->limbPosIndex++; + if (this->limbPosIndex >= ARRAY_COUNT(this->limbPos)) { + this->limbPosIndex = 0; + } + } + Collider_UpdateSpheres(limbIndex, &this->bodyCollider); + Collider_UpdateSpheres(limbIndex, &this->eyeCollider); +} + +void EnEgol_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnEgol* this = THIS; + + OPEN_DISPS(play->state.gfxCtx); + + func_8012C28C(play->state.gfxCtx); + if (this->action != EYEGORE_ACTION_DEAD) { + AnimatedMat_Draw(play, Lib_SegmentedToVirtual(gEyegoreEyeLaserTexAnim)); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnEgol_OverrideLimbDraw, EnEgol_PostLimbDraw, &this->actor); + POLY_OPA_DISP = func_801660B8(play, POLY_OPA_DISP); + } + if (this->dmgEffectTimer != 0) { + f32 drawDmgEffAlpha = 0.05f * this->dmgEffectTimer; + + Actor_DrawDamageEffects(play, &this->actor, this->limbPos, ARRAY_COUNT(this->limbPos), 0.8f, 0.8f, + drawDmgEffAlpha, ACTOR_DRAW_DMGEFF_LIGHT_ORBS); + } + if (this->laserState >= EYEGORE_LASER_FIRE) { + this->laserRot.y = this->actor.world.rot.y; + gSPSegment(POLY_OPA_DISP++, 0x08, func_8012CB28(play->state.gfxCtx, 0, this->texScroll)); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + Matrix_Translate(this->laserBase.x, this->laserBase.y, this->laserBase.z, MTXMODE_NEW); + Matrix_RotateZYX(this->laserRot.x, this->laserRot.y, 0, MTXMODE_APPLY); + Matrix_Scale(this->laserScale.x, this->laserScale.y, this->laserScale.z, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gEyegoreLaserDL); + } + if (((this->action == EYEGORE_ACTION_LASER) && (this->laserState >= EYEGORE_LASER_FIRE)) || this->chargingLaser) { + func_8012C2DC(play->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gLightOrbMaterial1DL); + if (!this->chargingLaser) { + s32 i; + f32 laserLightScaleMod; + f32 laserLightAlpha; + + gDPSetEnvColor(POLY_XLU_DISP++, 155, 255, 255, 128); + Matrix_Translate(this->eyePos.x, this->eyePos.y, this->eyePos.z, MTXMODE_NEW); + Matrix_Scale(this->laserLightScale, this->laserLightScale, this->laserLightScale, MTXMODE_APPLY); + + laserLightScaleMod = 10.0f; + laserLightAlpha = 80.0f; + for (i = 0; i < ARRAY_COUNT(sLightOrbColors); i++) { + Matrix_Push(); + Matrix_Scale(laserLightScaleMod, laserLightScaleMod, laserLightScaleMod, MTXMODE_APPLY); + Matrix_ReplaceRotation(&play->billboardMtxF); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, sLightOrbColors[i].r, sLightOrbColors[i].g, sLightOrbColors[i].b, + laserLightAlpha); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gLightOrbModelDL); + Matrix_Pop(); + laserLightScaleMod = 3.0f; + laserLightAlpha = 200.0f; + } + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 155, 255, 255, 128); + Matrix_Translate(this->eyePos.x, this->eyePos.y, this->eyePos.z, MTXMODE_NEW); + Matrix_Scale(this->chargeLightScale, this->chargeLightScale, this->chargeLightScale, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&play->billboardMtxF); + Matrix_RotateZS(this->chargeLightRot, MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gLightOrbModelDL); + Matrix_Pop(); + } + } + EnEgol_DrawEffects(this, play); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnEgol_SpawnEffect(EnEgol* this, Vec3f* pos, Vec3s* rot, s16 lifetime, f32 scale, s16 type) { + EnEgolEffect* effect = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (!effect->isActive) { + effect->isActive = true; + effect->scale = scale; + effect->pos = *pos; + effect->timer = lifetime; + effect->alpha = 255; + effect->type = type; + effect->rot.x = randPlusMinusPoint5Scaled(0x7530); + effect->rot.y = randPlusMinusPoint5Scaled(0x7530); + effect->rot.z = randPlusMinusPoint5Scaled(0x7530); + if ((effect->type == EYEGORE_EFFECT_PIECE_LARGE) || (effect->type == EYEGORE_EFFECT_PIECE_SMALL)) { + effect->accel.y = -1.0f; + effect->velocity.x = 4.0f * (Rand_ZeroOne() - 0.5f); + effect->velocity.y = 10.0f + (10.0f * Rand_ZeroOne()); + effect->velocity.z = 4.0f * (Rand_ZeroOne() - 0.5f); + break; + } else if (effect->type == EYEGORE_EFFECT_DEBRIS) { + effect->accel.y = -1.0f; + effect->velocity.x = 0.5f * (Rand_ZeroOne() - 0.5f); + effect->velocity.y = 5.0f + (5.0f * Rand_ZeroOne()); + effect->velocity.z = 0.5f * (Rand_ZeroOne() - 0.5f); + effect->timer = 20.0f + Rand_ZeroFloat(10.0f); + break; + } else if (effect->type == EYEGORE_EFFECT_IMPACT) { + effect->rot.x = rot->x; + effect->rot.y = 0; + effect->rot.z = rot->z; + break; + } + } + } +} + +void EnEgol_UpdateEffects(EnEgol* this, PlayState* play) { + EnEgolEffect* effect = this->effects; + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (effect->isActive) { + if (this->action == EYEGORE_ACTION_DYING) { + effect->timer = 0; + } + if (effect->type != EYEGORE_EFFECT_IMPACT) { + effect->rot.x += 0x100; + effect->rot.z += 0x130; + + effect->pos.x += effect->velocity.x; + effect->pos.y += effect->velocity.y; + effect->pos.z += effect->velocity.z; + + effect->velocity.y += effect->accel.y; + } + if (effect->timer != 0) { + effect->timer--; + } else if (effect->type == EYEGORE_EFFECT_IMPACT) { + effect->alpha -= 10; + if (effect->alpha < 10) { + effect->isActive = false; + } + } else { + effect->isActive = false; + } + } + } +} + +void EnEgol_DrawEffects(EnEgol* this, PlayState* play) { + GraphicsContext* gfxCtx = play->state.gfxCtx; + EnEgolEffect* effect = this->effects; + s16 i; + + OPEN_DISPS(gfxCtx); + + func_8012C28C(play->state.gfxCtx); + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (effect->isActive) { + Matrix_Push(); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateXS(effect->rot.x, MTXMODE_APPLY); + Matrix_RotateYS(effect->rot.y, MTXMODE_APPLY); + Matrix_RotateZS(effect->rot.z, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + + switch (effect->type) { + case EYEGORE_EFFECT_IMPACT: + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0x80, 155, 155, 155, 255); + Matrix_Translate(0.0f, 50.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, effect->alpha); + gSPDisplayList(POLY_OPA_DISP++, gEyegoreEffectImpactDL); + break; + + case EYEGORE_EFFECT_PIECE_LARGE: + case EYEGORE_EFFECT_PIECE_SMALL: + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0x80, 255, 255, 255, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, effect->alpha); + if (effect->type == EYEGORE_EFFECT_PIECE_LARGE) { + gSPDisplayList(POLY_OPA_DISP++, gEyegoreEffectLargeBodyPieceDL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gEyegoreEffectSmallBodyPieceDL); + } + break; + + case EYEGORE_EFFECT_DEBRIS: + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0x80, 255, 255, 255, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + gSPDisplayList(POLY_OPA_DISP++, gEyegoreEffectSolidDebrisDL); + break; + + default: + break; + } + Matrix_Pop(); + } + } + + CLOSE_DISPS(gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Egol/z_en_egol.h b/src/overlays/actors/ovl_En_Egol/z_en_egol.h index bfcc532310..cec69e5a14 100644 --- a/src/overlays/actors/ovl_En_Egol/z_en_egol.h +++ b/src/overlays/actors/ovl_En_Egol/z_en_egol.h @@ -2,16 +2,94 @@ #define Z_EN_EGOL_H #include "global.h" +#include "objects/object_eg/object_eg.h" struct EnEgol; typedef void (*EnEgolActionFunc)(struct EnEgol*, PlayState*); +#define EYEGORE_GET_PATH(thisx) ((thisx)->params & 0x3F) +#define EYEGORE_GET_SWITCH(thisx) (((thisx)->params >> 6) & 0x7F) + +#define EYEGORE_PARAMS(switch, path) ((((switch) & 0x7F) << 6) | ((path) & 0x3F)) + +#define EYEGORE_EFFECT_COUNT 100 + +typedef struct { + /* 0x00 */ u8 isActive; + /* 0x04 */ Vec3f pos; + /* 0x10 */ s16 alpha; + /* 0x12 */ s16 timer; + /* 0x14 */ s16 type; + /* 0x18 */ Vec3f velocity; + /* 0x24 */ Vec3f accel; + /* 0x30 */ Vec3s rot; + /* 0x38 */ f32 scale; +} EnEgolEffect; // size = 0x3C + typedef struct EnEgol { /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0x164]; + /* 0x0144 */ SkelAnime skelAnime; + /* 0x0188 */ Vec3s jointTable[EYEGORE_LIMB_MAX]; + /* 0x0218 */ Vec3s morphTable[EYEGORE_LIMB_MAX]; /* 0x02A8 */ EnEgolActionFunc actionFunc; - /* 0x02AC */ char unk_2AC[0x1BC4]; + /* 0x02AC */ Path* path; + /* 0x02B0 */ s16 waypoint; + /* 0x02B2 */ s16 action; + /* 0x02B4 */ s16 waitTimer; + /* 0x02B6 */ s16 eyeShutTimer; + /* 0x02B4 */ s16 headRot; + /* 0x02BA */ s16 switchFlag; + /* 0x02BC */ s16 dmgEffectTimer; + /* 0x02C0 */ Vec3f limbPos[12]; + /* 0x0350 */ s16 limbPosIndex; + /* 0x0352 */ s16 pathIndex; + /* 0x0354 */ s32 animation; + /* 0x0358 */ u8 isRetreating; + /* 0x035C */ f32 animEndFrame; + /* 0x0360 */ f32 wakeupRange; + /* 0x0364 */ f32 minLaserRange; + /* 0x0368 */ UNK_TYPE1 unk_368[4]; // f32? + /* 0x036C */ f32 laserLightScale; + /* 0x0370 */ f32 chargeLightScale; + /* 0x0374 */ s16 eyelidRot; + /* 0x0376 */ s16 eyelidRotTarget; + /* 0x0378 */ s16 actionTimer; + /* 0x037C */ s32 laserCount; + /* 0x0380 */ s32 chargingLaser; + /* 0x0384 */ s16 chargeLightRot; + /* 0x0388 */ s32 chargeLevel; + /* 0x038C */ s32 hitPlayer; + /* 0x0390 */ UNK_TYPE1 unk_390[0xC]; // Vec3f? + /* 0x039C */ Vec3f eyePos; + /* 0x03A8 */ Vec3f laserBase; + /* 0x03B4 */ Vec3f laserScale; + /* 0x03C0 */ Vec3f laserScaleTarget; + /* 0x03CC */ UNK_TYPE1 unk_3CC[0xC]; // Vec3f? + /* 0x03D8 */ Vec3f leftFootPos; + /* 0x03E4 */ Vec3f rightFootPos; + /* 0x03F0 */ Vec3f leftHandPos; + /* 0x03FC */ Vec3f rightHandPos; + /* 0x0408 */ Vec3f waypointPos; + /* 0x0414 */ Vec3s pupilRot; + /* 0x041A */ Vec3s laserRot; + /* 0x0420 */ Color_RGB16 eyePrimColor; + /* 0x0426 */ Color_RGB16 eyeEnvColor; + /* 0x042C */ s16 laserState; + /* 0x0430 */ s32 texScroll; + /* 0x0434 */ EnEgolEffect effects[EYEGORE_EFFECT_COUNT]; + /* 0x1BA4 */ ColliderJntSph bodyCollider; + /* 0x1BC4 */ ColliderJntSphElement bodyElements[6]; + /* 0x1D44 */ ColliderJntSph eyeCollider; + /* 0x1D64 */ ColliderJntSphElement eyeElements[1]; + /* 0x1DA4 */ ColliderQuad laserCollider; + /* 0x1E24 */ UNK_TYPE1 unk_1E24[4]; + /* 0x1E28 */ s16 subCamId; + /* 0x1E2C */ f32 subCamFov; + /* 0x1E30 */ f32 subCamFovTarget; + /* 0x1E34 */ Vec3f subCamAt; + /* 0x1E40 */ Vec3f subCamEye; + /* 0x1E4C */ UNK_TYPE1 unk_1E4C[0x24]; // Vec3f[3]? } EnEgol; // size = 0x1E70 #endif // Z_EN_EGOL_H diff --git a/src/overlays/actors/ovl_En_Estone/z_en_estone.c b/src/overlays/actors/ovl_En_Estone/z_en_estone.c index 67a3042ee1..c55ab52720 100644 --- a/src/overlays/actors/ovl_En_Estone/z_en_estone.c +++ b/src/overlays/actors/ovl_En_Estone/z_en_estone.c @@ -1,10 +1,12 @@ /* * File: z_en_estone.c * Overlay: ovl_En_Estone - * Description: Eyegore Rubble + * Description: Rubble from Eyegore's attacks */ #include "z_en_estone.h" +#include "objects/object_eg/object_eg.h" +#include "objects/gameplay_keep/gameplay_keep.h" #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20) @@ -12,13 +14,15 @@ void EnEstone_Init(Actor* thisx, PlayState* play); void EnEstone_Destroy(Actor* thisx, PlayState* play); -void EnEstone_Update(Actor* thisx, PlayState* play); -void EnEstone_Draw(Actor* thisx, PlayState* play); +void EnEstone_Update(Actor* thisx, PlayState* play2); +void EnEstone_Draw(Actor* thisx, PlayState* play2); -void func_80A9A1DC(EnEstone* this, PlayState* play); -void func_80A9A4B0(EnEstone* this, PlayState* play); +void EnEstone_Active(EnEstone* this, PlayState* play); +void EnEstone_Inactive(EnEstone* this, PlayState* play); +void EnEstone_SpawnEffect(EnEstone* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale, s16 lifetime); +void EnEstone_UpdateEffects(EnEstone* this, PlayState* play); +void EnEstone_DrawEffects(EnEstone* this, PlayState* play); -#if 0 ActorInit En_Estone_InitVars = { ACTOR_EN_ESTONE, ACTORCAT_PROP, @@ -31,33 +35,250 @@ ActorInit En_Estone_InitVars = { (ActorFunc)EnEstone_Draw, }; -// static ColliderCylinderInit sCylinderInit = { -static ColliderCylinderInit D_80A9AB70 = { - { COLTYPE_HARD, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_NONE, OC2_TYPE_2, COLSHAPE_CYLINDER, }, - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_NONE, }, +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HARD, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x00, 0x04 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, { 30, 30, -10, { 0, 0, 0 } }, }; -#endif +void EnEstone_Init(Actor* thisx, PlayState* play) { + EnEstone* this = THIS; + Vec3f accel; + Vec3f velocity; + f32 scale; + s32 i; + s32 pad; -extern ColliderCylinderInit D_80A9AB70; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 15.0f); + this->rotVel.x = this->rotVel.y = this->rotVel.z = randPlusMinusPoint5Scaled(1.0f) * 20.0f; + Collider_InitAndSetCylinder(play, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.world.rot.y += (s16)randPlusMinusPoint5Scaled(0x2710); + this->actor.shape.rot.y = this->actor.world.rot.y; -extern UNK_TYPE D_06010240; + if (this->actor.params == ENESTONE_TYPE_LARGE) { + this->actor.speedXZ = Rand_ZeroFloat(5.0f) + 2.0f; + this->scale = (Rand_ZeroFloat(1.0f) * 0.005f) + 0.005f; + this->actor.velocity.y = Rand_ZeroFloat(10.0f) + 15.0f; + this->actor.gravity = -2.0f; + } else { + this->actor.speedXZ = Rand_ZeroFloat(3.0f) + 1.0f; + this->scale = (Rand_ZeroFloat(1.0f) * 0.003f) + 0.003f; + this->actor.velocity.y = Rand_ZeroFloat(5.0f) + 7.0f; + this->actor.gravity = -1.0f; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/EnEstone_Init.s") + this->collider.dim.radius = this->scale * 3000.0f; + this->collider.dim.height = this->scale * 3000.0f; + this->collider.dim.yShift = this->scale * -1300.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/EnEstone_Destroy.s") + for (i = 0; i < 7; i++) { + accel.x = 0.5f * (Rand_ZeroOne() - 0.5f); + accel.y = -1.0f; + accel.z = 0.5f * (Rand_ZeroOne() - 0.5f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/func_80A9A1DC.s") + velocity.x = 2.0f * (Rand_ZeroOne() - 0.5f); + velocity.y = 8.0f + (Rand_ZeroOne() * 10.0f); + velocity.z = 2.0f * (Rand_ZeroOne() - 0.5f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/func_80A9A4B0.s") + scale = (Rand_ZeroFloat(1.0f) * 0.001f) + 0.001f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/EnEstone_Update.s") + EnEstone_SpawnEffect(this, &this->actor.world.pos, &velocity, &accel, scale, 70); + } + this->timer = 5; + this->inactive = false; + Actor_SetScale(&this->actor, 1.0f); + this->actionFunc = EnEstone_Active; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/EnEstone_Draw.s") +void EnEstone_Destroy(Actor* thisx, PlayState* play) { + EnEstone* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/func_80A9A774.s") + Collider_DestroyCylinder(play, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/func_80A9A870.s") +void EnEstone_Active(EnEstone* this, PlayState* play) { + Player* player = GET_PLAYER(play); + Vec3f accel; + Vec3f velocity; + s32 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Estone/func_80A9A9C0.s") + if ((this->actor.floorHeight == -10000.0f) || ((player->actor.world.pos.y - 200.0f) > this->actor.world.pos.y)) { + Actor_Kill(&this->actor); + return; + } + if (((this->actor.bgCheckFlags & 1) && (this->actor.velocity.y < 0.0f)) || (this->collider.base.atFlags & 4)) { + this->collider.base.atFlags &= ~4; + if (this->actor.params == ENESTONE_TYPE_LARGE) { + for (i = 0; i < 2; i++) { + accel.x = 2.0f * (Rand_ZeroOne() - 0.5f); + accel.y = -1.0f; + accel.z = 2.0f * (Rand_ZeroOne() - 0.5f); + velocity.x = 3.0f * (Rand_ZeroOne() - 0.5f); + velocity.y = 5.0f + (Rand_ZeroOne() * 10.0f); + velocity.z = 3.0f * (Rand_ZeroOne() - 0.5f); + EnEstone_SpawnEffect(this, &this->actor.world.pos, &velocity, &accel, + 0.003f + (0.002f * Rand_ZeroFloat(1.0f)), 20); + } + } + Math_Vec3f_Copy(&velocity, &this->actor.world.pos); + velocity.y = this->actor.floorHeight; + Actor_SpawnFloorDustRing(play, &this->actor, &velocity, 0.0f, 10, 6.0f, 50, 30, true); + this->actor.velocity.y = this->actor.gravity = 0.0f; + this->actor.speedXZ *= 0.3f; + this->actor.shape.shadowScale = 0.0f; + this->inactive = true; + this->timer = 50; + Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_WALL_BROKEN); + this->actionFunc = EnEstone_Inactive; + } else { + //! FAKE: https://decomp.me/scratch/YiPVN + label: + this->actor.shape.shadowScale = 4500.0f * this->scale; + Actor_SetScale(&this->actor, this->scale); + } +} + +void EnEstone_Inactive(EnEstone* this, PlayState* play) { + if (this->timer == 0) { + Actor_Kill(&this->actor); + } +} + +void EnEstone_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnEstone* this = THIS; + + DECR(this->timer); + + this->actionFunc(this, play); + + this->rot.x -= this->rotVel.x; + this->rot.y -= this->rotVel.y; + this->rot.z -= this->rotVel.z; + + Actor_MoveWithGravity(&this->actor); + if ((this->timer == 0) && !this->inactive) { + Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 50.0f, 100.0f, 0x1C); + } + if (!this->inactive) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + } + EnEstone_UpdateEffects(this, play); +} + +void EnEstone_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnEstone* this = THIS; + + if (this->inactive != true) { + Matrix_Push(); + + OPEN_DISPS(play->state.gfxCtx); + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateXFApply(this->rot.x * (M_PI / 180.0f)); + Matrix_RotateYF(this->rot.y * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_RotateZF(this->rot.z * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_Scale(this->scale, this->scale, this->scale, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0x80, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gEyegoreStoneDL); + + CLOSE_DISPS(play->state.gfxCtx); + + Matrix_Pop(); + } + EnEstone_DrawEffects(this, play); +} + +void EnEstone_SpawnEffect(EnEstone* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale, s16 lifetime) { + EnEstoneEffect* effect = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (!effect->isActive) { + effect->isActive = true; + effect->pos = *pos; + effect->velocity = *velocity; + effect->accel = *accel; + effect->scale = scale; + effect->timer = lifetime; + effect->rot.x = randPlusMinusPoint5Scaled(0x7530); + effect->rot.y = randPlusMinusPoint5Scaled(0x7530); + effect->rot.z = randPlusMinusPoint5Scaled(0x7530); + break; + } + } +} + +void EnEstone_UpdateEffects(EnEstone* this, PlayState* play) { + EnEstoneEffect* effect = this->effects; + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (effect->isActive) { + effect->rot.x += 0x100; + effect->rot.z += 0x130; + + effect->pos.x += effect->velocity.x; + effect->pos.y += effect->velocity.y; + effect->pos.z += effect->velocity.z; + + effect->velocity.y += effect->accel.y; + if ((effect->pos.y < this->actor.floorHeight) && (effect->velocity.y < 0.0f)) { + effect->timer = 0; + Actor_SpawnFloorDustRing(play, &this->actor, &effect->pos, 0.0f, 5, 3.0f, 30, 15, true); + } + if (effect->timer != 0) { + effect->timer--; + } else { + effect->isActive = false; + } + } + } +} + +void EnEstone_DrawEffects(EnEstone* this, PlayState* play) { + EnEstoneEffect* effect = this->effects; + GraphicsContext* gfxCtx = play->state.gfxCtx; + s16 i; + + OPEN_DISPS(gfxCtx); + + func_8012C28C(play->state.gfxCtx); + for (i = 0; i < ARRAY_COUNT(this->effects); i++, effect++) { + if (effect->isActive) { + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateXS(effect->rot.x, MTXMODE_APPLY); + Matrix_RotateYS(effect->rot.y, MTXMODE_APPLY); + Matrix_RotateZS(effect->rot.z, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x80, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + gSPDisplayList(POLY_OPA_DISP++, gameplay_keep_DL_06AB30); + } + } + + CLOSE_DISPS(gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Estone/z_en_estone.h b/src/overlays/actors/ovl_En_Estone/z_en_estone.h index 8e4fa075db..ceb26c9c60 100644 --- a/src/overlays/actors/ovl_En_Estone/z_en_estone.h +++ b/src/overlays/actors/ovl_En_Estone/z_en_estone.h @@ -7,10 +7,33 @@ struct EnEstone; typedef void (*EnEstoneActionFunc)(struct EnEstone*, PlayState*); +#define ENESTONE_EFFECT_COUNT 200 + +typedef enum { + /* 0 */ ENESTONE_TYPE_LARGE, + /* 1 */ ENESTONE_TYPE_SMALL, +} EnEstoneType; + +typedef struct { + /* 0x00 */ u8 isActive; + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ Vec3s rot; + /* 0x30 */ f32 scale; + /* 0x34 */ s16 timer; +} EnEstoneEffect; // size = 0x38 + typedef struct EnEstone { /* 0x0000 */ Actor actor; /* 0x0144 */ EnEstoneActionFunc actionFunc; - /* 0x0148 */ char unk_148[0x2C2C]; + /* 0x0148 */ f32 scale; + /* 0x014C */ Vec3f rot; + /* 0x0158 */ Vec3f rotVel; + /* 0x0164 */ s16 timer; + /* 0x0166 */ s16 inactive; + /* 0x0168 */ ColliderCylinder collider; + /* 0x01B4 */ EnEstoneEffect effects[ENESTONE_EFFECT_COUNT]; } EnEstone; // size = 0x2D74 #endif // Z_EN_ESTONE_H diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 8dfbf22cb9..b515239a58 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -11108,48 +11108,48 @@ 0x80A7C5EC:("ObjEtcetera_Update",), 0x80A7C690:("ObjEtcetera_DrawIdle",), 0x80A7C718:("ObjEtcetera_DrawAnimated",), - 0x80A7C990:("func_80A7C990",), - 0x80A7CA18:("func_80A7CA18",), - 0x80A7CBC4:("func_80A7CBC4",), - 0x80A7CD08:("func_80A7CD08",), + 0x80A7C990:("EnEgol_ChangeAnim",), + 0x80A7CA18:("EnEgol_FootstepEffects",), + 0x80A7CBC4:("EnEgol_DestroyBlocks",), + 0x80A7CD08:("EnEgol_GetWaypoint",), 0x80A7CD60:("EnEgol_Init",), 0x80A7D0F0:("EnEgol_Destroy",), - 0x80A7D140:("func_80A7D140",), - 0x80A7D168:("func_80A7D168",), - 0x80A7D1E4:("func_80A7D1E4",), - 0x80A7D240:("func_80A7D240",), - 0x80A7D27C:("func_80A7D27C",), - 0x80A7D2C4:("func_80A7D2C4",), - 0x80A7D710:("func_80A7D710",), - 0x80A7D780:("func_80A7D780",), - 0x80A7DAAC:("func_80A7DAAC",), - 0x80A7DAF0:("func_80A7DAF0",), - 0x80A7E2E8:("func_80A7E2E8",), - 0x80A7E344:("func_80A7E344",), - 0x80A7E478:("func_80A7E478",), - 0x80A7E4B4:("func_80A7E4B4",), - 0x80A7E7EC:("func_80A7E7EC",), - 0x80A7E820:("func_80A7E820",), - 0x80A7E924:("func_80A7E924",), - 0x80A7E970:("func_80A7E970",), - 0x80A7E9D0:("func_80A7E9D0",), - 0x80A7EA28:("func_80A7EA28",), - 0x80A7EA88:("func_80A7EA88",), - 0x80A7EAD8:("func_80A7EAD8",), - 0x80A7EB14:("func_80A7EB14",), - 0x80A7EB54:("func_80A7EB54",), - 0x80A7EB90:("func_80A7EB90",), - 0x80A7EBDC:("func_80A7EBDC",), - 0x80A7EC84:("func_80A7EC84",), - 0x80A7ED14:("func_80A7ED14",), - 0x80A7EFB8:("func_80A7EFB8",), + 0x80A7D140:("EnEgol_SetupWait",), + 0x80A7D168:("EnEgol_Wait",), + 0x80A7D1E4:("EnEgol_SetupStand",), + 0x80A7D240:("EnEgol_Stand",), + 0x80A7D27C:("EnEgol_SetupWalk",), + 0x80A7D2C4:("EnEgol_Walk",), + 0x80A7D710:("EnEgol_SetupRetreat",), + 0x80A7D780:("EnEgol_Retreat",), + 0x80A7DAAC:("EnEgol_SetupLaser",), + 0x80A7DAF0:("EnEgol_Laser",), + 0x80A7E2E8:("EnEgol_SetupStop",), + 0x80A7E344:("EnEgol_Stop",), + 0x80A7E478:("EnEgol_SetupSlam",), + 0x80A7E4B4:("EnEgol_Slam",), + 0x80A7E7EC:("EnEgol_SetupPunch",), + 0x80A7E820:("EnEgol_Punch",), + 0x80A7E924:("EnEgol_SetupSlamWait",), + 0x80A7E970:("EnEgol_SlamWait",), + 0x80A7E9D0:("EnEgol_SetupStunned",), + 0x80A7EA28:("EnEgol_Stunned",), + 0x80A7EA88:("EnEgol_SetupStunEnd",), + 0x80A7EAD8:("EnEgol_StunEnd",), + 0x80A7EB14:("EnEgol_SetupSlamEnd",), + 0x80A7EB54:("EnEgol_SlamEnd",), + 0x80A7EB90:("EnEgol_SetupDamaged",), + 0x80A7EBDC:("EnEgol_Damaged",), + 0x80A7EC84:("EnEgol_StartDeathCutscene",), + 0x80A7ED14:("EnEgol_Death",), + 0x80A7EFB8:("EnEgol_CollisionCheck",), 0x80A7F354:("EnEgol_Update",), - 0x80A7F8E8:("func_80A7F8E8",), - 0x80A7FAFC:("func_80A7FAFC",), + 0x80A7F8E8:("EnEgol_OverrideLimbDraw",), + 0x80A7FAFC:("EnEgol_PostLimbDraw",), 0x80A7FFB8:("EnEgol_Draw",), - 0x80A80508:("func_80A80508",), - 0x80A80750:("func_80A80750",), - 0x80A80904:("func_80A80904",), + 0x80A80508:("EnEgol_SpawnEffect",), + 0x80A80750:("EnEgol_UpdateEffects",), + 0x80A80904:("EnEgol_DrawEffects",), 0x80A811D0:("func_80A811D0",), 0x80A8120C:("func_80A8120C",), 0x80A81288:("func_80A81288",), @@ -11409,13 +11409,13 @@ 0x80A98F94:("func_80A98F94",), 0x80A99EA0:("EnEstone_Init",), 0x80A9A1B0:("EnEstone_Destroy",), - 0x80A9A1DC:("func_80A9A1DC",), - 0x80A9A4B0:("func_80A9A4B0",), + 0x80A9A1DC:("EnEstone_Active",), + 0x80A9A4B0:("EnEstone_Inactive",), 0x80A9A4E0:("EnEstone_Update",), 0x80A9A600:("EnEstone_Draw",), - 0x80A9A774:("func_80A9A774",), - 0x80A9A870:("func_80A9A870",), - 0x80A9A9C0:("func_80A9A9C0",), + 0x80A9A774:("EnEstone_SpawnEffect",), + 0x80A9A870:("EnEstone_UpdateEffects",), + 0x80A9A9C0:("EnEstone_DrawEffects",), 0x80A9ACD0:("func_80A9ACD0",), 0x80A9ACF0:("func_80A9ACF0",), 0x80A9AD18:("func_80A9AD18",), @@ -11794,14 +11794,14 @@ 0x80AB9C4C:("func_80AB9C4C",), 0x80ABA7A0:("EnEgblock_Init",), 0x80ABA868:("EnEgblock_Destroy",), - 0x80ABA8A4:("func_80ABA8A4",), - 0x80ABA988:("func_80ABA988",), - 0x80ABA9B8:("func_80ABA9B8",), + 0x80ABA8A4:("EnEgblock_Active",), + 0x80ABA988:("EnEgblock_Inactive",), + 0x80ABA9B8:("EnEgblock_DoNothing",), 0x80ABA9C8:("EnEgblock_Update",), 0x80ABAA14:("EnEgblock_Draw",), - 0x80ABAAF4:("func_80ABAAF4",), - 0x80ABACB4:("func_80ABACB4",), - 0x80ABAE64:("func_80ABAE64",), + 0x80ABAAF4:("EnEgblock_SpawnEffect",), + 0x80ABACB4:("EnEgblock_UpdateEffects",), + 0x80ABAE64:("EnEgblock_DrawEffects",), 0x80ABB0E0:("EnGuardNuts_Init",), 0x80ABB1E4:("EnGuardNuts_Destroy",), 0x80ABB210:("EnGuardNuts_ChangeAnim",),