mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-08-13 17:03:47 -04:00
39cc86c260
subrepo: subdir: "soh" merged: "ba904bbd0" upstream: origin: "https://github.com/HarbourMasters/soh.git" branch: "master" commit: "ba904bbd0" git-subrepo: version: "0.4.1" origin: "???" commit: "???"
769 lines
26 KiB
C
769 lines
26 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", "../z_en_sakana.c", 822);
|
|
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", "../z_en_sakana.c",
|
|
1169, 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", "../z_en_sakana.c", 1200, 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);
|
|
}
|