Small obj fixes and misc

Make whirlpool use acts and format it's file

Clean up bhv_cmd_begin and cur_obj_follow_path

Make BhvParams unsigned and fix snufit pu crash
This commit is contained in:
AloXado320 2023-01-19 00:20:35 -05:00
parent bd2e6ba3b1
commit 785fe98703
24 changed files with 58 additions and 124 deletions

View file

@ -3844,6 +3844,7 @@ const BehaviorScript bhvMessagePanel[] = {
BEGIN(OBJ_LIST_SURFACE),
OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE),
LOAD_COLLISION_DATA(wooden_signpost_seg3_collision_0302DD80),
SET_FLOAT(oCollisionDistance, 150),
SET_INTERACT_TYPE(INTERACT_TEXT),
SET_INT(oInteractionSubtype, INT_SUBTYPE_SIGN),
DROP_TO_FLOOR(),

View file

@ -29,10 +29,6 @@ enum LevelCmdGetOrSetValIds {
#define WARP_CHECKPOINT 0x80
#define WARP_NO_CHECKPOINT 0x00
#define WHIRLPOOL_COND_ALWAYS 0
#define WHIRLPOOL_COND_BOWSER2_BEATEN 2
#define WHIRLPOOL_COND_AT_LEAST_SECOND_STAR 3
// Head defines
#define REGULAR_FACE 0x0002
#define DIZZY_FACE 0x0003
@ -386,8 +382,8 @@ enum LevelCommandsIDList {
CMD_HH(unk6, unk8), \
CMD_HH(unk10, 0x0000)
#define WHIRLPOOL(index, condition, posX, posY, posZ, strength) \
CMD_BBBB(LVL_SCRIPT_CMD_3B, 0x0C, index, condition), \
#define WHIRLPOOL(index, acts, posX, posY, posZ, strength) \
CMD_BBBB(LVL_SCRIPT_CMD_3B, 0x0C, index, acts), \
CMD_HH(posX, posY), \
CMD_HH(posZ, strength)

View file

@ -118,7 +118,7 @@
#define /*0x17C*/ oOpacity OBJECT_FIELD_S32(0x3D)
#define /*0x180*/ oDamageOrCoinValue OBJECT_FIELD_S32(0x3E)
#define /*0x184*/ oHealth OBJECT_FIELD_S32(0x3F)
#define /*0x188*/ oBhvParams OBJECT_FIELD_S32(0x40)
#define /*0x188*/ oBhvParams OBJECT_FIELD_U32(0x40)
#define /*0x18C*/ oPrevAction OBJECT_FIELD_S32(0x41)
#define /*0x190*/ oInteractionSubtype OBJECT_FIELD_U32(0x42)
#define /*0x194*/ oCollisionDistance OBJECT_FIELD_F32(0x43)

View file

@ -86,7 +86,7 @@ const LevelScript level_ddd_entry[] = {
WARP_NODE(/*id*/ WARP_NODE_0A, /*destLevel*/ LEVEL_DDD, /*destArea*/ 1, /*destNode*/ WARP_NODE_0A, /*flags*/ WARP_NO_CHECKPOINT),
WARP_NODE(/*id*/ WARP_NODE_SUCCESS, /*destLevel*/ LEVEL_CASTLE, /*destArea*/ 3, /*destNode*/ WARP_NODE_35, /*flags*/ WARP_NO_CHECKPOINT),
WARP_NODE(/*id*/ WARP_NODE_DEATH, /*destLevel*/ LEVEL_CASTLE, /*destArea*/ 3, /*destNode*/ WARP_NODE_67, /*flags*/ WARP_NO_CHECKPOINT),
WHIRLPOOL(/*unk2*/ 0, /*unk3*/ 0, /*pos*/ -3174, -4915, 102, /*strength*/ 20),
WHIRLPOOL(/*index*/ 0, /*acts*/ ALL_ACTS, /*pos*/ -3174, -4915, 102, /*strength*/ 20),
JUMP_LINK(script_func_local_1),
JUMP_LINK(script_func_local_2),
INSTANT_WARP(/*index*/ 3, /*destArea*/ 2, /*displace*/ -8192, 0, 0),
@ -97,8 +97,8 @@ const LevelScript level_ddd_entry[] = {
END_AREA(),
AREA(/*index*/ 2, ddd_geo_000570),
WHIRLPOOL(/*unk2*/ 0, /*unk3*/ 0, /*pos*/ 3355, -3575, -515, /*strength*/ -30),
WHIRLPOOL(/*unk2*/ 1, /*unk3*/ 2, /*pos*/ 3917, -2040, -6041, /*strength*/ 50),
WHIRLPOOL(/*index*/ 0, /*acts*/ ALL_ACTS, /*pos*/ 3355, -3575, -515, /*strength*/ -30),
WHIRLPOOL(/*index*/ 1, /*acts*/ ACT_2 | ACT_3 | ACT_4 | ACT_5 | ACT_6, /*pos*/ 3917, -2040, -6041, /*strength*/ 50),
WARP_NODE(/*id*/ WARP_NODE_SUCCESS, /*destLevel*/ LEVEL_CASTLE, /*destArea*/ 3, /*destNode*/ WARP_NODE_35, /*flags*/ WARP_NO_CHECKPOINT),
WARP_NODE(/*id*/ WARP_NODE_DEATH, /*destLevel*/ LEVEL_CASTLE, /*destArea*/ 3, /*destNode*/ WARP_NODE_67, /*flags*/ WARP_NO_CHECKPOINT),
WARP_NODE(/*id*/ WARP_NODE_WARP_FLOOR, /*destLevel*/ LEVEL_CASTLE_GROUNDS, /*destArea*/ 1, /*destNode*/ WARP_NODE_1E, /*flags*/ WARP_NO_CHECKPOINT),

View file

@ -147,7 +147,7 @@ const LevelScript level_jrb_entry[] = {
WARP_NODE(/*id*/ WARP_NODE_WARP_FLOOR, /*destLevel*/ LEVEL_JRB, /*destArea*/ 2, /*destNode*/ WARP_NODE_0A, /*flags*/ WARP_NO_CHECKPOINT),
WARP_NODE(/*id*/ WARP_NODE_SUCCESS, /*destLevel*/ LEVEL_CASTLE, /*destArea*/ 1, /*destNode*/ WARP_NODE_35, /*flags*/ WARP_NO_CHECKPOINT),
WARP_NODE(/*id*/ WARP_NODE_DEATH, /*destLevel*/ LEVEL_CASTLE, /*destArea*/ 1, /*destNode*/ WARP_NODE_67, /*flags*/ WARP_NO_CHECKPOINT),
WHIRLPOOL(/*unk2*/ 0, /*unk3*/ 3, /*pos*/ 4979, -5222, 2482, /*strength*/ -30),
WHIRLPOOL(/*index*/ 0, /*acts*/ ACT_2 | ACT_3 | ACT_4 | ACT_5 | ACT_6, /*pos*/ 4979, -5222, 2482, /*strength*/ -30),
JUMP_LINK(script_func_local_1),
JUMP_LINK(script_func_local_2),
JUMP_LINK(script_func_local_3),

View file

@ -650,23 +650,8 @@ static s32 bhv_cmd_nop_4(void) {
}
// Command 0x00: Defines the start of the behavior script as well as the object list the object belongs to.
// Has some special behavior for certain objects.
// Usage: BEGIN(objList)
static s32 bhv_cmd_begin(void) {
// These objects were likely very early objects, which is why this code is here
// instead of in the respective behavior scripts.
// Initiate the room if the object is a haunted chair or the mad piano.
if (cur_obj_has_behavior(bhvHauntedChair)) {
bhv_init_room();
}
if (cur_obj_has_behavior(bhvMadPiano)) {
bhv_init_room();
}
// Set collision distance if the object is a message panel.
if (cur_obj_has_behavior(bhvMessagePanel)) {
gCurrentObject->oCollisionDistance = 150.0f;
}
gCurBhvCommand++;
return BHV_PROC_CONTINUE;
}

View file

@ -478,9 +478,9 @@ static void level_cmd_place_object(void) {
spawnInfo->startPos[1] = CMD_GET(s16, 6);
spawnInfo->startPos[2] = CMD_GET(s16, 8);
spawnInfo->startAngle[0] = CMD_GET(s16, 10) * 0x8000 / 180;
spawnInfo->startAngle[1] = CMD_GET(s16, 12) * 0x8000 / 180;
spawnInfo->startAngle[2] = CMD_GET(s16, 14) * 0x8000 / 180;
spawnInfo->startAngle[0] = degrees_to_angle(CMD_GET(s16, 10));
spawnInfo->startAngle[1] = degrees_to_angle(CMD_GET(s16, 12));
spawnInfo->startAngle[2] = degrees_to_angle(CMD_GET(s16, 14));
spawnInfo->areaIndex = sCurrAreaIndex;
spawnInfo->activeAreaIndex = sCurrAreaIndex;
@ -597,12 +597,10 @@ static void level_cmd_3A(void) {
static void level_cmd_create_whirlpool(void) {
struct Whirlpool *whirlpool;
s32 index = CMD_GET(u8, 2);
s32 beatBowser2 =
(save_file_get_flags() & (SAVE_FLAG_HAVE_KEY_2 | SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) != 0;
if (CMD_GET(u8, 3) == 0 || (CMD_GET(u8, 3) == 1 && !beatBowser2)
|| (CMD_GET(u8, 3) == 2 && beatBowser2) || (CMD_GET(u8, 3) == 3 && gCurrActNum >= 2)) {
if (sCurrAreaIndex != -1 && index < 2) {
// ex-alo change
// Make whirlpool spawn using acts instead of weird bowser 2 checks
if (sCurrAreaIndex != -1 && index < ARRAY_COUNT(gAreas[sCurrAreaIndex].whirlpools)
&& ((CMD_GET(u8, 3) & (1 << (gCurrActNum - 1))) || CMD_GET(u8, 3) == 0x1F)) {
if ((whirlpool = gAreas[sCurrAreaIndex].whirlpools[index]) == NULL) {
whirlpool = alloc_only_pool_alloc(sLevelPool, sizeof(struct Whirlpool));
gAreas[sCurrAreaIndex].whirlpools[index] = whirlpool;
@ -611,7 +609,6 @@ static void level_cmd_create_whirlpool(void) {
vec3s_set(whirlpool->pos, CMD_GET(s16, 4), CMD_GET(s16, 6), CMD_GET(s16, 8));
whirlpool->strength = CMD_GET(s16, 10);
}
}
sCurrentCmd = CMD_NEXT;
}

View file

@ -190,7 +190,7 @@ void load_obj_warp_nodes(void) {
}
void clear_areas(void) {
s32 i;
s32 i, j;
gCurrentArea = NULL;
gWarpTransition.isActive = FALSE;
@ -211,8 +211,9 @@ void clear_areas(void) {
gAreaData[i].objectSpawnInfos = NULL;
gAreaData[i].camera = NULL;
gAreaData[i].unused = NULL;
gAreaData[i].whirlpools[0] = NULL;
gAreaData[i].whirlpools[1] = NULL;
for (j = 0; j < ARRAY_COUNT(gAreaData[i].whirlpools); j++) {
gAreaData[i].whirlpools[i] = NULL;
}
gAreaData[i].dialog[0] = DIALOG_NONE;
gAreaData[i].dialog[1] = DIALOG_NONE;
gAreaData[i].musicParam = 0;

View file

@ -78,17 +78,10 @@ void bowling_ball_set_waypoints(void) {
}
void bhv_bowling_ball_roll_loop(void) {
s16 collisionFlags;
s32 followStatus;
#ifdef AVOID_UB
followStatus = 0;
#endif
bowling_ball_set_waypoints();
collisionFlags = object_step();
s16 collisionFlags = object_step();
//! Uninitialized parameter, but the parameter is unused in the called function
followStatus = cur_obj_follow_path(followStatus);
s32 followStatus = cur_obj_follow_path();
o->oBowlingBallTargetYaw = o->oPathedTargetYaw;
o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, o->oBowlingBallTargetYaw, 0x400);
@ -113,15 +106,9 @@ void bhv_bowling_ball_roll_loop(void) {
}
void bhv_bowling_ball_initialize_loop(void) {
s32 followStatus;
#ifdef AVOID_UB
followStatus = 0;
#endif
bowling_ball_set_waypoints();
//! Uninitialized parameter, but the parameter is unused in the called function
followStatus = cur_obj_follow_path(followStatus);
f32 followStatus = cur_obj_follow_path();
o->oMoveAngleYaw = o->oPathedTargetYaw;

View file

@ -605,7 +605,7 @@ static void koopa_the_quick_act_race(void) {
// Hitbox is slightly larger while racing
cur_obj_push_mario_away_from_cylinder(180.0f, 300.0f);
if (cur_obj_follow_path(0) == PATH_REACHED_END) {
if (cur_obj_follow_path() == PATH_REACHED_END) {
o->oAction = KOOPA_THE_QUICK_ACT_DECELERATE;
} else {
f32 downhillSteepness;

View file

@ -42,14 +42,9 @@ void bhv_manta_ray_init(void) {
static void manta_ray_move(void) {
s16 animFrame = o->header.gfx.animInfo.animFrame;
s32 pathStatus;
#ifdef AVOID_UB
pathStatus = 0;
#endif
o->oPathedStartWaypoint = (struct Waypoint *) sMantaRayTraj;
//! Uninitialized parameter, but the parameter is unused in the called function
pathStatus = cur_obj_follow_path(pathStatus);
cur_obj_follow_path();
o->oMantaTargetYaw = o->oPathedTargetYaw;
o->oMantaTargetPitch = o->oPathedTargetPitch;

View file

@ -107,21 +107,13 @@ void bhv_mips_act_wait_for_nearby_mario(void) {
* Continue to follow our path around the basement area.
*/
void bhv_mips_act_follow_path(void) {
s16 collisionFlags = 0;
s32 followStatus;
// Retrieve current waypoint.
struct Waypoint **pathBase = segmented_to_virtual(&inside_castle_seg7_trajectory_mips);
struct Waypoint *waypoint = segmented_to_virtual(*(pathBase + o->oMipsStartWaypointIndex));
#ifdef AVOID_UB
followStatus = 0;
#endif
// Set start waypoint and follow the path from there.
o->oPathedStartWaypoint = waypoint;
//! Uninitialized parameter, but the parameter is unused in the called function
followStatus = cur_obj_follow_path(followStatus);
s32 followStatus = cur_obj_follow_path();
// Update velocity and angle and do movement.
#ifndef VERSION_JP
@ -130,7 +122,7 @@ void bhv_mips_act_follow_path(void) {
o->oForwardVel = 45.0f;
#endif
o->oMoveAngleYaw = o->oPathedTargetYaw;
collisionFlags = object_step();
s16 collisionFlags = object_step();
// If we are at the end of the path, do idle animation and wait for Mario.
if (followStatus == PATH_REACHED_END) {

View file

@ -65,7 +65,7 @@ static void racing_penguin_act_prepare_for_race(void) {
}
static void racing_penguin_act_race(void) {
if (cur_obj_follow_path(0) == PATH_REACHED_END) {
if (cur_obj_follow_path() == PATH_REACHED_END) {
o->oRacingPenguinReachedBottom = TRUE;
o->oAction = RACING_PENGUIN_ACT_FINISH_RACE;
} else {

View file

@ -52,16 +52,9 @@ void adjust_rolling_face_pitch(f32 f12) {
}
void snowmans_bottom_act_1(void) {
UNUSED s16 collisionFlags;
s32 followStatus;
#ifdef AVOID_UB
followStatus = 0;
#endif
o->oPathedStartWaypoint = segmented_to_virtual(&ccm_seg7_trajectory_snowman);
collisionFlags = object_step_without_floor_orient();
//! Uninitialized parameter, but the parameter is unused in the called function
followStatus = cur_obj_follow_path(followStatus);
object_step_without_floor_orient();
s32 followStatus = cur_obj_follow_path();
o->oSnowmansBottomUnkF8 = o->oPathedTargetYaw;
o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, o->oSnowmansBottomUnkF8, 0x400);
@ -166,7 +159,7 @@ void bhv_snowmans_bottom_loop(void) {
void bhv_snowmans_head_init(void) {
u8 starFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum));
s8 sp36 = (o->oBhvParams >> 24) & 0xFF;
u8 bhvParams = (o->oBhvParams >> 24) & 0xFF;
cur_obj_scale(0.7f);
@ -174,7 +167,7 @@ void bhv_snowmans_head_init(void) {
o->oFriction = 0.999f;
o->oBuoyancy = 2.0f;
if ((starFlags & (1 << sp36)) && gCurrActNum != sp36 + 1) {
if ((starFlags & (1 << bhvParams)) && gCurrActNum != bhvParams + 1) {
spawn_object_abs_with_rot(o, 0, MODEL_CCM_SNOWMAN_BASE, bhvBigSnowmanWhole, -4230, -1344, 1813,
0, 0, 0);
o->oPosX = -4230.0f;

View file

@ -67,9 +67,7 @@ Gfx *geo_snufit_scale_body(s32 callContext, struct GraphNode *node, UNUSED Mat4
* then prepares to shoot after a period.
*/
void snufit_act_idle(void) {
// This line would could cause a crash in certain PU situations,
// if the game would not have already crashed.
s32 marioDist = (s32)(o->oDistanceToMario / 10.0f);
f32 marioDist = (o->oDistanceToMario / 10.0f);
if (o->oTimer > marioDist && o->oDistanceToMario < 800.0f) {

View file

@ -17,7 +17,7 @@ void bhv_spawned_star_init(void) {
o->oBhvParams = o->parentObj->oBhvParams;
}
s32 starIndex = (o->oBhvParams >> 24) & 0xFF;
u8 starIndex = (o->oBhvParams >> 24) & 0xFF;
if (save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum)) & (1 << starIndex)) {
cur_obj_set_model(MODEL_TRANSPARENT_STAR);

View file

@ -13,7 +13,7 @@ static struct ObjectHitbox sCollectStarHitbox = {
};
void bhv_collect_star_init(void) {
s8 starIndex = (o->oBhvParams >> 24) & 0xFF;
u8 starIndex = (o->oBhvParams >> 24) & 0xFF;
u8 currentLevelStarFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum));
if (currentLevelStarFlags & (1 << starIndex)) {

View file

@ -352,7 +352,7 @@ void ukiki_act_go_to_cage(void) {
o->oPathedStartWaypoint = (struct Waypoint *) sCageUkikiPath;
if (cur_obj_follow_path(0) != PATH_REACHED_END) {
if (cur_obj_follow_path() != PATH_REACHED_END) {
o->oForwardVel = 10.0f;
cur_obj_rotate_yaw_toward(o->oPathedTargetYaw, 0x400);
o->oPosY = o->oFloorHeight;

View file

@ -68,7 +68,7 @@ void unagi_act_1_4(s32 arg0) {
cur_obj_play_sound_2(SOUND_GENERAL_MOVING_WATER);
}
if (cur_obj_follow_path(0) == PATH_REACHED_END) {
if (cur_obj_follow_path() == PATH_REACHED_END) {
o->oAction = arg0;
}

View file

@ -19,10 +19,6 @@ void bhv_whirlpool_init(void) {
o->oFaceAngleRoll = 0;
}
void whirlpool_set_hitbox(void) {
obj_set_hitbox(o, &sWhirlpoolHitbox);
}
void whirpool_orient_graph(void) {
f32 cosPitch = coss(o->oFaceAnglePitch);
f32 sinPitch = sins(o->oFaceAnglePitch);
@ -63,7 +59,7 @@ void bhv_whirlpool_loop(void) {
cur_obj_play_sound_1(SOUND_ENV_WATER);
whirlpool_set_hitbox();
obj_set_hitbox(o, &sWhirlpoolHitbox);
}
void bhv_jet_stream_loop(void) {

View file

@ -128,7 +128,7 @@ static void apply_water_current(struct MarioState *m, Vec3f step) {
step[2] += currentSpeed * coss(currentAngle);
}
for (i = 0; i < 2; i++) {
for (i = 0; i < ARRAY_COUNT(gAreaData[i].whirlpools); i++) {
struct Whirlpool *whirlpool = gCurrentArea->whirlpools[i];
if (whirlpool != NULL) {
f32 strength = 0.0f;
@ -146,10 +146,11 @@ static void apply_water_current(struct MarioState *m, Vec3f step) {
yawToWhirlpool -= (s16)(0x2000 * 1000.0f / (distance + 1000.0f));
if (whirlpool->strength >= 0) {
#ifdef VANILLA_CHECKS
if (gCurrLevelNum == LEVEL_DDD && gCurrAreaIndex == 2) {
whirlpoolRadius = 4000.0f;
}
#endif
if (distance >= 26.0f && distance < whirlpoolRadius) {
strength = whirlpool->strength * (1.0f - distance / whirlpoolRadius);
}

View file

@ -59,7 +59,6 @@ void bhv_cannon_closed_init(void);
void cannon_door_act_opening(void);
void bhv_cannon_closed_loop(void);
void bhv_whirlpool_init(void);
void whirlpool_set_hitbox(void);
void bhv_whirlpool_loop(void);
void bhv_jet_stream_loop(void);
void bhv_homing_amp_init(void);

View file

@ -1859,22 +1859,15 @@ void cur_obj_set_face_angle_to_move_angle(void) {
o->oFaceAngleRoll = o->oMoveAngleRoll;
}
s32 cur_obj_follow_path(UNUSED s32 unusedArg) {
struct Waypoint *startWaypoint;
struct Waypoint *lastWaypoint;
struct Waypoint *targetWaypoint;
f32 prevToNextX, prevToNextY, prevToNextZ;
UNUSED u8 filler[4];
f32 objToNextXZ;
f32 objToNextX, objToNextY, objToNextZ;
s32 cur_obj_follow_path(void) {
if (o->oPathedPrevWaypointFlags == 0) {
o->oPathedPrevWaypoint = o->oPathedStartWaypoint;
o->oPathedPrevWaypointFlags = WAYPOINT_FLAGS_INITIALIZED;
}
startWaypoint = o->oPathedStartWaypoint;
lastWaypoint = o->oPathedPrevWaypoint;
struct Waypoint *startWaypoint = o->oPathedStartWaypoint;
struct Waypoint *lastWaypoint = o->oPathedPrevWaypoint;
struct Waypoint *targetWaypoint;
if ((lastWaypoint + 1)->flags != WAYPOINT_FLAGS_END) {
targetWaypoint = lastWaypoint + 1;
@ -1884,14 +1877,14 @@ s32 cur_obj_follow_path(UNUSED s32 unusedArg) {
o->oPathedPrevWaypointFlags = lastWaypoint->flags | WAYPOINT_FLAGS_INITIALIZED;
prevToNextX = targetWaypoint->pos[0] - lastWaypoint->pos[0];
prevToNextY = targetWaypoint->pos[1] - lastWaypoint->pos[1];
prevToNextZ = targetWaypoint->pos[2] - lastWaypoint->pos[2];
f32 prevToNextX = targetWaypoint->pos[0] - lastWaypoint->pos[0];
f32 prevToNextY = targetWaypoint->pos[1] - lastWaypoint->pos[1];
f32 prevToNextZ = targetWaypoint->pos[2] - lastWaypoint->pos[2];
objToNextX = targetWaypoint->pos[0] - o->oPosX;
objToNextY = targetWaypoint->pos[1] - o->oPosY;
objToNextZ = targetWaypoint->pos[2] - o->oPosZ;
objToNextXZ = sqrtf(sqr(objToNextX) + sqr(objToNextZ));
f32 objToNextX = targetWaypoint->pos[0] - o->oPosX;
f32 objToNextY = targetWaypoint->pos[1] - o->oPosY;
f32 objToNextZ = targetWaypoint->pos[2] - o->oPosZ;
f32 objToNextXZ = sqrtf(sqr(objToNextX) + sqr(objToNextZ));
o->oPathedTargetYaw = atan2s(objToNextZ, objToNextX);
o->oPathedTargetPitch = atan2s(objToNextXZ, -objToNextY);

View file

@ -190,7 +190,7 @@ void obj_set_throw_matrix_from_transform(struct Object *obj);
void obj_build_transform_relative_to_parent(struct Object *obj);
void obj_create_transform_from_self(struct Object *obj);
void cur_obj_rotate_face_angle_using_vel(void);
s32 cur_obj_follow_path(UNUSED s32 unused);
s32 cur_obj_follow_path(void);
void chain_segment_init(struct ChainSegment *segment);
f32 random_f32_around_zero(f32 diameter);
void obj_scale_random(struct Object *obj, f32 rangeLength, f32 minScale);