Shipwright/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c

769 lines
25 KiB
C

/*
* File: z_en_fish.c
* Overlay: ovl_En_Fish
* Description: Fish
*/
#include "z_en_fish.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "vt.h"
#define FLAGS 0
void EnFish_Init(Actor* thisx, GlobalContext* globalCtx);
void EnFish_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnFish_Update(Actor* thisx, GlobalContext* globalCtx);
void EnFish_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnFish_Respawning_SetupSlowDown(EnFish* this);
void EnFish_Respawning_SlowDown(EnFish* this, GlobalContext* globalCtx);
void EnFish_Respawning_SetupFollowChild(EnFish* this);
void EnFish_Respawning_FollowChild(EnFish* this, GlobalContext* globalCtx);
void EnFish_Respawning_SetupFleePlayer(EnFish* this);
void EnFish_Respawning_FleePlayer(EnFish* this, GlobalContext* globalCtx);
void EnFish_Respawning_SetupApproachPlayer(EnFish* this);
void EnFish_Respawning_ApproachPlayer(EnFish* this, GlobalContext* globalCtx);
void EnFish_Dropped_SetupFall(EnFish* this);
void EnFish_Dropped_Fall(EnFish* this, GlobalContext* globalCtx);
void EnFish_Dropped_SetupFlopOnGround(EnFish* this);
void EnFish_Dropped_FlopOnGround(EnFish* this, GlobalContext* globalCtx);
void EnFish_Dropped_SetupSwimAway(EnFish* this);
void EnFish_Dropped_SwimAway(EnFish* this, GlobalContext* globalCtx);
void EnFish_Unique_SetupSwimIdle(EnFish* this);
void EnFish_Unique_SwimIdle(EnFish* this, GlobalContext* globalCtx);
// Used in the cutscene functions
static Actor* D_80A17010 = NULL;
static f32 D_80A17014 = 0.0f;
static f32 D_80A17018 = 0.0f;
static ColliderJntSphElementInit sJntSphElementsInit[1] = {
{
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0xFFCFFFFF, 0x00, 0x00 },
TOUCH_NONE,
BUMP_NONE,
OCELEM_ON,
},
{ 0, { { 0, 0, 0 }, 5 }, 100 },
},
};
static ColliderJntSphInit sJntSphInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_NONE,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_1,
COLSHAPE_JNTSPH,
},
1,
sJntSphElementsInit,
};
const ActorInit En_Fish_InitVars = {
ACTOR_EN_FISH,
ACTORCAT_ITEMACTION,
FLAGS,
OBJECT_GAMEPLAY_KEEP,
sizeof(EnFish),
(ActorFunc)EnFish_Init,
(ActorFunc)EnFish_Destroy,
(ActorFunc)EnFish_Update,
(ActorFunc)EnFish_Draw,
NULL,
};
static InitChainEntry sInitChain[] = {
ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneForward, 900, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneScale, 40, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneDownward, 700, ICHAIN_STOP),
};
f32 EnFish_XZDistanceSquared(Vec3f* v1, Vec3f* v2) {
return SQ(v1->x - v2->x) + SQ(v1->z - v2->z);
}
void EnFish_SetInWaterAnimation(EnFish* this) {
Animation_Change(&this->skelAnime, &gFishInWaterAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gFishInWaterAnim),
ANIMMODE_LOOP_INTERP, 2.0f);
}
void EnFish_SetOutOfWaterAnimation(EnFish* this) {
Animation_Change(&this->skelAnime, &gFishOutOfWaterAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gFishOutOfWaterAnim),
ANIMMODE_LOOP_INTERP, 2.0f);
}
void EnFish_BeginRespawn(EnFish* this) {
this->respawnTimer = 400;
Actor_SetScale(&this->actor, 0.001f);
this->actor.draw = NULL;
}
void EnFish_SetCutsceneData(EnFish* this) {
Actor* thisx = &this->actor;
if (D_80A17010 == NULL) {
D_80A17010 = thisx;
Actor_SetScale(thisx, 0.01f);
thisx->draw = EnFish_Draw;
thisx->shape.rot.x = 0;
thisx->shape.rot.y = -0x6410;
thisx->shape.rot.z = 0x4000;
thisx->shape.yOffset = 600.0f;
D_80A17014 = 10.0f;
D_80A17018 = 0.0f;
thisx->flags |= ACTOR_FLAG_4;
EnFish_SetOutOfWaterAnimation(this);
}
}
void EnFish_ClearCutsceneData(EnFish* this) {
D_80A17010 = NULL;
D_80A17014 = 0.0f;
D_80A17018 = 0.0f;
}
void EnFish_Init(Actor* thisx, GlobalContext* globalCtx) {
EnFish* this = (EnFish*)thisx;
s16 params = this->actor.params;
Actor_ProcessInitChain(&this->actor, sInitChain);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishSkel, &gFishInWaterAnim, this->jointTable, this->morphTable,
7);
Collider_InitJntSph(globalCtx, &this->collider);
Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems);
this->actor.colChkInfo.mass = 50;
this->slowPhase = Rand_ZeroOne() * (0xFFFF + 0.5f);
this->fastPhase = Rand_ZeroOne() * (0xFFFF + 0.5f);
if (params == FISH_DROPPED) {
this->actor.flags |= ACTOR_FLAG_4;
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 8.0f);
EnFish_Dropped_SetupFall(this);
} else if (params == FISH_SWIMMING_UNIQUE) {
EnFish_Unique_SetupSwimIdle(this);
} else {
EnFish_Respawning_SetupSlowDown(this);
}
}
void EnFish_Destroy(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
EnFish* this = (EnFish*)thisx;
Collider_DestroyJntSph(globalCtx, &this->collider);
}
void EnFish_SetYOffset(EnFish* this) {
this->actor.shape.yOffset += (Math_SinS(this->slowPhase) * 10.0f + Math_SinS(this->fastPhase) * 5.0f);
this->actor.shape.yOffset = CLAMP(this->actor.shape.yOffset, -200.0f, 200.0f);
}
s32 EnFish_InBottleRange(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
Player* player = GET_PLAYER(globalCtx);
Vec3f sp1C;
if (this->actor.xzDistToPlayer < 32.0f) {
sp1C.x = (Math_SinS(this->actor.yawTowardsPlayer + 0x8000) * 16.0f) + player->actor.world.pos.x;
sp1C.y = player->actor.world.pos.y;
sp1C.z = (Math_CosS(this->actor.yawTowardsPlayer + 0x8000) * 16.0f) + player->actor.world.pos.z;
//! @bug: this check is superfluous: it is automatically satisfied if the coarse check is satisfied. It may have
//! been intended to check the actor is in front of Player, but yawTowardsPlayer does not depend on Player's
//! world rotation.
if (EnFish_XZDistanceSquared(&sp1C, &this->actor.world.pos) <= SQ(20.0f)) {
return true;
}
}
return false;
}
s32 EnFish_CheckXZDistanceToPlayer(EnFish* this, GlobalContext* globalCtx) {
return (this->actor.xzDistToPlayer < 60.0f);
}
// Respawning type functions
void EnFish_Respawning_SetupSlowDown(EnFish* this) {
this->actor.gravity = 0.0f;
this->actor.minVelocityY = 0.0f;
this->timer = Rand_S16Offset(5, 35);
this->unk_250 = 0;
EnFish_SetInWaterAnimation(this);
this->actionFunc = EnFish_Respawning_SlowDown;
}
void EnFish_Respawning_SlowDown(EnFish* this, GlobalContext* globalCtx) {
EnFish_SetYOffset(this);
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.05f, 0.3f, 0.0f);
this->skelAnime.playSpeed = CLAMP_MAX(this->actor.speedXZ * 1.4f + 0.8f, 2.0f);
SkelAnime_Update(&this->skelAnime);
this->actor.shape.rot.y = this->actor.world.rot.y;
if (this->timer <= 0) {
EnFish_Respawning_SetupFollowChild(this);
} else if (&this->actor == this->actor.child) {
EnFish_Respawning_SetupApproachPlayer(this);
} else if (EnFish_CheckXZDistanceToPlayer(this, globalCtx)) {
EnFish_Respawning_SetupFleePlayer(this);
}
}
// The three following actionfunctions also turn the yaw to home if the fish is too far from it.
void EnFish_Respawning_SetupFollowChild(EnFish* this) {
this->actor.gravity = 0.0f;
this->actor.minVelocityY = 0.0f;
this->timer = Rand_S16Offset(15, 45);
this->unk_250 = 0;
EnFish_SetInWaterAnimation(this);
this->actionFunc = EnFish_Respawning_FollowChild;
}
void EnFish_Respawning_FollowChild(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
EnFish_SetYOffset(this);
Math_SmoothStepToF(&this->actor.speedXZ, 1.8f, 0.08f, 0.4f, 0.0f);
if ((EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(80.0f)) || (this->timer < 4)) {
Math_StepToAngleS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos),
3000);
} else if ((this->actor.child != NULL) && (&this->actor != this->actor.child)) {
Math_StepToAngleS(&this->actor.world.rot.y,
Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.child->world.pos), 3000);
}
this->actor.shape.rot.y = this->actor.world.rot.y;
this->skelAnime.playSpeed = CLAMP_MAX(this->actor.speedXZ * 1.5f + 0.8f, 4.0f);
SkelAnime_Update(&this->skelAnime);
if (this->timer <= 0) {
EnFish_Respawning_SetupSlowDown(this);
} else if (&this->actor == this->actor.child) {
EnFish_Respawning_SetupApproachPlayer(this);
} else if (EnFish_CheckXZDistanceToPlayer(this, globalCtx)) {
EnFish_Respawning_SetupFleePlayer(this);
}
}
void EnFish_Respawning_SetupFleePlayer(EnFish* this) {
this->actor.gravity = 0.0f;
this->actor.minVelocityY = 0.0f;
this->timer = Rand_S16Offset(10, 40);
this->unk_250 = 0;
EnFish_SetInWaterAnimation(this);
this->actionFunc = EnFish_Respawning_FleePlayer;
}
void EnFish_Respawning_FleePlayer(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
s16 pad2;
s16 frames;
s16 yaw;
s16 playerClose;
EnFish_SetYOffset(this);
playerClose = EnFish_CheckXZDistanceToPlayer(this, globalCtx);
Math_SmoothStepToF(&this->actor.speedXZ, 4.2f, 0.08f, 1.4f, 0.0f);
if (EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(160.0f)) {
yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos);
Math_StepToAngleS(&this->actor.world.rot.y, yaw, 3000);
} else if ((this->actor.child != NULL) && (&this->actor != this->actor.child)) {
yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.child->world.pos);
Math_StepToAngleS(&this->actor.world.rot.y, yaw, 2000);
} else if (playerClose) {
yaw = this->actor.yawTowardsPlayer + 0x8000;
frames = globalCtx->state.frames;
if (frames & 0x10) {
if (frames & 0x20) {
yaw += 0x2000;
}
} else {
if (frames & 0x20) {
yaw -= 0x2000;
}
}
if (globalCtx) {}
Math_StepToAngleS(&this->actor.world.rot.y, yaw, 2000);
}
this->actor.shape.rot.y = this->actor.world.rot.y;
this->skelAnime.playSpeed = CLAMP_MAX(this->actor.speedXZ * 1.5f + 0.8f, 4.0f);
SkelAnime_Update(&this->skelAnime);
if ((this->timer <= 0) || !playerClose) {
EnFish_Respawning_SetupSlowDown(this);
} else if (&this->actor == this->actor.child) {
EnFish_Respawning_SetupApproachPlayer(this);
}
}
void EnFish_Respawning_SetupApproachPlayer(EnFish* this) {
this->actor.gravity = 0.0f;
this->actor.minVelocityY = 0.0f;
EnFish_SetInWaterAnimation(this);
this->timer = Rand_S16Offset(10, 40);
this->unk_250 = 0;
this->actionFunc = EnFish_Respawning_ApproachPlayer;
}
void EnFish_Respawning_ApproachPlayer(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
Player* player = GET_PLAYER(globalCtx);
s32 pad2;
Vec3f sp38;
s16 yaw;
s16 temp_a0_2;
EnFish_SetYOffset(this);
Math_SmoothStepToF(&this->actor.speedXZ, 1.8f, 0.1f, 0.5f, 0.0f);
if (EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(80.0f)) {
yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos);
Math_StepToAngleS(&this->actor.world.rot.y, yaw, 3000);
} else {
if ((s16)globalCtx->state.frames & 0x40) {
temp_a0_2 = (this->actor.yawTowardsPlayer + 0x9000);
} else {
temp_a0_2 = (this->actor.yawTowardsPlayer + 0x7000);
}
sp38.x = player->actor.world.pos.x + (Math_SinS(temp_a0_2) * 20.0f);
sp38.y = player->actor.world.pos.y;
sp38.z = player->actor.world.pos.z + (Math_CosS(temp_a0_2) * 20.0f);
yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &sp38);
Math_StepToAngleS(&this->actor.world.rot.y, yaw, 3000);
}
this->actor.shape.rot.y = this->actor.world.rot.y;
this->skelAnime.playSpeed = CLAMP_MAX((this->actor.speedXZ * 1.5f) + 0.8f, 4.0f);
SkelAnime_Update(&this->skelAnime);
if (this->timer <= 0) {
EnFish_Respawning_SetupSlowDown(this);
}
}
// Dropped type functions
void EnFish_Dropped_SetupFall(EnFish* this) {
this->actor.gravity = -1.0f;
this->actor.minVelocityY = -10.0f;
this->actor.shape.yOffset = 0.0f;
EnFish_SetOutOfWaterAnimation(this);
this->unk_250 = 5;
this->actionFunc = EnFish_Dropped_Fall;
this->timer = 300;
}
void EnFish_Dropped_Fall(EnFish* this, GlobalContext* globalCtx) {
Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 0.1f, 0.0f);
Math_StepToAngleS(&this->actor.world.rot.x, 0x4000, 100);
Math_StepToAngleS(&this->actor.world.rot.z, -0x4000, 100);
this->actor.shape.rot.x = this->actor.world.rot.x;
this->actor.shape.rot.y = this->actor.world.rot.y;
this->actor.shape.rot.z = this->actor.world.rot.z;
SkelAnime_Update(&this->skelAnime);
if (this->actor.bgCheckFlags & 1) { // On floor
this->timer = 400;
EnFish_Dropped_SetupFlopOnGround(this);
} else if (this->actor.bgCheckFlags & 0x20) { // In water
EnFish_Dropped_SetupSwimAway(this);
} else if ((this->timer <= 0) && (this->actor.params == FISH_DROPPED) &&
(this->actor.floorHeight < BGCHECK_Y_MIN + 10.0f)) {
osSyncPrintf(VT_COL(YELLOW, BLACK));
// "BG missing? Running Actor_delete"
osSyncPrintf("BG 抜け? Actor_delete します(%s %d)\n", __FILE__, __LINE__);
osSyncPrintf(VT_RST);
Actor_Kill(&this->actor);
}
}
/**
* If the fish is on a floor, this function is looped back to by EnFish_Dropped_FlopOnGround to set a new flopping
* height and whether the sound should play again.
*/
void EnFish_Dropped_SetupFlopOnGround(EnFish* this) {
s32 pad;
f32 randomFloat;
s32 playSound;
this->actor.gravity = -1.0f;
this->actor.minVelocityY = -10.0f;
randomFloat = Rand_ZeroOne();
if (randomFloat < 0.1f) {
this->actor.velocity.y = (Rand_ZeroOne() * 3.0f) + 2.5f;
playSound = true;
} else if (randomFloat < 0.2f) {
this->actor.velocity.y = (Rand_ZeroOne() * 1.2f) + 0.2f;
playSound = true;
} else {
this->actor.velocity.y = 0.0f;
if (Rand_ZeroOne() < 0.2f) {
playSound = true;
} else {
playSound = false;
}
}
this->actor.shape.yOffset = 300.0f;
EnFish_SetOutOfWaterAnimation(this);
this->actionFunc = EnFish_Dropped_FlopOnGround;
this->unk_250 = 5;
if (playSound && (this->actor.draw != NULL)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EV_FISH_LEAP);
}
}
void EnFish_Dropped_FlopOnGround(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
s16 frames = globalCtx->state.frames;
s16 targetXRot;
Math_SmoothStepToF(&this->actor.speedXZ, Rand_ZeroOne() * 0.2f, 0.1f, 0.1f, 0.0f);
targetXRot = (s16)((((frames >> 5) & 2) | ((frames >> 2) & 1)) << 0xB) * 0.3f;
if (frames & 4) {
targetXRot = -targetXRot;
}
Math_StepToAngleS(&this->actor.world.rot.x, targetXRot, 4000);
Math_StepToAngleS(&this->actor.world.rot.z, 0x4000, 1000);
this->actor.world.rot.y +=
(s16)(((Math_SinS(this->slowPhase) * 2000.0f) + (Math_SinS(this->fastPhase) * 1000.0f)) * Rand_ZeroOne());
this->actor.shape.rot = this->actor.world.rot;
SkelAnime_Update(&this->skelAnime);
if (this->timer <= 0) {
Actor_Kill(&this->actor);
return;
}
if (this->timer <= 60) {
// Blink when about to disappear
if (frames & 4) {
this->actor.draw = EnFish_Draw;
} else {
this->actor.draw = NULL;
}
} else if (this->actor.bgCheckFlags & 0x20) { // In water
EnFish_Dropped_SetupSwimAway(this);
} else if (this->actor.bgCheckFlags & 1) { // On floor
EnFish_Dropped_SetupFlopOnGround(this);
}
}
void EnFish_Dropped_SetupSwimAway(EnFish* this) {
this->actor.home.pos = this->actor.world.pos;
this->actor.flags |= ACTOR_FLAG_4;
this->timer = 200;
this->actor.gravity = 0.0f;
this->actor.minVelocityY = 0.0f;
this->actor.shape.yOffset = 0.0f;
EnFish_SetInWaterAnimation(this);
this->actionFunc = EnFish_Dropped_SwimAway;
this->unk_250 = 5;
}
void EnFish_Dropped_SwimAway(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
Math_SmoothStepToF(&this->actor.speedXZ, 2.8f, 0.1f, 0.4f, 0.0f);
// If touching wall or not in water, turn back and slow down for one frame.
if ((this->actor.bgCheckFlags & 8) || !(this->actor.bgCheckFlags & 0x20)) {
this->actor.home.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos);
this->actor.speedXZ *= 0.5f;
}
Math_StepToAngleS(&this->actor.world.rot.x, 0, 1500);
Math_StepToAngleS(&this->actor.world.rot.y, this->actor.home.rot.y, 3000);
Math_StepToAngleS(&this->actor.world.rot.z, 0, 1000);
this->actor.shape.rot = this->actor.world.rot;
// Raise if on a floor.
if (this->actor.bgCheckFlags & 1) {
Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 4.0f, 2.0f);
} else {
Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 10.0f, 2.0f);
}
// Shrink when close to disappearing.
if (this->timer < 100) {
Actor_SetScale(&this->actor, this->actor.scale.x * 0.982f);
}
this->skelAnime.playSpeed = CLAMP_MAX((this->actor.speedXZ * 1.5f) + 1.0f, 4.0f);
SkelAnime_Update(&this->skelAnime);
if (this->timer <= 0) {
Actor_Kill(&this->actor);
}
}
// Unique type functions
void EnFish_Unique_SetupSwimIdle(EnFish* this) {
this->actor.gravity = 0.0f;
this->actor.minVelocityY = 0.0f;
this->timer = Rand_S16Offset(5, 35);
this->unk_250 = 0;
EnFish_SetInWaterAnimation(this);
this->actionFunc = EnFish_Unique_SwimIdle;
}
void EnFish_Unique_SwimIdle(EnFish* this, GlobalContext* globalCtx) {
static f32 speedStopping[] = { 0.0f, 0.04f, 0.09f };
static f32 speedMoving[] = { 0.5f, 0.1f, 0.15f };
f32 playSpeed;
u32 frames = globalCtx->gameplayFrames;
f32* speed;
s32 pad2;
f32 extraPlaySpeed;
s32 pad3;
if (this->actor.xzDistToPlayer < 60.0f) {
if (this->timer < 12) {
speed = speedMoving;
} else {
speed = speedStopping;
}
} else {
if (this->timer < 4) {
speed = speedMoving;
} else {
speed = speedStopping;
}
}
EnFish_SetYOffset(this);
Math_SmoothStepToF(&this->actor.speedXZ, speed[0], speed[1], speed[2], 0.0f);
extraPlaySpeed = 0.0f;
if ((EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(15.0f))) {
if (!Math_ScaledStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos),
200)) {
extraPlaySpeed = 0.5f;
}
} else if ((this->timer < 4) && !Math_ScaledStepToS(&this->actor.world.rot.y, frames * 0x80, 100)) {
extraPlaySpeed = 0.5f;
}
this->actor.shape.rot.y = this->actor.world.rot.y;
playSpeed = (this->actor.speedXZ * 1.2f) + 0.2f + extraPlaySpeed;
this->skelAnime.playSpeed = CLAMP(playSpeed, 1.5f, 0.5);
SkelAnime_Update(&this->skelAnime);
if (this->timer <= 0) {
this->timer = Rand_S16Offset(5, 80);
}
}
// Cutscene functions
void EnFish_Cutscene_FlopOnGround(EnFish* this, GlobalContext* globalCtx) {
f32 sp24 = Math_SinS(this->slowPhase);
f32 sp20 = Math_SinS(this->fastPhase);
D_80A17014 += D_80A17018;
if (D_80A17014 <= 1.0f) {
D_80A17014 = 1.0f;
if (Rand_ZeroOne() < 0.1f) {
D_80A17018 = (Rand_ZeroOne() * 3.0f) + 2.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_FISH_LEAP);
} else {
D_80A17018 = 0.0f;
}
} else {
D_80A17018 -= 0.4f;
}
this->skelAnime.playSpeed = ((sp24 + sp20) * 0.5f) + 2.0f;
SkelAnime_Update(&this->skelAnime);
}
void EnFish_Cutscene_WiggleFlyingThroughAir(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
f32 sp28 = Math_SinS(this->slowPhase);
f32 sp24 = Math_SinS(this->fastPhase);
this->actor.shape.rot.x -= 500;
this->actor.shape.rot.z += 100;
Math_StepToF(&D_80A17014, 0.0f, 1.0f);
this->skelAnime.playSpeed = ((sp28 + sp24) * 0.5f) + 2.0f;
SkelAnime_Update(&this->skelAnime);
}
void EnFish_UpdateCutscene(EnFish* this, GlobalContext* globalCtx) {
s32 pad;
s32 pad2;
CsCmdActorAction* csAction = globalCtx->csCtx.npcActions[1];
Vec3f startPos;
Vec3f endPos;
f32 progress;
s32 bgId;
if (csAction == NULL) {
// "Warning : DEMO ended without dousa (action) 3 termination being called"
osSyncPrintf("Warning : dousa 3 消滅 が呼ばれずにデモが終了した(%s %d)(arg_data 0x%04x)\n", __FILE__,
__LINE__, this->actor.params);
EnFish_ClearCutsceneData(this);
Actor_Kill(&this->actor);
return;
}
this->slowPhase += 0x111;
this->fastPhase += 0x500;
switch (csAction->action) {
case 1:
EnFish_Cutscene_FlopOnGround(this, globalCtx);
break;
case 2:
EnFish_Cutscene_WiggleFlyingThroughAir(this, globalCtx);
break;
case 3:
// "DEMO fish termination"
osSyncPrintf("デモ魚消滅\n");
EnFish_ClearCutsceneData(this);
Actor_Kill(&this->actor);
return;
default:
// "Improper DEMO action"
osSyncPrintf("不正なデモ動作(%s %d)(arg_data 0x%04x)\n", __FILE__, __LINE__, this->actor.params);
break;
}
startPos.x = csAction->startPos.x;
startPos.y = csAction->startPos.y;
startPos.z = csAction->startPos.z;
endPos.x = csAction->endPos.x;
endPos.y = csAction->endPos.y;
endPos.z = csAction->endPos.z;
progress = Environment_LerpWeight(csAction->endFrame, csAction->startFrame, globalCtx->csCtx.frames);
this->actor.world.pos.x = (endPos.x - startPos.x) * progress + startPos.x;
this->actor.world.pos.y = (endPos.y - startPos.y) * progress + startPos.y + D_80A17014;
this->actor.world.pos.z = (endPos.z - startPos.z) * progress + startPos.z;
this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId,
&this->actor, &this->actor.world.pos);
}
// Update functions and Draw
void EnFish_OrdinaryUpdate(EnFish* this, GlobalContext* globalCtx) {
if (this->timer > 0) {
this->timer--;
}
this->slowPhase += 0x111;
this->fastPhase += 0x500;
if ((this->actor.child != NULL) && (this->actor.child->update == NULL) && (&this->actor != this->actor.child)) {
this->actor.child = NULL;
}
if ((this->actionFunc == NULL) || (this->actionFunc(this, globalCtx), (this->actor.update != NULL))) {
Actor_MoveForward(&this->actor);
if (this->unk_250 != 0) {
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 17.5f, 4.0f, 0.0f, this->unk_250);
}
if (this->actor.xzDistToPlayer < 70.0f) {
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
Actor_SetFocus(&this->actor, this->actor.shape.yOffset * 0.01f);
if (Actor_HasParent(&this->actor, globalCtx)) {
this->actor.parent = NULL;
if (this->actor.params == FISH_DROPPED) {
Actor_Kill(&this->actor);
return;
}
EnFish_BeginRespawn(this);
} else if (EnFish_InBottleRange(this, globalCtx)) {
// GI_MAX in this case allows the player to catch the actor in a bottle
func_8002F434(&this->actor, globalCtx, GI_MAX, 80.0f, 20.0f);
}
}
}
void EnFish_RespawningUpdate(EnFish* this, GlobalContext* globalCtx) {
if (this->actor.params == FISH_SWIMMING_UNIQUE) {
Actor_Kill(&this->actor);
return;
}
if ((this->actor.child != NULL) && (this->actor.child->update == NULL) && (&this->actor != this->actor.child)) {
this->actor.child = NULL;
}
if ((this->actionFunc == NULL) || (this->actionFunc(this, globalCtx), (this->actor.update != NULL))) {
Actor_MoveForward(&this->actor);
if (this->respawnTimer == 20) {
this->actor.draw = EnFish_Draw;
} else if (this->respawnTimer == 0) {
Actor_SetScale(&this->actor, 0.01f);
} else if (this->respawnTimer < 20) {
Actor_SetScale(&this->actor, CLAMP_MAX(this->actor.scale.x + 0.001f, 0.01f));
}
}
}
void EnFish_Update(Actor* thisx, GlobalContext* globalCtx) {
EnFish* this = (EnFish*)thisx;
if ((D_80A17010 == NULL) && (this->actor.params == FISH_DROPPED) && (globalCtx->csCtx.state != 0) &&
(globalCtx->csCtx.npcActions[1] != NULL)) {
EnFish_SetCutsceneData(this);
}
if ((D_80A17010 != NULL) && (&this->actor == D_80A17010)) {
EnFish_UpdateCutscene(this, globalCtx);
} else if (this->respawnTimer > 0) {
this->respawnTimer--;
EnFish_RespawningUpdate(this, globalCtx);
} else {
EnFish_OrdinaryUpdate(this, globalCtx);
}
}
void EnFish_Draw(Actor* thisx, GlobalContext* globalCtx) {
EnFish* this = (EnFish*)thisx;
func_80093D18(globalCtx->state.gfxCtx);
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, NULL);
Collider_UpdateSpheres(0, &this->collider);
}