diff --git a/spec b/spec index 0afddf622a..2302f03771 100644 --- a/spec +++ b/spec @@ -4630,8 +4630,7 @@ beginseg name "ovl_En_Tanron3" compress include "build/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.o" - include "build/data/ovl_En_Tanron3/ovl_En_Tanron3.data.o" - include "build/data/ovl_En_Tanron3/ovl_En_Tanron3.reloc.o" + include "build/src/overlays/actors/ovl_En_Tanron3/ovl_En_Tanron3_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_Boss_03/z_boss_03.h b/src/overlays/actors/ovl_Boss_03/z_boss_03.h index e93baca0bf..ca8a92328a 100644 --- a/src/overlays/actors/ovl_Boss_03/z_boss_03.h +++ b/src/overlays/actors/ovl_Boss_03/z_boss_03.h @@ -9,9 +9,13 @@ typedef void (*Boss03ActionFunc)(struct Boss03*, GlobalContext*); typedef struct Boss03 { /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0x1E4]; + /* 0x0144 */ UNK_TYPE1 unk_144[0x10E]; + /* 0x0252 */ s8 unk_252; // number of Tanron3 fish that are currently alive, maybe "numSmallFishAlive"? + /* 0x0253 */ UNK_TYPE1 unk_253[0xD1]; + /* 0x0324 */ s16 unk_324; + /* 0x0326 */ UNK_TYPE1 unk_326[0x2]; /* 0x0328 */ Boss03ActionFunc actionFunc; - /* 0x032C */ char unk_32C[0x250]; + /* 0x032C */ UNK_TYPE1 unk_32C[0x250]; } Boss03; // size = 0x57C extern const ActorInit Boss_03_InitVars; diff --git a/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.c b/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.c index 2de774191f..313a0a1fdb 100644 --- a/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.c +++ b/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.c @@ -1,10 +1,11 @@ /* * File: z_en_tanron3.c * Overlay: ovl_En_Tanron3 - * Description: Small Fish summoned by Gyorg + * Description: Small fish summoned by Gyorg */ #include "z_en_tanron3.h" +#include "overlays/actors/ovl_Boss_03/z_boss_03.h" #define FLAGS 0x00000035 @@ -15,7 +16,14 @@ void EnTanron3_Destroy(Actor* thisx, GlobalContext* globalCtx); void EnTanron3_Update(Actor* thisx, GlobalContext* globalCtx); void EnTanron3_Draw(Actor* thisx, GlobalContext* globalCtx); -#if 0 +void EnTanron3_SetupLive(EnTanron3* this, GlobalContext* globalCtx); +void EnTanron3_Live(EnTanron3* this, GlobalContext* globalCtx); +void EnTanron3_Die(EnTanron3* this, GlobalContext* globalCtx); + +static Vec3f sZeroVec[] = { 0.0f, 0.0f, 0.0f }; + +static Boss03* sGyorg = NULL; + const ActorInit En_Tanron3_InitVars = { ACTOR_EN_TANRON3, ACTORCAT_BOSS, @@ -28,39 +36,419 @@ const ActorInit En_Tanron3_InitVars = { (ActorFunc)EnTanron3_Draw, }; -// static ColliderCylinderInit sCylinderInit = { -static ColliderCylinderInit D_80BB9750 = { - { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, }, - { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x02 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK3, + { 0xF7CFFFFF, 0x00, 0x02 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, { 7, 10, -5, { 0, 0, 0 } }, }; -#endif +// This actor has two colliders (one for AC and one for AT), but uses the same +// ColliderCylinderInit for both of them, leaving this one totally unused. +static ColliderCylinderInit sUnusedCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK3, + { 0xF7CFFFFF, 0x00, 0x02 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, 20, -10, { 0, 0, 0 } }, +}; -extern ColliderCylinderInit D_80BB9750; +extern FlexSkeletonHeader D_0600DA20; +extern AnimationHeader D_0600DAAC; -extern UNK_TYPE D_0600DAAC; +void EnTanron3_CreateEffect(GlobalContext* globalCtx, Vec3f* effectPos) { + UnkTanron3Effect* effectPtr = (UnkTanron3Effect*)globalCtx->specialEffects; + s16 i; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB85A0.s") + for (i = 0; i < 150; i++, effectPtr++) { + if ((effectPtr->type == 0) || (effectPtr->type == 1)) { + effectPtr->type = 2; + effectPtr->pos = *effectPos; + effectPtr->velocity = *sZeroVec; + effectPtr->accel = *sZeroVec; + effectPtr->accel.y = -2.0f; + effectPtr->unk_34.x = 0.1f; + effectPtr->unk_34.y = 0.0f; + effectPtr->unk_34.z = Rand_ZeroFloat(2 * M_PI); + effectPtr->unk_02 = Rand_ZeroFloat(100.0f); + effectPtr->velocity.x = randPlusMinusPoint5Scaled(25.0f); + effectPtr->velocity.z = randPlusMinusPoint5Scaled(25.0f); + break; + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/EnTanron3_Init.s") +void EnTanron3_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTanron3* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/EnTanron3_Destroy.s") + this->actor.gravity = -1.0f; + Collider_InitAndSetCylinder(globalCtx, &this->atCollider, &this->actor, &sCylinderInit); + Collider_InitAndSetCylinder(globalCtx, &this->acCollider, &this->actor, &sCylinderInit); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_0600DA20, &D_0600DAAC, this->jointTable, this->morphTable, 10); + Actor_SetScale(&this->actor, 0.02f); + EnTanron3_SetupLive(this, globalCtx); + this->actor.flags &= ~1; + this->currentRotationAngle = Rand_ZeroFloat(500000.0f); + this->waterSurfaceYPos = 430.0f; + sGyorg = (Boss03*)this->actor.parent; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB87D4.s") +void EnTanron3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + sGyorg->unk_252--; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB897C.s") +void EnTanron3_SpawnBubbles(EnTanron3* this, GlobalContext* globalCtx) { + static Color_RGBA8 sPrimColor = { 100, 55, 55, 255 }; + static Color_RGBA8 sEnvColor = { 50, 10, 10, 255 }; + s32 i; + Vec3f velocity; + Vec3f acceleration; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB8A48.s") + for (i = 0; i < 20; i++) { + Matrix_InsertYRotation_f(Rand_ZeroFloat(2 * M_PI), MTXMODE_NEW); + Matrix_RotateStateAroundXAxis(Rand_ZeroFloat(2 * M_PI)); + Matrix_GetStateTranslationAndScaledZ(Rand_ZeroFloat(3.0f) + 2.0f, &velocity); + acceleration.x = velocity.x * -0.05f; + acceleration.y = velocity.y * -0.05f; + acceleration.z = velocity.z * -0.05f; + EffectSsDtBubble_SpawnCustomColor(globalCtx, &this->actor.world.pos, &velocity, &acceleration, &sPrimColor, + &sEnvColor, Rand_ZeroFloat(30.0f) + 70.0f, Rand_ZeroFloat(5.0f) + 15.0f, 0); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB91D4.s") +void EnTanron3_SetupLive(EnTanron3* this, GlobalContext* globalCtx) { + this->actionFunc = EnTanron3_Live; + Animation_MorphToLoop(&this->skelAnime, &D_0600DAAC, -10.0f); + this->rotationStep = 0; + this->rotationScale = 5; + this->workTimer[TANRON3_WORK_TIMER_PICK_NEW_DEVIATION] = 50; + this->actor.speedXZ = 5.0f; + this->speedMaxStep = 0.5f; + this->deviation.x = randPlusMinusPoint5Scaled(500.0f); + this->deviation.y = randPlusMinusPoint5Scaled(100.0f); + this->deviation.z = randPlusMinusPoint5Scaled(500.0f); + Math_Vec3f_Copy(&this->targetPos, &this->actor.world.pos); + this->timer = Rand_ZeroFloat(100.0f); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB9288.s") +/** + * This controls the vast majority of the fish's behavior while it's alive, including: + * - deciding whether to be hostile or not + * - determing whether the fish is beached or not + * - swimming towards the player to attack them + * - swimming around idly if the player is out of range + * - flopping around on land if it beaches itself + */ +void EnTanron3_Live(EnTanron3* this, GlobalContext* globalCtx) { + s32 atanTemp; + f32 xDistance; + f32 yDistance; + f32 zDistance; + f32 xzDistance; + f32 extraScaleY = 0.0f; + Player* player = GET_PLAYER(globalCtx); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB9308.s") + this->skelAnime.curFrame = 4.0f; + if ((player->actor.bgCheckFlags & 1) && player->actor.shape.feetPos[0].y >= 438.0f) { + // Player is standing on the central platform, so stop chasing them + this->isNonHostile = true; + } else if (this->isNonHostile && this->workTimer[TANRON3_WORK_TIMER_WAIT] == 0 && !(this->timer & 0x1F)) { + xDistance = this->targetPos.x - player->actor.world.pos.x; + zDistance = this->targetPos.z - player->actor.world.pos.z; + if (sqrtf(SQ(xDistance) + SQ(zDistance)) < 500.0f) { + // Player is in the water and close enough, so start chasing them + this->isNonHostile = false; + this->workTimer[TANRON3_WORK_TIMER_ATTACK] = 150; + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/EnTanron3_Update.s") + if (this->actor.world.pos.y < this->waterSurfaceYPos) { + // The fish is below the water's surface, so it's no longer beached if it was before + this->isBeached = false; + switch (this->isNonHostile) { + case false: + this->targetSpeedXZ = 5.0f; + this->targetRotationStep = 0x1000; + this->nextRotationAngle = 0x3A98; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/func_80BB95FC.s") + Math_Vec3f_Copy(&this->targetPos, &player->actor.world.pos); + if (!(this->timer & 0xF)) { + if (Rand_ZeroOne() < 0.5f && this->actor.xzDistToPlayer <= 200.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIRANHA_ATTACK); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Tanron3/EnTanron3_Draw.s") + // If the player gets eaten by Gyorg, or if the attack timer ran out, + // stop chasing the player for a little bit. + if (this->workTimer[TANRON3_WORK_TIMER_ATTACK] == 0 || (player->stateFlags2 & 0x80)) { + this->workTimer[TANRON3_WORK_TIMER_WAIT] = 150; + this->isNonHostile = true; + } + break; + case true: + if (sGyorg->unk_324 != 0 && !(this->timer & 0x7)) { + this->nextRotationAngle = 0x4E20; + this->actor.speedXZ = 6.0f; + } else { + this->nextRotationAngle = 0x1F40; + } + this->targetRotationStep = 0x200; + this->targetSpeedXZ = 2.0f; + + // If the fish is idly swimming around, this code will make it spin in a circle. + // Its target is constantly updated, so it constantly rotates to face it, and its + // momentum keeps it spinning. This code can also be reached when the fish "give up" + // on attacking the player; in that case, this code will make it turn in the + // opposite direction and swim away. In both cases, the fish's target y-position + // will be slightly above the halfway point of the water. + atanTemp = Math_FAtan2F(this->targetPos.z, this->targetPos.x); + Matrix_RotateY(atanTemp, MTXMODE_NEW); + Matrix_GetStateTranslationAndScaledZ(700.0f, &this->targetPos); + this->targetPos.y = 250.0f; + + extraScaleY = 150.0f; + break; + } + + if (this->workTimer[TANRON3_WORK_TIMER_OUT_OF_WATER] == 0) { + if (this->workTimer[TANRON3_WORK_TIMER_PICK_NEW_DEVIATION] == 0 && this->actor.speedXZ > 1.0f) { + this->workTimer[TANRON3_WORK_TIMER_PICK_NEW_DEVIATION] = Rand_ZeroFloat(20.0f); + this->deviation.x = randPlusMinusPoint5Scaled(100.0f); + this->deviation.y = randPlusMinusPoint5Scaled(50.0f + extraScaleY); + this->deviation.z = randPlusMinusPoint5Scaled(100.0f); + } + this->targetPosWithDeviation.y = this->targetPos.y + this->deviation.y + 50.0f; + } + + this->targetPosWithDeviation.x = this->targetPos.x + this->deviation.x; + this->targetPosWithDeviation.z = this->targetPos.z + this->deviation.z; + xDistance = this->targetPosWithDeviation.x - this->actor.world.pos.x; + yDistance = this->targetPosWithDeviation.y - this->actor.world.pos.y; + zDistance = this->targetPosWithDeviation.z - this->actor.world.pos.z; + + // Rotate the fish to look towards its target + xzDistance = sqrtf(SQ(xDistance) + SQ(zDistance)); + atanTemp = Math_FAtan2F(xzDistance, -yDistance); + Math_ApproachS(&this->actor.world.rot.x, atanTemp, this->rotationScale, this->rotationStep); + atanTemp = Math_FAtan2F(zDistance, xDistance); + Math_SmoothStepToS(&this->actor.world.rot.y, atanTemp, this->rotationScale, this->rotationStep, 0); + Math_ApproachS(&this->rotationStep, this->targetRotationStep, 1, 0x100); + + Math_ApproachF(&this->actor.speedXZ, this->targetSpeedXZ, 1.0f, this->speedMaxStep); + Actor_SetVelocityAndMoveXYRotationReverse(&this->actor); + } else { + switch (this->isBeached) { + case false: + // Fish is above water but hasn't touched land before + this->actor.gravity = -1.0f; + this->targetPosWithDeviation.y = this->waterSurfaceYPos - 50.0f; + this->workTimer[TANRON3_WORK_TIMER_OUT_OF_WATER] = 25; + Math_ApproachS(&this->actor.world.rot.x, 0x3000, 5, 0xBD0); + if (this->actor.bgCheckFlags & 8) { + this->actor.speedXZ = 0.0f; + if (this->actor.velocity.y > 0.0f) { + this->actor.velocity.y = -1.0f; + } + } + if (this->actor.bgCheckFlags & 1) { + // Fish has touched land + this->isBeached = true; + } + break; + case true: + this->nextRotationAngle = 0x3A98; + this->actor.gravity = -1.5f; + if (this->actor.bgCheckFlags & 1) { + // Fish is still touching land, so it's still beached. Randomly flop around + this->actor.velocity.y = Rand_ZeroFloat(5.0f) + 5.0f; + this->actor.speedXZ = Rand_ZeroFloat(2.0f) + 2.0f; + if (Rand_ZeroOne() < 0.5f) { + this->targetShapeRotation.x = + (s16)randPlusMinusPoint5Scaled(500.0f) + this->targetShapeRotation.x + 0x8000; + } + if (Rand_ZeroOne() < 0.5f) { + this->targetShapeRotation.z = + (s16)randPlusMinusPoint5Scaled(500.0f) + this->targetShapeRotation.z + 0x8000; + } + if (Rand_ZeroOne() < 0.5f) { + this->targetShapeRotation.y = (s16)Rand_ZeroFloat(0x10000); + } + this->actor.world.rot.y = Math_FAtan2F(this->actor.world.pos.z, this->actor.world.pos.x) + + (s16)randPlusMinusPoint5Scaled(0xCE20); + } + + Math_ApproachS(&this->actor.shape.rot.y, this->targetShapeRotation.y, 3, 0x500); + Math_ApproachS(&this->actor.shape.rot.x, this->targetShapeRotation.x, 3, 0xC00); + Math_ApproachS(&this->actor.shape.rot.z, this->targetShapeRotation.z, 3, 0xC00); + if ((Rand_ZeroOne() < 0.5f) & !(this->timer & 0x3)) { + Vec3f effectPos; + + effectPos.x = randPlusMinusPoint5Scaled(30.0f) + this->actor.world.pos.x; + effectPos.y = this->actor.world.pos.y; + effectPos.z = randPlusMinusPoint5Scaled(30.0f) + this->actor.world.pos.z; + EnTanron3_CreateEffect(globalCtx, &effectPos); + } + break; + } + Actor_SetVelocityAndMoveYRotationAndGravity(&this->actor); + } + + this->currentRotationAngle += this->nextRotationAngle; + this->trunkRotation = Math_SinS(this->currentRotationAngle) * 5000.0f; + this->bodyRotation = Math_SinS(this->currentRotationAngle + 0x6978) * 5000.0f; + this->tailRotation = Math_SinS(this->currentRotationAngle) * 5000.0f; + if (!this->isBeached) { + this->actor.shape.rot = this->actor.world.rot; + } +} + +void EnTanron3_SetupDie(EnTanron3* this, GlobalContext* globalCtx) { + f32 xDistance; + f32 yDistance; + f32 zDistance; + Player* player = GET_PLAYER(globalCtx); + + this->actionFunc = EnTanron3_Die; + xDistance = this->actor.world.pos.x - player->actor.world.pos.x; + yDistance = this->actor.world.pos.y - player->actor.world.pos.y + 30.0f; + zDistance = this->actor.world.pos.z - player->actor.world.pos.z; + this->actor.world.rot.x = Math_FAtan2F(sqrtf(SQ(xDistance) + SQ(zDistance)), -yDistance); + this->actor.world.rot.y = Math_FAtan2F(zDistance, xDistance); + this->workTimer[TANRON3_WORK_TIMER_DIE] = 6; + this->actor.speedXZ = 10.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KONB_MINI_DEAD); +} + +void EnTanron3_Die(EnTanron3* this, GlobalContext* globalCtx) { + Actor_SetVelocityAndMoveXYRotationReverse(&this->actor); + if (this->workTimer[TANRON3_WORK_TIMER_DIE] == 0) { + EnTanron3_SpawnBubbles(this, globalCtx); + Actor_MarkForDeath(&this->actor); + if (Rand_ZeroOne() < 0.3f) { + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0x60); + } + } +} + +void EnTanron3_CheckCollisions(EnTanron3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->actor.world.pos.y > 350.0f) { + if (this->atCollider.base.atFlags & AT_HIT) { + this->atCollider.base.atFlags &= ~AT_HIT; + func_800B8D50(globalCtx, NULL, 3.0f, Math_FAtan2F(-player->actor.world.pos.z, -player->actor.world.pos.x), + 5.0f, 0); + } + } + if (this->acCollider.base.acFlags & AC_HIT) { + this->acCollider.base.acFlags &= ~AC_HIT; + if (this->deathTimer == 0) { + this->deathTimer = 15; + this->fogTimer = 15; + EnTanron3_SetupDie(this, globalCtx); + sGyorg->unk_324 = 20; + } + } +} + +void EnTanron3_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnTanron3* this = THIS; + s16 i; + Vec3f splashPos; + + if (KREG(63) == 0) { + this->timer++; + + for (i = 0; i < TANRON3_WORK_TIMER_MAX; i++) { + if (this->workTimer[i] != 0) { + this->workTimer[i]--; + } + } + if (this->deathTimer != 0) { + this->deathTimer--; + } + if (this->fogTimer != 0) { + this->fogTimer--; + } + + this->actionFunc(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 20.0f, 5); + + // The fish has either just entered or just exited the water, so create a splash effect + if (((this->actor.prevPos.y < this->waterSurfaceYPos) && (this->waterSurfaceYPos <= this->actor.world.pos.y)) || + ((this->actor.prevPos.y > this->waterSurfaceYPos) && (this->waterSurfaceYPos >= this->actor.world.pos.y))) { + splashPos.x = this->actor.world.pos.x; + splashPos.y = this->waterSurfaceYPos + 10.0f; + splashPos.z = this->actor.world.pos.z; + EffectSsGSplash_Spawn(globalCtx, &splashPos, NULL, NULL, 1, 500); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_OUT_OF_WATER); + } + } + + EnTanron3_CheckCollisions(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->atCollider); + Collider_UpdateCylinder(&this->actor, &this->acCollider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->atCollider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->acCollider.base); + + if ((s8)sGyorg->actor.colChkInfo.health <= 0 && this->actionFunc != EnTanron3_Die) { + EnTanron3_SetupDie(this, globalCtx); + this->workTimer[TANRON3_WORK_TIMER_DIE] = 0; + } +} + +s32 EnTanron3_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + EnTanron3* this = THIS; + + if (limbIndex == 1) { + rot->y += this->bodyRotation; + } + if (limbIndex == 3) { + rot->y += this->tailRotation; + } + if (limbIndex == 4) { + rot->y += this->trunkRotation; + } + return false; +} + +void EnTanron3_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnTanron3* this = THIS; + + OPEN_DISPS(globalCtx->state.gfxCtx); + func_8012C28C(globalCtx->state.gfxCtx); + if ((this->fogTimer % 2) != 0) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099); + } + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnTanron3_OverrideLimbDraw, NULL, &this->actor); + POLY_OPA_DISP = func_801660B8(globalCtx, POLY_OPA_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.h b/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.h index 885d65af9d..9a2cb25da7 100644 --- a/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.h +++ b/src/overlays/actors/ovl_En_Tanron3/z_en_tanron3.h @@ -7,10 +7,53 @@ struct EnTanron3; typedef void (*EnTanron3ActionFunc)(struct EnTanron3*, GlobalContext*); +#define TANRON3_WORK_TIMER_PICK_NEW_DEVIATION 0 +#define TANRON3_WORK_TIMER_DIE 0 +#define TANRON3_WORK_TIMER_OUT_OF_WATER 1 +#define TANRON3_WORK_TIMER_ATTACK 2 +#define TANRON3_WORK_TIMER_WAIT 2 +#define TANRON3_WORK_TIMER_MAX 3 + +typedef struct { + /* 0x00 */ u8 type; + /* 0x02 */ s16 unk_02; + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ char unk_28[0xC]; + /* 0x34 */ Vec3f unk_34; + /* 0x40 */ char unk_40[0x4]; +} UnkTanron3Effect; + typedef struct EnTanron3 { - /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0x1B4]; - /* 0x02F8 */ EnTanron3ActionFunc actionFunc; + /* 0x000 */ Actor actor; + /* 0x144 */ SkelAnime skelAnime; + /* 0x188 */ Vec3s jointTable[10]; + /* 0x1C4 */ Vec3s morphTable[10]; + /* 0x200 */ s16 timer; + /* 0x202 */ u8 isNonHostile; // If true, the fish will not move towards the player to attack them + /* 0x203 */ u8 isBeached; // If true, the fish is on the central platform flopping around + /* 0x204 */ s16 workTimer[TANRON3_WORK_TIMER_MAX]; + /* 0x20A */ s16 deathTimer; + /* 0x20C */ s16 fogTimer; + /* 0x210 */ Vec3f targetPosWithDeviation; // The destination that the fish actually ends up moving towards + /* 0x21C */ Vec3f targetPos; // The exact destination where the fish wants to be located + /* 0x228 */ Vec3f deviation; // A random amount of "noise" added to targetPos + /* 0x234 */ s16 rotationStep; + /* 0x236 */ s16 targetRotationStep; + /* 0x238 */ s16 rotationScale; + /* 0x23C */ f32 targetSpeedXZ; + /* 0x240 */ f32 speedMaxStep; + /* 0x244 */ f32 waterSurfaceYPos; + /* 0x248 */ Vec3s targetShapeRotation; + /* 0x250 */ s32 currentRotationAngle; + /* 0x254 */ s32 nextRotationAngle; + /* 0x258 */ s16 tailRotation; + /* 0x25A */ s16 trunkRotation; + /* 0x25C */ s16 bodyRotation; + /* 0x260 */ ColliderCylinder atCollider; + /* 0x2AC */ ColliderCylinder acCollider; + /* 0x2F8 */ EnTanron3ActionFunc actionFunc; } EnTanron3; // size = 0x2FC extern const ActorInit En_Tanron3_InitVars; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index de56d79015..79a254d428 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -15360,17 +15360,17 @@ 0x80BB7800:("EnTanron2_Update",), 0x80BB7B90:("func_80BB7B90",), 0x80BB7C14:("EnTanron2_Draw",), - 0x80BB85A0:("func_80BB85A0",), + 0x80BB85A0:("EnTanron3_CreateEffect",), 0x80BB86BC:("EnTanron3_Init",), 0x80BB87B0:("EnTanron3_Destroy",), - 0x80BB87D4:("func_80BB87D4",), - 0x80BB897C:("func_80BB897C",), - 0x80BB8A48:("func_80BB8A48",), - 0x80BB91D4:("func_80BB91D4",), - 0x80BB9288:("func_80BB9288",), - 0x80BB9308:("func_80BB9308",), + 0x80BB87D4:("EnTanron3_SpawnBubbles",), + 0x80BB897C:("EnTanron3_SetupLive",), + 0x80BB8A48:("EnTanron3_Live",), + 0x80BB91D4:("EnTanron3_SetupDie",), + 0x80BB9288:("EnTanron3_Die",), + 0x80BB9308:("EnTanron3_CheckCollisions",), 0x80BB93EC:("EnTanron3_Update",), - 0x80BB95FC:("func_80BB95FC",), + 0x80BB95FC:("EnTanron3_OverrideLimbDraw",), 0x80BB9670:("EnTanron3_Draw",), 0x80BB98E0:("ObjChan_Init",), 0x80BB99F0:("ObjChan_Destroy",),