En_Tanron3 (Gyorg's fishes) OK and somewhat documented (#376)

* Import data to C

* EnTanron3_Init OK

* EnTanron3_Destroy OK

* func_80BB897C OK

* func_80BB91D4 OK

* func_80BB9288 OK

* EnTanron3_Draw OK

* func_80BB95FC OK

* EnTanron3_Update OK

* func_80BB9308 OK

* func_80BB87D4 OK

* func_80BB85A0 OK

* func_80BB8A48 OK

* Update spec with compiled reloc

* Name most of the functions

* Name a few more things

* Merge animation system changes

* Name remaining unnamed variables

* Finish documentation

* Use modulo for fogTimer

* Move enum/structs to .h

* boss03Parent -> sGyorg

* D_80BB9720 -> sZeroVec

* Move initialization of effectPtr up

* Use 2 * M_PI

* Use MTXMODE_NEW

* 65536.0f -> 0x10000

* timer += 1 -> timer++

* Timer cleanup

* Remove unneccesary parentheses

* When performing bitwise operations on timer, use hex

* Lowercase "fish" in the comment at the top

* Respond to jpburnett's feedback

* Respond to Elliptic and Kenix's reviews

* Undo renaming the Gyorg var, but keep the comment

* Add a little bit more documentation

* Variable renames

* Remove a comment that no longer applies

* Document some swimming behavior

* PICK_DIRECTION -> PICK_NEW_DEVIATION

* Minor comment tweak

* Respond to Elliptic's review

* Remove timer inconsistency
This commit is contained in:
Tom Overton 2021-12-19 11:40:39 -08:00 committed by GitHub
parent 70990bf43f
commit 038eb997d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 470 additions and 36 deletions

3
spec
View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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",),