mirror of https://github.com/zeldaret/mm.git
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:
parent
70990bf43f
commit
038eb997d0
3
spec
3
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",),
|
||||
|
|
|
|||
Loading…
Reference in New Issue