312 lines
11 KiB
C
312 lines
11 KiB
C
|
|
||
|
/**
|
||
|
* Behavior for bhvPlatformOnTrack and bhvTrackBall.
|
||
|
* The platform spawns up to 5 track balls at a time, which then despawn
|
||
|
* themselves as the platform moves past them.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Collision models for the different types of platforms.
|
||
|
*/
|
||
|
static void *sPlatformOnTrackCollisionModels[] = {
|
||
|
/* PLATFORM_ON_TRACK_TYPE_CARPET */ rr_seg7_collision_07029038,
|
||
|
/* PLATFORM_ON_TRACK_TYPE_SKI_LIFT */ ccm_seg7_collision_070163F8,
|
||
|
/* PLATFORM_ON_TRACK_TYPE_CHECKERED */ checkerboard_platform_seg8_collision_0800D710,
|
||
|
/* PLATFORM_ON_TRACK_TYPE_GRATE */ bitfs_seg7_collision_070157E0,
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Paths for the different instances of these platforms.
|
||
|
*/
|
||
|
static void *sPlatformOnTrackPaths[] = {
|
||
|
rr_seg7_trajectory_0702EC3C, rr_seg7_trajectory_0702ECC0, ccm_seg7_trajectory_0701669C,
|
||
|
bitfs_seg7_trajectory_070159AC, hmc_seg7_trajectory_0702B86C, lll_seg7_trajectory_0702856C,
|
||
|
lll_seg7_trajectory_07028660, rr_seg7_trajectory_0702ED9C, rr_seg7_trajectory_0702EEE0,
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Despawn all track balls and enter the init action.
|
||
|
*/
|
||
|
static void platform_on_track_reset(void) {
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_INIT;
|
||
|
// This will cause the track balls to all despawn
|
||
|
o->oPlatformOnTrackBaseBallIndex += 99;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If this platform is the kind that disappears, pause for a while, then
|
||
|
* begin blinking, and finally reset.
|
||
|
*/
|
||
|
static void platform_on_track_mario_not_on_platform(void) {
|
||
|
if (!((u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_DONT_DISAPPEAR)) {
|
||
|
// Once oTimer reaches 150, blink 40 times
|
||
|
if (obj_wait_then_blink(150, 40)) {
|
||
|
platform_on_track_reset();
|
||
|
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Init function for bhvPlatformOnTrack.
|
||
|
*/
|
||
|
void bhv_platform_on_track_init(void) {
|
||
|
if (!(o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)) {
|
||
|
s16 pathIndex = (u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_MASK_PATH;
|
||
|
o->oPlatformOnTrackType = ((u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_MASK_TYPE) >> 4;
|
||
|
|
||
|
o->oPlatformOnTrackIsNotSkiLift = o->oPlatformOnTrackType - PLATFORM_ON_TRACK_TYPE_SKI_LIFT;
|
||
|
|
||
|
o->collisionData =
|
||
|
segmented_to_virtual(sPlatformOnTrackCollisionModels[o->oPlatformOnTrackType]);
|
||
|
|
||
|
o->oPlatformOnTrackStartWaypoint = segmented_to_virtual(sPlatformOnTrackPaths[pathIndex]);
|
||
|
|
||
|
o->oPlatformOnTrackIsNotHMC = pathIndex - 4;
|
||
|
|
||
|
o->oBehParams2ndByte = o->oMoveAngleYaw; // TODO: Weird?
|
||
|
|
||
|
if (o->oPlatformOnTrackType == PLATFORM_ON_TRACK_TYPE_CHECKERED) {
|
||
|
o->header.gfx.scale[1] = 2.0f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Move to the start waypoint, spawn the first track balls, and enter the
|
||
|
* wait for mario action.
|
||
|
*/
|
||
|
static void platform_on_track_act_init(void) {
|
||
|
s32 i;
|
||
|
|
||
|
o->oPlatformOnTrackPrevWaypoint = o->oPlatformOnTrackStartWaypoint;
|
||
|
o->oPlatformOnTrackPrevWaypointFlags = 0;
|
||
|
o->oPlatformOnTrackBaseBallIndex = 0;
|
||
|
|
||
|
o->oPosX = o->oHomeX = o->oPlatformOnTrackStartWaypoint->pos[0];
|
||
|
o->oPosY = o->oHomeY = o->oPlatformOnTrackStartWaypoint->pos[1];
|
||
|
o->oPosZ = o->oHomeZ = o->oPlatformOnTrackStartWaypoint->pos[2];
|
||
|
|
||
|
o->oFaceAngleYaw = o->oBehParams2ndByte;
|
||
|
o->oForwardVel = o->oVelX = o->oVelY = o->oVelZ = o->oPlatformOnTrackDistMovedSinceLastBall = 0.0f;
|
||
|
|
||
|
o->oPlatformOnTrackWasStoodOn = FALSE;
|
||
|
|
||
|
if (o->oPlatformOnTrackIsNotSkiLift) {
|
||
|
o->oFaceAngleRoll = 0;
|
||
|
}
|
||
|
|
||
|
// Spawn track balls
|
||
|
for (i = 1; i < 6; i++) {
|
||
|
platform_on_track_update_pos_or_spawn_ball(i, o->oHomeX, o->oHomeY, o->oHomeZ);
|
||
|
}
|
||
|
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_WAIT_FOR_MARIO;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Wait for mario to stand on the platform for 20 frames, then begin moving.
|
||
|
*/
|
||
|
static void platform_on_track_act_wait_for_mario(void) {
|
||
|
if (gMarioObject->platform == o) {
|
||
|
if (o->oTimer > 20) {
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_MOVE_ALONG_TRACK;
|
||
|
}
|
||
|
} else {
|
||
|
if (o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) {
|
||
|
platform_on_track_reset();
|
||
|
}
|
||
|
|
||
|
o->oTimer = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Move along the track. After reaching the end, either start falling,
|
||
|
* return to the init action, or continue moving back to the start waypoint.
|
||
|
*/
|
||
|
static void platform_on_track_act_move_along_track(void) {
|
||
|
s16 initialAngle;
|
||
|
|
||
|
if (!o->oPlatformOnTrackIsNotSkiLift) {
|
||
|
PlaySound(SOUND_ENVIRONMENT_ELEVATOR3);
|
||
|
} else if (!o->oPlatformOnTrackIsNotHMC) {
|
||
|
PlaySound(SOUND_ENVIRONMENT_ELEVATOR1);
|
||
|
}
|
||
|
|
||
|
// Fall after reaching the last waypoint if desired
|
||
|
if (o->oPlatformOnTrackPrevWaypointFlags == WAYPOINT_FLAGS_END
|
||
|
&& !((u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_RETURN_TO_START)) {
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_FALL;
|
||
|
} else {
|
||
|
// The ski lift should pause or stop after reaching a special waypoint
|
||
|
if (o->oPlatformOnTrackPrevWaypointFlags != 0 && !o->oPlatformOnTrackIsNotSkiLift) {
|
||
|
if (o->oPlatformOnTrackPrevWaypointFlags == WAYPOINT_FLAGS_END
|
||
|
|| o->oPlatformOnTrackPrevWaypointFlags == WAYPOINT_FLAGS_PLATFORM_ON_TRACK_PAUSE) {
|
||
|
PlaySound2(SOUND_GENERAL_UNKNOWN4_LOWPRIO);
|
||
|
|
||
|
o->oForwardVel = 0.0f;
|
||
|
if (o->oPlatformOnTrackPrevWaypointFlags == WAYPOINT_FLAGS_END) {
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_INIT;
|
||
|
} else {
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_PAUSE_BRIEFLY;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// The ski lift accelerates, while the others instantly start
|
||
|
if (!o->oPlatformOnTrackIsNotSkiLift) {
|
||
|
obj_forward_vel_approach(10.0, 0.1f);
|
||
|
} else {
|
||
|
o->oForwardVel = 10.0f;
|
||
|
}
|
||
|
|
||
|
// Spawn a new track ball if necessary
|
||
|
if (approach_f32_ptr(&o->oPlatformOnTrackDistMovedSinceLastBall, 300.0f, o->oForwardVel)) {
|
||
|
o->oPlatformOnTrackDistMovedSinceLastBall -= 300.0f;
|
||
|
|
||
|
o->oHomeX = o->oPosX;
|
||
|
o->oHomeY = o->oPosY;
|
||
|
o->oHomeZ = o->oPosZ;
|
||
|
o->oPlatformOnTrackBaseBallIndex = (u16)(o->oPlatformOnTrackBaseBallIndex + 1);
|
||
|
|
||
|
platform_on_track_update_pos_or_spawn_ball(5, o->oHomeX, o->oHomeY, o->oHomeZ);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
platform_on_track_update_pos_or_spawn_ball(0, o->oPosX, o->oPosY, o->oPosZ);
|
||
|
|
||
|
o->oMoveAnglePitch = o->oPlatformOnTrackPitch;
|
||
|
o->oMoveAngleYaw = o->oPlatformOnTrackYaw;
|
||
|
|
||
|
//! Both oAngleVelYaw and oAngleVelRoll aren't reset until the platform
|
||
|
// starts moving again, resulting in unexpected platform displacement
|
||
|
// after reappearing
|
||
|
|
||
|
// Turn face yaw and compute yaw vel
|
||
|
if (!((u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_DONT_TURN_YAW)) {
|
||
|
s16 targetFaceYaw = o->oMoveAngleYaw + 0x4000;
|
||
|
s16 yawSpeed = abs_angle_diff(targetFaceYaw, o->oFaceAngleYaw) / 20;
|
||
|
|
||
|
initialAngle = o->oFaceAngleYaw;
|
||
|
clamp_s16(&yawSpeed, 100, 500);
|
||
|
obj_face_yaw_approach(targetFaceYaw, yawSpeed);
|
||
|
o->oAngleVelYaw = (s16) o->oFaceAngleYaw - initialAngle;
|
||
|
}
|
||
|
|
||
|
// Turn face roll and compute roll vel
|
||
|
if (((u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_DONT_TURN_ROLL)) {
|
||
|
s16 rollSpeed = abs_angle_diff(o->oMoveAnglePitch, o->oFaceAngleRoll) / 20;
|
||
|
|
||
|
initialAngle = o->oFaceAngleRoll;
|
||
|
clamp_s16(&rollSpeed, 100, 500);
|
||
|
//! If the platform is moving counterclockwise upward or
|
||
|
// clockwise downward, this will be backward
|
||
|
obj_face_roll_approach(o->oMoveAnglePitch, rollSpeed);
|
||
|
o->oAngleVelRoll = (s16) o->oFaceAngleRoll - initialAngle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (gMarioObject->platform != o) {
|
||
|
platform_on_track_mario_not_on_platform();
|
||
|
} else {
|
||
|
o->oTimer = 0;
|
||
|
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Wait 20 frames then continue moving.
|
||
|
*/
|
||
|
static void platform_on_track_act_pause_briefly(void) {
|
||
|
if (o->oTimer > 20) {
|
||
|
o->oAction = PLATFORM_ON_TRACK_ACT_MOVE_ALONG_TRACK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fall downward with no terminal velocity, stopping after reaching y = -12k
|
||
|
* and eventually blinking and disappearing.
|
||
|
*/
|
||
|
static void platform_on_track_act_fall(void) {
|
||
|
obj_move_using_vel_and_gravity();
|
||
|
|
||
|
if (gMarioObject->platform != o) {
|
||
|
platform_on_track_mario_not_on_platform();
|
||
|
} else {
|
||
|
o->oTimer = 0;
|
||
|
//! Doesn't ensure visibility
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Control the rocking of the ski lift.
|
||
|
*/
|
||
|
static void platform_on_track_rock_ski_lift(void) {
|
||
|
s32 targetRoll = 0;
|
||
|
UNUSED s32 initialRoll = o->oFaceAngleRoll;
|
||
|
|
||
|
o->oFaceAngleRoll += (s32) o->oPlatformOnTrackSkiLiftRollVel;
|
||
|
|
||
|
// Tilt away from the moving direction and toward mario
|
||
|
if (gMarioObject->platform == o) {
|
||
|
targetRoll = o->oForwardVel * sins(o->oMoveAngleYaw) * -50.0f
|
||
|
+ (s32)(o->oDistanceToMario * sins(o->oAngleToMario - o->oFaceAngleYaw) * -4.0f);
|
||
|
}
|
||
|
|
||
|
oscillate_toward(
|
||
|
/* value */ &o->oFaceAngleRoll,
|
||
|
/* vel */ &o->oPlatformOnTrackSkiLiftRollVel,
|
||
|
/* target */ targetRoll,
|
||
|
/* velCloseToZero */ 5.0f,
|
||
|
/* accel */ 6.0f,
|
||
|
/* slowdown */ 1.5f);
|
||
|
clamp_f32(&o->oPlatformOnTrackSkiLiftRollVel, -100.0f, 100.0f);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update function for bhvPlatformOnTrack.
|
||
|
*/
|
||
|
void bhv_platform_on_track_update(void) {
|
||
|
switch (o->oAction) {
|
||
|
case PLATFORM_ON_TRACK_ACT_INIT:
|
||
|
platform_on_track_act_init();
|
||
|
break;
|
||
|
case PLATFORM_ON_TRACK_ACT_WAIT_FOR_MARIO:
|
||
|
platform_on_track_act_wait_for_mario();
|
||
|
break;
|
||
|
case PLATFORM_ON_TRACK_ACT_MOVE_ALONG_TRACK:
|
||
|
platform_on_track_act_move_along_track();
|
||
|
break;
|
||
|
case PLATFORM_ON_TRACK_ACT_PAUSE_BRIEFLY:
|
||
|
platform_on_track_act_pause_briefly();
|
||
|
break;
|
||
|
case PLATFORM_ON_TRACK_ACT_FALL:
|
||
|
platform_on_track_act_fall();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!o->oPlatformOnTrackIsNotSkiLift) {
|
||
|
platform_on_track_rock_ski_lift();
|
||
|
} else if (o->oPlatformOnTrackType == PLATFORM_ON_TRACK_TYPE_CARPET) {
|
||
|
if (!o->oPlatformOnTrackWasStoodOn && gMarioObject->platform == o) {
|
||
|
o->oPlatformOnTrackOffsetY = -8.0f;
|
||
|
o->oPlatformOnTrackWasStoodOn = TRUE;
|
||
|
}
|
||
|
|
||
|
approach_f32_ptr(&o->oPlatformOnTrackOffsetY, 0.0f, 0.5f);
|
||
|
o->oPosY += o->oPlatformOnTrackOffsetY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update function for bhvTrackBall.
|
||
|
*/
|
||
|
void bhv_track_ball_update(void) {
|
||
|
// Despawn after the elevator passes this ball
|
||
|
s16 relativeIndex =
|
||
|
(s16) o->oBehParams2ndByte - (s16) o->parentObj->oPlatformOnTrackBaseBallIndex - 1;
|
||
|
if (relativeIndex < 1 || relativeIndex > 5) {
|
||
|
mark_object_for_deletion(o);
|
||
|
}
|
||
|
}
|