mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-08-13 17:03:47 -04:00
99260acaf1
* Use PlayState instead of GlobalContext - GlobalContext -> PlayState - globalCtx -> play - GlobalCtx -> PlayState - globalContext -> playState * Find and replace Gameplay_ with Play_ * Correct some misnamed argument cases
758 lines
28 KiB
C
758 lines
28 KiB
C
/*
|
||
* 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, PlayState* play);
|
||
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, PlayState* play);
|
||
void EnGoroiwa_Destroy(Actor* thisx, PlayState* play);
|
||
void EnGoroiwa_Update(Actor* thisx, PlayState* play);
|
||
void EnGoroiwa_Draw(Actor* thisx, PlayState* play);
|
||
|
||
void EnGoroiwa_SetupRoll(EnGoroiwa* this);
|
||
void EnGoroiwa_Roll(EnGoroiwa* this, PlayState* play);
|
||
void EnGoroiwa_SetupMoveAndFallToGround(EnGoroiwa* this);
|
||
void EnGoroiwa_MoveAndFallToGround(EnGoroiwa* this, PlayState* play);
|
||
void EnGoroiwa_SetupWait(EnGoroiwa* this);
|
||
void EnGoroiwa_Wait(EnGoroiwa* this, PlayState* play);
|
||
void EnGoroiwa_SetupMoveUp(EnGoroiwa* this);
|
||
void EnGoroiwa_MoveUp(EnGoroiwa* this, PlayState* play);
|
||
void EnGoroiwa_SetupMoveDown(EnGoroiwa* this);
|
||
void EnGoroiwa_MoveDown(EnGoroiwa* this, PlayState* play);
|
||
|
||
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, PlayState* play) {
|
||
s32 pad;
|
||
|
||
Collider_InitJntSph(play, &this->collider);
|
||
Collider_SetJntSph(play, &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, PlayState* play) {
|
||
if (play->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, PlayState* play) {
|
||
Path* path = &play->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, PlayState* play, Vec3f* dest) {
|
||
s16 loopMode = (this->actor.params >> 8) & 3;
|
||
Path* path = &play->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, PlayState* play) {
|
||
this->endWaypoint = play->setupPathList [this->actor.params & 0xFF].count - 1;
|
||
this->currentWaypoint = 0;
|
||
this->nextWaypoint = 1;
|
||
this->pathDirection = 1;
|
||
}
|
||
|
||
void EnGoroiwa_TeleportToWaypoint(EnGoroiwa* this, PlayState* play, s32 waypoint) {
|
||
Path* path = &play->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, PlayState* play) {
|
||
s32 pad;
|
||
Path* path = &play->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(PlayState* play, 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(play, &randPos, &velocity, &accel, (s16)(Rand_ZeroOne() * 30.0f) + 100, 80);
|
||
func_800286CC(play, &randPos, &velocity, &accel, (s16)(Rand_ZeroOne() * 20.0f) + 80, 80);
|
||
}
|
||
}
|
||
|
||
void EnGoroiwa_SpawnWaterEffects(PlayState* play, 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(play, &splashPos, 0, 0, 0, 350);
|
||
}
|
||
|
||
EffectSsGRipple_Spawn(play, contactPos, 300, 700, 0);
|
||
EffectSsGRipple_Spawn(play, contactPos, 500, 900, 4);
|
||
EffectSsGRipple_Spawn(play, contactPos, 500, 1300, 8);
|
||
}
|
||
|
||
s32 EnGoroiwa_MoveAndFall(EnGoroiwa* this, PlayState* play) {
|
||
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 = &play->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, PlayState* play) {
|
||
Path* path = &play->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, PlayState* play) {
|
||
s32 pad;
|
||
Path* path = &play->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, PlayState* play) {
|
||
s32 pad;
|
||
Path* path = &play->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(play), 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(play, &play->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(play, &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(play, &play->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(play, &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, PlayState* play) {
|
||
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, play, &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, PlayState* play) {
|
||
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, play, this->currentWaypoint);
|
||
}
|
||
}
|
||
|
||
EnGoroiwa_FaceNextWaypoint(this, play);
|
||
}
|
||
|
||
void EnGoroiwa_SpawnFragments(EnGoroiwa* this, PlayState* play) {
|
||
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(play, &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(play, &effectPos, 80.0f, 5, 70, 110, 1);
|
||
func_80033480(play, &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, PlayState* play) {
|
||
static f32 yOffsets[] = { 0.0f, 595.0f };
|
||
EnGoroiwa* this = (EnGoroiwa*)thisx;
|
||
s32 pathIdx;
|
||
|
||
Actor_ProcessInitChain(&this->actor, sInitChain);
|
||
EnGoroiwa_InitCollider(this, play);
|
||
pathIdx = this->actor.params & 0xFF;
|
||
if (pathIdx == 0xFF) {
|
||
// "Error: Invalid arg_data"
|
||
osSyncPrintf("Error : arg_data が不正(%s %d)(arg_data 0x%04x)\n", __FILE__, __LINE__,
|
||
this->actor.params);
|
||
Actor_Kill(&this->actor);
|
||
return;
|
||
}
|
||
if (play->setupPathList[pathIdx].count < 2) {
|
||
// "Error: Invalid Path Data"
|
||
osSyncPrintf("Error : レールデータ が不正(%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, play);
|
||
EnGoroiwa_InitPath(this, play);
|
||
EnGoroiwa_TeleportToWaypoint(this, play, 0);
|
||
EnGoroiwa_InitRotation(this);
|
||
EnGoroiwa_FaceNextWaypoint(this, play);
|
||
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, PlayState* play2) {
|
||
PlayState* play = play2;
|
||
EnGoroiwa* this = (EnGoroiwa*)thisx;
|
||
|
||
Collider_DestroyJntSph(play, &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, PlayState* play) {
|
||
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, play);
|
||
}
|
||
}
|
||
func_8002F6D4(play, &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(play)->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, play)) {
|
||
loopMode = (this->actor.params >> 8) & 3;
|
||
if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK &&
|
||
(this->nextWaypoint == 0 || this->nextWaypoint == this->endWaypoint)) {
|
||
EnGoroiwa_SpawnFragments(this, play);
|
||
}
|
||
EnGoroiwa_NextWaypoint(this, play);
|
||
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, play);
|
||
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, PlayState* play) {
|
||
EnGoroiwa_MoveAndFall(this, play);
|
||
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, play);
|
||
}
|
||
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, PlayState* play) {
|
||
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, PlayState* play) {
|
||
if (this->collider.base.atFlags & AT_HIT) {
|
||
this->collider.base.atFlags &= ~AT_HIT;
|
||
func_8002F6D4(play, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 4);
|
||
func_8002F7DC(&GET_PLAYER(play)->actor, NA_SE_PL_BODY_HIT);
|
||
if ((this->actor.home.rot.z & 1) == 1) {
|
||
this->collisionDisabledTimer = 50;
|
||
}
|
||
} else if (EnGoroiwa_MoveUpToNextWaypoint(this, play)) {
|
||
EnGoroiwa_NextWaypoint(this, play);
|
||
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, PlayState* play) {
|
||
if (this->collider.base.atFlags & AT_HIT) {
|
||
this->collider.base.atFlags &= ~AT_HIT;
|
||
func_8002F6D4(play, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 4);
|
||
func_8002F7DC(&GET_PLAYER(play)->actor, NA_SE_PL_BODY_HIT);
|
||
if ((this->actor.home.rot.z & 1) == 1) {
|
||
this->collisionDisabledTimer = 50;
|
||
}
|
||
} else if (EnGoroiwa_MoveDownToNextWaypoint(this, play)) {
|
||
EnGoroiwa_NextWaypoint(this, play);
|
||
EnGoroiwa_SetupRoll(this);
|
||
this->stateFlags &= ~ENGOROIWA_RETAIN_ROT_SPEED;
|
||
this->actor.speedXZ = 0.0f;
|
||
}
|
||
}
|
||
|
||
void EnGoroiwa_Update(Actor* thisx, PlayState* play) {
|
||
EnGoroiwa* this = (EnGoroiwa*)thisx;
|
||
Player* player = GET_PLAYER(play);
|
||
s32 pad;
|
||
s32 sp30;
|
||
|
||
if (!(player->stateFlags1 & 0x300000C0)) {
|
||
if (this->collisionDisabledTimer > 0) {
|
||
this->collisionDisabledTimer--;
|
||
}
|
||
this->actionFunc(this, play);
|
||
switch ((this->actor.params >> 10) & 1) {
|
||
case 1:
|
||
Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C);
|
||
break;
|
||
case 0:
|
||
this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&play->colCtx, &this->actor.floorPoly, &sp30,
|
||
&this->actor, &this->actor.world.pos);
|
||
break;
|
||
}
|
||
EnGoroiwa_UpdateRotation(this, play);
|
||
if (this->actor.xzDistToPlayer < 300.0f) {
|
||
EnGoroiwa_UpdateCollider(this);
|
||
if ((this->stateFlags & ENGOROIWA_ENABLE_AT) && this->collisionDisabledTimer <= 0) {
|
||
CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base);
|
||
}
|
||
if ((this->stateFlags & ENGOROIWA_ENABLE_OC) && this->collisionDisabledTimer <= 0) {
|
||
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void EnGoroiwa_Draw(Actor* thisx, PlayState* play) {
|
||
Gfx_DrawDListOpa(play, gRollingRockDL);
|
||
}
|