Shipwright/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.c

758 lines
29 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* File: z_en_goroiwa.c
* Overlay: ovl_En_Goroiwa
* Description: Rolling boulders
*/
#include "z_en_goroiwa.h"
#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_goroiwa/object_goroiwa.h"
#include "vt.h"
#define FLAGS ACTOR_FLAG_4
typedef s32 (*EnGoroiwaUnkFunc1)(EnGoroiwa* this, GlobalContext* globalCtx);
typedef void (*EnGoroiwaUnkFunc2)(EnGoroiwa* this);
#define ENGOROIWA_ENABLE_AT (1 << 0)
#define ENGOROIWA_ENABLE_OC (1 << 1)
#define ENGOROIWA_PLAYER_IN_THE_WAY (1 << 2)
#define ENGOROIWA_RETAIN_ROT_SPEED (1 << 3)
#define ENGOROIWA_IN_WATER (1 << 4)
#define ENGOROIWA_LOOPMODE_ONEWAY 0
/* same as ENGOROIWA_LOOPMODE_ONEWAY but display rock fragments as if the boulder broke at the end of the path*/
#define ENGOROIWA_LOOPMODE_ONEWAY_BREAK 1
#define ENGOROIWA_LOOPMODE_ROUNDTRIP 3
void EnGoroiwa_Init(Actor* thisx, GlobalContext* globalCtx);
void EnGoroiwa_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnGoroiwa_Update(Actor* thisx, GlobalContext* globalCtx);
void EnGoroiwa_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnGoroiwa_SetupRoll(EnGoroiwa* this);
void EnGoroiwa_Roll(EnGoroiwa* this, GlobalContext* globalCtx);
void EnGoroiwa_SetupMoveAndFallToGround(EnGoroiwa* this);
void EnGoroiwa_MoveAndFallToGround(EnGoroiwa* this, GlobalContext* globalCtx);
void EnGoroiwa_SetupWait(EnGoroiwa* this);
void EnGoroiwa_Wait(EnGoroiwa* this, GlobalContext* globalCtx);
void EnGoroiwa_SetupMoveUp(EnGoroiwa* this);
void EnGoroiwa_MoveUp(EnGoroiwa* this, GlobalContext* globalCtx);
void EnGoroiwa_SetupMoveDown(EnGoroiwa* this);
void EnGoroiwa_MoveDown(EnGoroiwa* this, GlobalContext* globalCtx);
const ActorInit En_Goroiwa_InitVars = {
ACTOR_EN_GOROIWA,
ACTORCAT_PROP,
FLAGS,
OBJECT_GOROIWA,
sizeof(EnGoroiwa),
(ActorFunc)EnGoroiwa_Init,
(ActorFunc)EnGoroiwa_Destroy,
(ActorFunc)EnGoroiwa_Update,
(ActorFunc)EnGoroiwa_Draw,
NULL,
};
static ColliderJntSphElementInit sJntSphElementsInit[] = {
{
{
ELEMTYPE_UNK0,
{ 0x20000000, 0x00, 0x04 },
{ 0x00000000, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_NONE,
OCELEM_ON,
},
{ 0, { { 0, 0, 0 }, 58 }, 100 },
},
};
static ColliderJntSphInit sJntSphInit = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ENEMY,
AC_NONE,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_2,
COLSHAPE_JNTSPH,
},
1,
sJntSphElementsInit,
};
static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_HEAVY };
static f32 sUnused[] = { 10.0f, 9.2f };
void EnGoroiwa_UpdateCollider(EnGoroiwa* this) {
static f32 yOffsets[] = { 0.0f, 59.5f };
Sphere16* worldSphere = &this->collider.elements[0].dim.worldSphere;
worldSphere->center.x = this->actor.world.pos.x;
worldSphere->center.y = this->actor.world.pos.y + yOffsets[(this->actor.params >> 10) & 1];
worldSphere->center.z = this->actor.world.pos.z;
}
void EnGoroiwa_InitCollider(EnGoroiwa* this, GlobalContext* globalCtx) {
s32 pad;
Collider_InitJntSph(globalCtx, &this->collider);
Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems);
EnGoroiwa_UpdateCollider(this);
this->collider.elements[0].dim.worldSphere.radius = 58;
}
void EnGoroiwa_UpdateFlags(EnGoroiwa* this, u8 setFlags) {
this->stateFlags &= ~(ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC);
this->stateFlags |= setFlags;
}
s32 EnGoroiwa_Vec3fNormalize(Vec3f* ret, Vec3f* a) {
f32 magnitude = Math3D_Vec3fMagnitude(a);
f32 scale;
if (magnitude < 0.001f) {
return false;
}
scale = 1.0f / magnitude;
ret->x = a->x * scale;
ret->y = a->y * scale;
ret->z = a->z * scale;
return true;
}
void EnGoroiwa_SetSpeed(EnGoroiwa* this, GlobalContext* globalCtx) {
if (globalCtx->sceneNum == SCENE_SPOT04) {
this->isInKokiri = true;
R_EN_GOROIWA_SPEED = 920;
} else {
this->isInKokiri = false;
R_EN_GOROIWA_SPEED = 1000;
}
}
void EnGoroiwa_FaceNextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) {
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
Vec3s* nextPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint;
Vec3f nextPosF;
nextPosF.x = nextPos->x;
nextPosF.y = nextPos->y;
nextPosF.z = nextPos->z;
this->actor.world.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &nextPosF);
}
void EnGoroiwa_GetPrevWaypointDiff(EnGoroiwa* this, GlobalContext* globalCtx, Vec3f* dest) {
s16 loopMode = (this->actor.params >> 8) & 3;
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
s16 prevWaypoint = this->currentWaypoint - this->pathDirection;
Vec3s* prevPointPos;
Vec3s* currentPointPos;
if (prevWaypoint < 0) {
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) {
prevWaypoint = this->endWaypoint;
} else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) {
prevWaypoint = 1;
}
} else if (prevWaypoint > this->endWaypoint) {
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) {
prevWaypoint = 0;
} else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) {
prevWaypoint = this->endWaypoint - 1;
}
}
currentPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->currentWaypoint;
prevPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + prevWaypoint;
dest->x = currentPointPos->x - prevPointPos->x;
dest->y = currentPointPos->x - prevPointPos->y;
dest->z = currentPointPos->x - prevPointPos->z;
}
void EnGoroiw_CheckEndOfPath(EnGoroiwa* this) {
s16 loopMode = (this->actor.params >> 8) & 3;
if (this->nextWaypoint < 0) {
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) {
this->currentWaypoint = this->endWaypoint;
this->nextWaypoint = this->endWaypoint - 1;
this->pathDirection = -1;
} else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) {
this->currentWaypoint = 0;
this->nextWaypoint = 1;
this->pathDirection = 1;
}
} else if (this->nextWaypoint > this->endWaypoint) {
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) {
this->currentWaypoint = 0;
this->nextWaypoint = 1;
this->pathDirection = 1;
} else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) {
this->currentWaypoint = this->endWaypoint;
this->nextWaypoint = this->endWaypoint - 1;
this->pathDirection = -1;
}
}
}
void EnGoroiwa_SetNextWaypoint(EnGoroiwa* this) {
this->currentWaypoint = this->nextWaypoint;
this->nextWaypoint += this->pathDirection;
EnGoroiw_CheckEndOfPath(this);
}
void EnGoroiwa_ReverseDirection(EnGoroiwa* this) {
this->pathDirection *= -1;
this->currentWaypoint = this->nextWaypoint;
this->nextWaypoint += this->pathDirection;
}
void EnGoroiwa_InitPath(EnGoroiwa* this, GlobalContext* globalCtx) {
this->endWaypoint = globalCtx->setupPathList [this->actor.params & 0xFF].count - 1;
this->currentWaypoint = 0;
this->nextWaypoint = 1;
this->pathDirection = 1;
}
void EnGoroiwa_TeleportToWaypoint(EnGoroiwa* this, GlobalContext* globalCtx, s32 waypoint) {
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
Vec3s* pointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + waypoint;
this->actor.world.pos.x = pointPos->x;
this->actor.world.pos.y = pointPos->y;
this->actor.world.pos.z = pointPos->z;
}
void EnGoroiwa_InitRotation(EnGoroiwa* this) {
this->prevUnitRollAxis.x = 1.0f;
this->rollRotSpeed = 1.0f;
}
s32 EnGoroiwa_GetAscendDirection(EnGoroiwa* this, GlobalContext* globalCtx) {
s32 pad;
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint;
Vec3s* currentPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->currentWaypoint;
if (nextPointPos->x == currentPointPos->x && nextPointPos->z == currentPointPos->z) {
if (nextPointPos->y == currentPointPos->y) {
// "Error: Invalid path data (points overlap)"
osSyncPrintf("Error : レールデータ不正(点が重なっている)");
osSyncPrintf("(%s %d)(arg_data 0x%04x)\n", __FILE__, __LINE__, this->actor.params);
}
if (nextPointPos->y > currentPointPos->y) {
return 1;
} else {
return -1;
}
}
return 0;
}
void EnGoroiwa_SpawnDust(GlobalContext* globalCtx, Vec3f* pos) {
static Vec3f velocity = { 0.0f, 0.0f, 0.0f };
static Vec3f accel = { 0.0f, 0.3f, 0.0f };
Vec3f randPos;
s32 i;
s16 angle = 0;
for (i = 0; i < 8; i++) {
angle += 0x4E20;
randPos.x = pos->x + (47.0f * (Rand_ZeroOne() * 0.5f + 0.5f)) * Math_SinS(angle);
randPos.y = pos->y + (Rand_ZeroOne() - 0.5f) * 40.0f;
randPos.z = pos->z + ((47.0f * (Rand_ZeroOne() * 0.5f + 0.5f))) * Math_CosS(angle);
func_800286CC(globalCtx, &randPos, &velocity, &accel, (s16)(Rand_ZeroOne() * 30.0f) + 100, 80);
func_800286CC(globalCtx, &randPos, &velocity, &accel, (s16)(Rand_ZeroOne() * 20.0f) + 80, 80);
}
}
void EnGoroiwa_SpawnWaterEffects(GlobalContext* globalCtx, Vec3f* contactPos) {
Vec3f splashPos;
s32 i;
s16 angle = 0;
for (i = 0; i < 11; i++) {
angle += 0x1746;
splashPos.x = contactPos->x + (Math_SinS(angle) * 55.0f);
splashPos.y = contactPos->y;
splashPos.z = contactPos->z + (Math_CosS(angle) * 55.0f);
EffectSsGSplash_Spawn(globalCtx, &splashPos, 0, 0, 0, 350);
}
EffectSsGRipple_Spawn(globalCtx, contactPos, 300, 700, 0);
EffectSsGRipple_Spawn(globalCtx, contactPos, 500, 900, 4);
EffectSsGRipple_Spawn(globalCtx, contactPos, 500, 1300, 8);
}
s32 EnGoroiwa_MoveAndFall(EnGoroiwa* this, GlobalContext* globalCtx) {
Path* path;
s32 result;
s32 pad;
Vec3s* nextPointPos;
Math_StepToF(&this->actor.speedXZ, R_EN_GOROIWA_SPEED * 0.01f, 0.3f);
func_8002D868(&this->actor);
path = &globalCtx->setupPathList[this->actor.params & 0xFF];
nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint;
result = true;
result &= Math_StepToF(&this->actor.world.pos.x, nextPointPos->x, fabsf(this->actor.velocity.x));
result &= Math_StepToF(&this->actor.world.pos.z, nextPointPos->z, fabsf(this->actor.velocity.z));
this->actor.world.pos.y += this->actor.velocity.y;
return result;
}
s32 EnGoroiwa_Move(EnGoroiwa* this, GlobalContext* globalCtx) {
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
s32 pad;
Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint;
Vec3s* currentPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->currentWaypoint;
s32 nextPointReached;
Vec3f posDiff;
Vec3f nextPointPosF;
nextPointPosF.x = nextPointPos->x;
nextPointPosF.y = nextPointPos->y;
nextPointPosF.z = nextPointPos->z;
Math_StepToF(&this->actor.speedXZ, R_EN_GOROIWA_SPEED * 0.01f, 0.3f);
if (Math3D_Vec3fDistSq(&nextPointPosF, &this->actor.world.pos) < SQ(5.0f)) {
Math_Vec3f_Diff(&nextPointPosF, &this->actor.world.pos, &posDiff);
} else {
posDiff.x = nextPointPosF.x - currentPointPos->x;
posDiff.y = nextPointPosF.y - currentPointPos->y;
posDiff.z = nextPointPosF.z - currentPointPos->z;
}
EnGoroiwa_Vec3fNormalize(&this->actor.velocity, &posDiff);
this->actor.velocity.x *= this->actor.speedXZ;
this->actor.velocity.y *= this->actor.speedXZ;
this->actor.velocity.z *= this->actor.speedXZ;
nextPointReached = true;
nextPointReached &= Math_StepToF(&this->actor.world.pos.x, nextPointPosF.x, fabsf(this->actor.velocity.x));
nextPointReached &= Math_StepToF(&this->actor.world.pos.y, nextPointPosF.y, fabsf(this->actor.velocity.y));
nextPointReached &= Math_StepToF(&this->actor.world.pos.z, nextPointPosF.z, fabsf(this->actor.velocity.z));
return nextPointReached;
}
s32 EnGoroiwa_MoveUpToNextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) {
s32 pad;
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint;
Math_StepToF(&this->actor.velocity.y, (R_EN_GOROIWA_SPEED * 0.01f) * 0.5f, 0.18f);
this->actor.world.pos.x = nextPointPos->x;
this->actor.world.pos.z = nextPointPos->z;
return Math_StepToF(&this->actor.world.pos.y, nextPointPos->y, fabsf(this->actor.velocity.y));
}
s32 EnGoroiwa_MoveDownToNextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) {
s32 pad;
Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF];
Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint;
f32 nextPointY;
f32 thisY;
f32 yDistToFloor;
s32 quakeIdx;
CollisionPoly* floorPoly;
Vec3f raycastFrom;
f32 floorY;
s32 pad2;
s32 floorBgId;
Vec3f dustPos;
WaterBox* waterBox;
f32 ySurface;
Vec3f waterHitPos;
nextPointY = nextPointPos->y;
Math_StepToF(&this->actor.velocity.y, -14.0f, 1.0f);
this->actor.world.pos.x = nextPointPos->x;
this->actor.world.pos.z = nextPointPos->z;
thisY = this->actor.world.pos.y;
this->actor.world.pos.y += this->actor.velocity.y;
if (this->actor.velocity.y < 0.0f && this->actor.world.pos.y <= nextPointY) {
if (this->bounceCount == 0) {
if (this->actor.xzDistToPlayer < 600.0f) {
quakeIdx = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3);
Quake_SetSpeed(quakeIdx, -0x3CB0);
Quake_SetQuakeValues(quakeIdx, 3, 0, 0, 0);
Quake_SetCountdown(quakeIdx, 7);
}
this->rollRotSpeed = 0.0f;
if (!(this->stateFlags & ENGOROIWA_IN_WATER)) {
raycastFrom.x = this->actor.world.pos.x;
raycastFrom.y = this->actor.world.pos.y + 50.0f;
raycastFrom.z = this->actor.world.pos.z;
floorY = BgCheck_EntityRaycastFloor5(globalCtx, &globalCtx->colCtx, &floorPoly, &floorBgId,
&this->actor, &raycastFrom);
yDistToFloor = floorY - (this->actor.world.pos.y - 59.5f);
if (fabsf(yDistToFloor) < 15.0f) {
dustPos.x = this->actor.world.pos.x;
dustPos.y = floorY + 10.0f;
dustPos.z = this->actor.world.pos.z;
EnGoroiwa_SpawnDust(globalCtx, &dustPos);
}
}
}
if (this->bounceCount >= 1) {
return true;
}
this->bounceCount++;
this->actor.velocity.y *= -0.3f;
this->actor.world.pos.y = nextPointY - ((this->actor.world.pos.y - nextPointY) * 0.3f);
}
if (this->bounceCount == 0 &&
WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z,
&ySurface, &waterBox) &&
this->actor.world.pos.y <= ySurface) {
this->stateFlags |= ENGOROIWA_IN_WATER;
if (ySurface < thisY) {
waterHitPos.x = this->actor.world.pos.x;
waterHitPos.y = ySurface;
waterHitPos.z = this->actor.world.pos.z;
EnGoroiwa_SpawnWaterEffects(globalCtx, &waterHitPos);
this->actor.velocity.y *= 0.2f;
}
if (this->actor.velocity.y < -8.0f) {
this->actor.velocity.y = -8.0f;
}
}
return false;
}
void EnGoroiwa_UpdateRotation(EnGoroiwa* this, GlobalContext* globalCtx) {
static Vec3f unitY = { 0.0f, 1.0f, 0.0f };
s32 pad;
Vec3f* rollAxisPtr;
f32 rollAngleDiff;
Vec3f rollAxis;
Vec3f unitRollAxis;
MtxF mtx;
Vec3f unusedDiff;
if (this->stateFlags & ENGOROIWA_RETAIN_ROT_SPEED) {
rollAngleDiff = this->prevRollAngleDiff;
} else {
this->prevRollAngleDiff = Math3D_Vec3f_DistXYZ(&this->actor.world.pos, &this->actor.prevPos) * (1.0f / 59.5f);
rollAngleDiff = this->prevRollAngleDiff;
}
rollAngleDiff *= this->rollRotSpeed;
rollAxisPtr = &rollAxis;
if (this->stateFlags & ENGOROIWA_RETAIN_ROT_SPEED) {
/*
* EnGoroiwa_GetPrevWaypointDiff has no side effects and its result goes unused,
* its result was probably meant to be used instead of the actor's velocity in the
* Math3D_Vec3f_Cross call.
*/
EnGoroiwa_GetPrevWaypointDiff(this, globalCtx, &unusedDiff);
Math3D_Vec3f_Cross(&unitY, &this->actor.velocity, rollAxisPtr);
} else {
Math3D_Vec3f_Cross(&unitY, &this->actor.velocity, rollAxisPtr);
}
if (EnGoroiwa_Vec3fNormalize(&unitRollAxis, rollAxisPtr)) {
this->prevUnitRollAxis = unitRollAxis;
} else {
unitRollAxis = this->prevUnitRollAxis;
}
Matrix_RotateAxis(rollAngleDiff, &unitRollAxis, MTXMODE_NEW);
Matrix_RotateY(this->actor.shape.rot.y * (2.0f * M_PI / 0x10000), MTXMODE_APPLY);
Matrix_RotateX(this->actor.shape.rot.x * (2.0f * M_PI / 0x10000), MTXMODE_APPLY);
Matrix_RotateZ(this->actor.shape.rot.z * (2.0f * M_PI / 0x10000), MTXMODE_APPLY);
Matrix_Get(&mtx);
Matrix_MtxFToYXZRotS(&mtx, &this->actor.shape.rot, 0);
}
void EnGoroiwa_NextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) {
s16 loopMode = (this->actor.params >> 8) & 3;
EnGoroiwa_SetNextWaypoint(this);
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) {
if (this->currentWaypoint == 0 || this->currentWaypoint == this->endWaypoint) {
EnGoroiwa_TeleportToWaypoint(this, globalCtx, this->currentWaypoint);
}
}
EnGoroiwa_FaceNextWaypoint(this, globalCtx);
}
void EnGoroiwa_SpawnFragments(EnGoroiwa* this, GlobalContext* globalCtx) {
static f32 yOffsets[] = { 0.0f, 59.5f };
s16 angle1;
s16 angle2;
s32 pad;
Vec3f* thisPos = &this->actor.world.pos;
Vec3f effectPos;
Vec3f fragmentVelocity;
f32 cos1;
f32 sin1;
f32 sin2;
s16 yOffsetIdx = (this->actor.params >> 10) & 1;
s32 i;
for (i = 0, angle1 = 0; i < 16; i++, angle1 += 0x4E20) {
sin1 = Math_SinS(angle1);
cos1 = Math_CosS(angle1);
angle2 = Rand_ZeroOne() * 0xFFFF;
effectPos.x = Rand_ZeroOne() * 50.0f * sin1 * Math_SinS(angle2);
sin2 = Math_SinS(angle2);
effectPos.y = (Rand_ZeroOne() - 0.5f) * 100.0f * sin2 + yOffsets[yOffsetIdx];
effectPos.z = Rand_ZeroOne() * 50.0f * cos1 * Math_SinS(angle2);
fragmentVelocity.x = effectPos.x * 0.2f;
fragmentVelocity.y = Rand_ZeroOne() * 15.0f + 2.0f;
fragmentVelocity.z = effectPos.z * 0.2f;
Math_Vec3f_Sum(&effectPos, thisPos, &effectPos);
EffectSsKakera_Spawn(globalCtx, &effectPos, &fragmentVelocity, &effectPos, -340, 33, 28, 2, 0,
Rand_ZeroOne() * 7.0f + 1.0f, 1, 0, 70, KAKERA_COLOR_NONE, 1, gBoulderFragmentsDL);
}
effectPos.x = thisPos->x;
effectPos.y = thisPos->y + yOffsets[yOffsetIdx];
effectPos.z = thisPos->z;
func_80033480(globalCtx, &effectPos, 80.0f, 5, 70, 110, 1);
func_80033480(globalCtx, &effectPos, 90.0f, 5, 110, 160, 1);
}
static InitChainEntry sInitChain[] = {
ICHAIN_F32_DIV1000(gravity, -860, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(minVelocityY, -15000, ICHAIN_CONTINUE),
ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneForward, 1500, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneScale, 150, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP),
};
void EnGoroiwa_Init(Actor* thisx, GlobalContext* globalCtx) {
static f32 yOffsets[] = { 0.0f, 595.0f };
EnGoroiwa* this = (EnGoroiwa*)thisx;
s32 pathIdx;
Actor_ProcessInitChain(&this->actor, sInitChain);
EnGoroiwa_InitCollider(this, globalCtx);
pathIdx = this->actor.params & 0xFF;
if (pathIdx == 0xFF) {
// "Error: Invalid arg_data"
osSyncPrintf(" : arg_data が不正(%s %d)(arg_data 0x%04x)\n", __FILE__, __LINE__,
this->actor.params);
Actor_Kill(&this->actor);
return;
}
if (globalCtx->setupPathList[pathIdx].count < 2) {
// "Error: Invalid Path Data"
osSyncPrintf(" : レールデータ が不正(%s %d)\n", __FILE__, __LINE__);
Actor_Kill(&this->actor);
return;
}
CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit);
ActorShape_Init(&this->actor.shape, yOffsets[(this->actor.params >> 10) & 1], ActorShadow_DrawCircle, 9.4f);
this->actor.shape.shadowAlpha = 200;
EnGoroiwa_SetSpeed(this, globalCtx);
EnGoroiwa_InitPath(this, globalCtx);
EnGoroiwa_TeleportToWaypoint(this, globalCtx, 0);
EnGoroiwa_InitRotation(this);
EnGoroiwa_FaceNextWaypoint(this, globalCtx);
EnGoroiwa_SetupRoll(this);
// "(Goroiwa)"
osSyncPrintf("(ごろ岩)(arg 0x%04x)(rail %d)(end %d)(bgc %d)(hit %d)\n", this->actor.params,
this->actor.params & 0xFF, (this->actor.params >> 8) & 3, (this->actor.params >> 10) & 1,
this->actor.home.rot.z & 1);
}
void EnGoroiwa_Destroy(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
EnGoroiwa* this = (EnGoroiwa*)thisx;
Collider_DestroyJntSph(globalCtx, &this->collider);
}
void EnGoroiwa_SetupRoll(EnGoroiwa* this) {
this->actionFunc = EnGoroiwa_Roll;
EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC);
this->rollRotSpeed = 1.0f;
}
void EnGoroiwa_Roll(EnGoroiwa* this, GlobalContext* globalCtx) {
static EnGoroiwaUnkFunc1 moveFuncs[] = { EnGoroiwa_Move, EnGoroiwa_MoveAndFall };
static EnGoroiwaUnkFunc2 onHitSetupFuncs[] = { EnGoroiwa_SetupWait, EnGoroiwa_SetupMoveAndFallToGround };
s32 ascendDirection;
s16 yawDiff;
s16 loopMode;
if (this->collider.base.atFlags & AT_HIT) {
this->collider.base.atFlags &= ~AT_HIT;
this->stateFlags &= ~ENGOROIWA_PLAYER_IN_THE_WAY;
yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y;
if (yawDiff > -0x4000 && yawDiff < 0x4000) {
this->stateFlags |= ENGOROIWA_PLAYER_IN_THE_WAY;
if (((this->actor.params >> 10) & 1) || (this->actor.home.rot.z & 1) != 1) {
EnGoroiwa_ReverseDirection(this);
EnGoroiwa_FaceNextWaypoint(this, globalCtx);
}
}
func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 0);
osSyncPrintf(VT_FGCOL(CYAN));
osSyncPrintf("Player ぶっ飛ばし\n"); // "Player knocked down"
osSyncPrintf(VT_RST);
onHitSetupFuncs[(this->actor.params >> 10) & 1](this);
func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT);
if ((this->actor.home.rot.z & 1) == 1) {
this->collisionDisabledTimer = 50;
}
} else if (moveFuncs[(this->actor.params >> 10) & 1](this, globalCtx)) {
loopMode = (this->actor.params >> 8) & 3;
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK &&
(this->nextWaypoint == 0 || this->nextWaypoint == this->endWaypoint)) {
EnGoroiwa_SpawnFragments(this, globalCtx);
}
EnGoroiwa_NextWaypoint(this, globalCtx);
if ((loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) &&
(this->currentWaypoint == 0 || this->currentWaypoint == this->endWaypoint)) {
EnGoroiwa_SetupWait(this);
} else if (!((this->actor.params >> 10) & 1) && this->currentWaypoint != 0 &&
this->currentWaypoint != this->endWaypoint) {
ascendDirection = EnGoroiwa_GetAscendDirection(this, globalCtx);
if (ascendDirection > 0) {
EnGoroiwa_SetupMoveUp(this);
} else if (ascendDirection < 0) {
EnGoroiwa_SetupMoveDown(this);
} else {
EnGoroiwa_SetupRoll(this);
}
} else {
EnGoroiwa_SetupRoll(this);
}
}
Audio_PlayActorSound2(&this->actor, NA_SE_EV_BIGBALL_ROLL - SFX_FLAG);
}
void EnGoroiwa_SetupMoveAndFallToGround(EnGoroiwa* this) {
this->actionFunc = EnGoroiwa_MoveAndFallToGround;
EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_OC);
this->actor.gravity = -0.86f;
this->actor.minVelocityY = -15.0f;
this->actor.speedXZ *= 0.15f;
this->actor.velocity.y = 5.0f;
this->rollRotSpeed = 1.0f;
}
void EnGoroiwa_MoveAndFallToGround(EnGoroiwa* this, GlobalContext* globalCtx) {
EnGoroiwa_MoveAndFall(this, globalCtx);
if ((this->actor.bgCheckFlags & 1) && this->actor.velocity.y < 0.0f) {
if ((this->stateFlags & ENGOROIWA_PLAYER_IN_THE_WAY) && (this->actor.home.rot.z & 1) == 1) {
EnGoroiwa_ReverseDirection(this);
EnGoroiwa_FaceNextWaypoint(this, globalCtx);
}
EnGoroiwa_SetupWait(this);
}
}
void EnGoroiwa_SetupWait(EnGoroiwa* this) {
static s16 waitDurations[] = { 20, 6 };
this->actionFunc = EnGoroiwa_Wait;
this->actor.speedXZ = 0.0f;
EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_OC);
this->waitTimer = waitDurations[this->actor.home.rot.z & 1];
this->rollRotSpeed = 0.0f;
}
void EnGoroiwa_Wait(EnGoroiwa* this, GlobalContext* globalCtx) {
if (this->waitTimer > 0) {
this->waitTimer--;
} else {
this->collider.base.atFlags &= ~AT_HIT;
EnGoroiwa_SetupRoll(this);
}
}
void EnGoroiwa_SetupMoveUp(EnGoroiwa* this) {
this->actionFunc = EnGoroiwa_MoveUp;
EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC);
this->rollRotSpeed = 0.0f;
this->actor.velocity.y = fabsf(this->actor.speedXZ) * 0.1f;
}
void EnGoroiwa_MoveUp(EnGoroiwa* this, GlobalContext* globalCtx) {
if (this->collider.base.atFlags & AT_HIT) {
this->collider.base.atFlags &= ~AT_HIT;
func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 4);
func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT);
if ((this->actor.home.rot.z & 1) == 1) {
this->collisionDisabledTimer = 50;
}
} else if (EnGoroiwa_MoveUpToNextWaypoint(this, globalCtx)) {
EnGoroiwa_NextWaypoint(this, globalCtx);
EnGoroiwa_SetupRoll(this);
this->actor.speedXZ = 0.0f;
}
}
void EnGoroiwa_SetupMoveDown(EnGoroiwa* this) {
this->actionFunc = EnGoroiwa_MoveDown;
EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC);
this->rollRotSpeed = 0.3f;
this->bounceCount = 0;
this->actor.velocity.y = fabsf(this->actor.speedXZ) * -0.3f;
this->stateFlags |= ENGOROIWA_RETAIN_ROT_SPEED;
this->stateFlags &= ~ENGOROIWA_IN_WATER;
}
void EnGoroiwa_MoveDown(EnGoroiwa* this, GlobalContext* globalCtx) {
if (this->collider.base.atFlags & AT_HIT) {
this->collider.base.atFlags &= ~AT_HIT;
func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 4);
func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT);
if ((this->actor.home.rot.z & 1) == 1) {
this->collisionDisabledTimer = 50;
}
} else if (EnGoroiwa_MoveDownToNextWaypoint(this, globalCtx)) {
EnGoroiwa_NextWaypoint(this, globalCtx);
EnGoroiwa_SetupRoll(this);
this->stateFlags &= ~ENGOROIWA_RETAIN_ROT_SPEED;
this->actor.speedXZ = 0.0f;
}
}
void EnGoroiwa_Update(Actor* thisx, GlobalContext* globalCtx) {
EnGoroiwa* this = (EnGoroiwa*)thisx;
Player* player = GET_PLAYER(globalCtx);
s32 pad;
s32 sp30;
if (!(player->stateFlags1 & 0x300000C0)) {
if (this->collisionDisabledTimer > 0) {
this->collisionDisabledTimer--;
}
this->actionFunc(this, globalCtx);
switch ((this->actor.params >> 10) & 1) {
case 1:
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C);
break;
case 0:
this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &sp30,
&this->actor, &this->actor.world.pos);
break;
}
EnGoroiwa_UpdateRotation(this, globalCtx);
if (this->actor.xzDistToPlayer < 300.0f) {
EnGoroiwa_UpdateCollider(this);
if ((this->stateFlags & ENGOROIWA_ENABLE_AT) && this->collisionDisabledTimer <= 0) {
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
if ((this->stateFlags & ENGOROIWA_ENABLE_OC) && this->collisionDisabledTimer <= 0) {
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
}
}
}
void EnGoroiwa_Draw(Actor* thisx, GlobalContext* globalCtx) {
Gfx_DrawDListOpa(globalCtx, gRollingRockDL);
}