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

759 lines
29 KiB
C
Raw Normal View History

/*
* 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", "../z_en_gr.c", 559, 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;
if (1) {}
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", "../z_en_gr.c", 1033,
this->actor.params);
Actor_Kill(&this->actor);
return;
}
if (globalCtx->setupPathList[pathIdx].count < 2) {
// "Error: Invalid Path Data"
osSyncPrintf(" : レールデータ が不正(%s %d)\n", "../z_en_gr.c", 1043);
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);
}