sm64pc/src/game/obj_behaviors_2.c

2489 lines
73 KiB
C

#include <ultra64.h>
#include "sm64.h"
#include "prevent_bss_reordering.h"
#include "behavior_actions.h"
#include "engine/behavior_script.h"
#include "camera.h"
#include "display.h"
#include "engine/math_util.h"
#include "object_helpers.h"
#include "mario_actions_cutscene.h"
#include "behavior_data.h"
#include "mario.h"
#include "engine/surface_collision.h"
#include "obj_behaviors_2.h"
#include "audio/external.h"
#include "seq_ids.h"
#include "level_update.h"
#include "memory.h"
#include "platform_displacement.h"
#include "rendering_graph_node.h"
#include "engine/surface_load.h"
#include "obj_behaviors.h"
#include "object_constants.h"
#include "interaction.h"
#include "object_list_processor.h"
#include "spawn_sound.h"
#include "geo_misc.h"
#include "save_file.h"
#include "room.h"
extern u32 wiggler_seg5_anims_0500C874[];
extern u32 spiny_egg_seg5_anims_050157E4[];
extern struct ObjectNode *gObjectLists;
extern u8 jrb_seg7_trajectory_unagi_1[];
extern u8 jrb_seg7_trajectory_unagi_2[];
extern u8 dorrie_seg6_collision_0600FBB8[];
extern u8 dorrie_seg6_collision_0600F644[];
extern u8 ssl_seg7_collision_070284B0[];
extern u8 ssl_seg7_collision_07028274[];
extern u8 ssl_seg7_collision_07028370[];
extern u8 ssl_seg7_collision_070282F8[];
extern u8 ccm_seg7_trajectory_penguin_race[];
extern u8 bob_seg7_trajectory_koopa[];
extern u8 thi_seg7_trajectory_koopa[];
extern u8 rr_seg7_collision_07029038[];
extern u8 ccm_seg7_collision_070163F8[];
extern u8 checkerboard_platform_seg8_collision_0800D710[];
extern u8 bitfs_seg7_collision_070157E0[];
extern u8 rr_seg7_trajectory_0702EC3C[];
extern u8 rr_seg7_trajectory_0702ECC0[];
extern u8 ccm_seg7_trajectory_0701669C[];
extern u8 bitfs_seg7_trajectory_070159AC[];
extern u8 hmc_seg7_trajectory_0702B86C[];
extern u8 lll_seg7_trajectory_0702856C[];
extern u8 lll_seg7_trajectory_07028660[];
extern u8 rr_seg7_trajectory_0702ED9C[];
extern u8 rr_seg7_trajectory_0702EEE0[];
extern u8 bitdw_seg7_collision_0700F70C[];
extern u8 bits_seg7_collision_0701ADD8[];
extern u8 bits_seg7_collision_0701AE5C[];
extern u8 bob_seg7_collision_bridge[];
extern u8 bitfs_seg7_collision_07015928[];
extern u8 rr_seg7_collision_07029750[];
extern u8 rr_seg7_collision_07029858[];
extern u8 vcutm_seg7_collision_0700AC44[];
extern u8 bits_seg7_collision_0701ACAC[];
extern u8 bits_seg7_collision_0701AC28[];
extern u8 bitdw_seg7_collision_0700F7F0[];
extern u8 bitdw_seg7_collision_0700F898[];
extern u8 ttc_seg7_collision_07014F70[];
extern u8 ttc_seg7_collision_07015008[];
extern u8 ttc_seg7_collision_070152B4[];
extern u8 ttc_seg7_collision_070153E0[];
extern u8 ttc_seg7_collision_07015584[];
extern u8 ttc_seg7_collision_07015650[];
extern u8 ttc_seg7_collision_07015754[];
extern u8 ttc_seg7_collision_070157D8[];
extern u8 bits_seg7_collision_0701A9A0[];
extern u8 bits_seg7_collision_0701AA0C[];
extern u8 bitfs_seg7_collision_07015714[];
extern u8 bitfs_seg7_collision_07015768[];
extern u8 rr_seg7_collision_070295F8[];
extern u8 rr_seg7_collision_0702967C[];
extern u8 bitdw_seg7_collision_0700F688[];
extern u8 bits_seg7_collision_0701AA84[];
extern u8 rr_seg7_collision_07029508[];
extern u8 bits_seg7_collision_0701B734[];
extern u8 bits_seg7_collision_0701B59C[];
extern u8 bits_seg7_collision_0701B404[];
extern u8 bits_seg7_collision_0701B26C[];
extern u8 bits_seg7_collision_0701B0D4[];
extern u8 bitdw_seg7_collision_0700FD9C[];
extern u8 bitdw_seg7_collision_0700FC7C[];
extern u8 bitdw_seg7_collision_0700FB5C[];
extern u8 bitdw_seg7_collision_0700FA3C[];
extern u8 bitdw_seg7_collision_0700F91C[];
extern u8 rr_seg7_collision_0702A6B4[];
extern u8 rr_seg7_collision_0702A32C[];
extern u8 rr_seg7_collision_07029FA4[];
extern u8 rr_seg7_collision_07029C1C[];
extern u8 rr_seg7_collision_07029924[];
extern u8 bits_seg7_collision_0701AD54[];
extern u8 bitfs_seg7_collision_070157E0[];
extern u8 bitfs_seg7_collision_07015124[];
extern u32 spiny_seg5_anims_05016EAC[];
#define POS_OP_SAVE_POSITION 0
#define POS_OP_COMPUTE_VELOCITY 1
#define POS_OP_RESTORE_POSITION 2
#define o gCurrentObject
f32 sObjSavedPosX;
f32 sObjSavedPosY;
f32 sObjSavedPosZ;
void shelled_koopa_attack_handler(s32 attackType);
void wiggler_jumped_on_attack_handler(void);
void huge_goomba_weakly_attacked(void);
static s32 obj_is_rendering_enabled(void) {
if (o->header.gfx.node.flags & GRAPH_RENDER_ACTIVE) {
return TRUE;
} else {
return FALSE;
}
}
static s16 obj_get_pitch_from_vel(void) {
return -atan2s(o->oForwardVel, o->oVelY);
}
/**
* Show dialog proposing a race.
* If the player accepts the race, then leave time stop enabled and mario in the
* text action so that the racing object can wait before starting the race.
* If the player declines the race, then disable time stop and allow mario to
* move again.
*/
static s32 obj_update_race_proposition_dialog(s16 arg0) {
s32 dialogResponse =
obj_update_dialog_unk2(2, DIALOG_UNK2_FLAG_0 | DIALOG_UNK2_LEAVE_TIME_STOP_ENABLED, 0xA3, arg0);
if (dialogResponse == 2) {
set_mario_npc_dialog(0);
disable_time_stop_including_mario();
}
return dialogResponse;
}
static void obj_set_dist_from_home(f32 distFromHome) {
o->oPosX = o->oHomeX + distFromHome * coss(o->oMoveAngleYaw);
o->oPosZ = o->oHomeZ + distFromHome * sins(o->oMoveAngleYaw);
}
static s32 obj_is_near_to_and_facing_mario(f32 maxDist, s16 maxAngleDiff) {
if (o->oDistanceToMario < maxDist
&& abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario) < maxAngleDiff) {
return TRUE;
}
return FALSE;
}
static void obj_perform_position_op(s32 op) {
switch (op) {
case POS_OP_SAVE_POSITION:
sObjSavedPosX = o->oPosX;
sObjSavedPosY = o->oPosY;
sObjSavedPosZ = o->oPosZ;
break;
case POS_OP_COMPUTE_VELOCITY:
o->oVelX = o->oPosX - sObjSavedPosX;
o->oVelY = o->oPosY - sObjSavedPosY;
o->oVelZ = o->oPosZ - sObjSavedPosZ;
break;
case POS_OP_RESTORE_POSITION:
o->oPosX = sObjSavedPosX;
o->oPosY = sObjSavedPosY;
o->oPosZ = sObjSavedPosZ;
break;
}
}
static void platform_on_track_update_pos_or_spawn_ball(s32 ballIndex, f32 x, f32 y, f32 z) {
struct Object *trackBall;
struct Waypoint *initialPrevWaypoint;
struct Waypoint *nextWaypoint;
struct Waypoint *prevWaypoint;
UNUSED s32 unused;
f32 amountToMove;
f32 dx;
f32 dy;
f32 dz;
f32 distToNextWaypoint;
if (ballIndex == 0 || ((u16)(o->oBehParams >> 16) & 0x0080)) {
initialPrevWaypoint = o->oPlatformOnTrackPrevWaypoint;
nextWaypoint = initialPrevWaypoint;
if (ballIndex != 0) {
amountToMove = 300.0f * ballIndex;
} else {
obj_perform_position_op(POS_OP_SAVE_POSITION);
o->oPlatformOnTrackPrevWaypointFlags = 0;
amountToMove = o->oForwardVel;
}
do {
prevWaypoint = nextWaypoint;
nextWaypoint += 1;
if (nextWaypoint->flags == WAYPOINT_FLAGS_END) {
if (ballIndex == 0) {
o->oPlatformOnTrackPrevWaypointFlags = WAYPOINT_FLAGS_END;
}
if (((u16)(o->oBehParams >> 16) & PLATFORM_ON_TRACK_BP_RETURN_TO_START)) {
nextWaypoint = o->oPlatformOnTrackStartWaypoint;
} else {
return;
}
}
dx = nextWaypoint->pos[0] - x;
dy = nextWaypoint->pos[1] - y;
dz = nextWaypoint->pos[2] - z;
distToNextWaypoint = sqrtf(dx * dx + dy * dy + dz * dz);
// Move directly to the next waypoint, even if it's farther away
// than amountToMove
amountToMove -= distToNextWaypoint;
x += dx;
y += dy;
z += dz;
} while (amountToMove > 0.0f);
// If we moved farther than amountToMove, move in the opposite direction
// No risk of near-zero division: If distToNextWaypoint is close to
// zero, then that means we didn't cross a waypoint this frame (since
// otherwise distToNextWaypoint would equal the distance between two
// waypoints, which should never be that small). But this implies that
// amountToMove - distToNextWaypoint <= 0, and amountToMove is at least
// 0.1 (from platform on track behavior).
distToNextWaypoint = amountToMove / distToNextWaypoint;
x += dx * distToNextWaypoint;
y += dy * distToNextWaypoint;
z += dz * distToNextWaypoint;
if (ballIndex != 0) {
trackBall = spawn_object_relative(o->oPlatformOnTrackBaseBallIndex + ballIndex, 0, 0, 0, o,
MODEL_TRAJECTORY_MARKER_BALL, bhvTrackBall);
if (trackBall != NULL) {
trackBall->oPosX = x;
trackBall->oPosY = y;
trackBall->oPosZ = z;
}
} else {
if (prevWaypoint != initialPrevWaypoint) {
if (o->oPlatformOnTrackPrevWaypointFlags == 0) {
o->oPlatformOnTrackPrevWaypointFlags = initialPrevWaypoint->flags;
}
o->oPlatformOnTrackPrevWaypoint = prevWaypoint;
}
o->oPosX = x;
o->oPosY = y;
o->oPosZ = z;
obj_perform_position_op(POS_OP_COMPUTE_VELOCITY);
o->oPlatformOnTrackPitch =
atan2s(sqrtf(o->oVelX * o->oVelX + o->oVelZ * o->oVelZ), -o->oVelY);
o->oPlatformOnTrackYaw = atan2s(o->oVelZ, o->oVelX);
}
}
}
static void func_802F8D78(f32 arg0, f32 arg1) {
f32 val24;
f32 val20;
f32 val1C;
f32 c;
f32 s;
f32 val10;
f32 val0C;
f32 val08;
f32 val04;
f32 val00;
if (o->oForwardVel == 0.0f) {
val24 = val20 = val1C = 0.0f;
if (o->oMoveFlags & OBJ_MOVE_IN_AIR) {
val20 = 50.0f;
} else {
if (o->oFaceAnglePitch < 0) {
val1C = -arg0;
} else if (o->oFaceAnglePitch > 0) {
val1C = arg0;
}
if (o->oFaceAngleRoll < 0) {
val24 = -arg1;
} else if (o->oFaceAngleRoll > 0) {
val24 = arg1;
}
}
c = coss(o->oFaceAnglePitch);
s = sins(o->oFaceAnglePitch);
val08 = val1C * c + val20 * s;
val0C = val20 * c - val1C * s;
c = coss(o->oFaceAngleRoll);
s = sins(o->oFaceAngleRoll);
val04 = val24 * c + val0C * s;
val0C = val0C * c - val24 * s;
c = coss(o->oFaceAngleYaw);
s = sins(o->oFaceAngleYaw);
val10 = val04 * c - val08 * s;
val08 = val08 * c + val04 * s;
val04 = val24 * c - val1C * s;
val00 = val1C * c + val24 * s;
o->oPosX = o->oHomeX - val04 + val10;
o->oGraphYOffset = val20 - val0C;
o->oPosZ = o->oHomeZ + val00 - val08;
}
}
static void obj_rotate_yaw_and_bounce_off_walls(s16 targetYaw, s16 turnAmount) {
if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
targetYaw = obj_reflect_move_angle_off_wall();
}
obj_rotate_yaw_toward(targetYaw, turnAmount);
}
static s16 obj_get_pitch_to_home(f32 latDistToHome) {
return atan2s(latDistToHome, o->oPosY - o->oHomeY);
}
static void obj_compute_vel_from_move_pitch(f32 speed) {
o->oForwardVel = speed * coss(o->oMoveAnglePitch);
o->oVelY = speed * -sins(o->oMoveAnglePitch);
}
static s32 clamp_s16(s16 *value, s16 minimum, s16 maximum) {
if (*value <= minimum) {
*value = minimum;
} else if (*value >= maximum) {
*value = maximum;
} else {
return FALSE;
}
return TRUE;
}
static s32 clamp_f32(f32 *value, f32 minimum, f32 maximum) {
if (*value <= minimum) {
*value = minimum;
} else if (*value >= maximum) {
*value = maximum;
} else {
return FALSE;
}
return TRUE;
}
static void func_802F927C(s32 arg0) {
set_obj_animation_and_sound_state(arg0);
func_8029F728();
}
static s32 func_802F92B0(s32 arg0) {
set_obj_animation_and_sound_state(arg0);
return func_8029F788();
}
static s32 func_802F92EC(s32 arg0, s32 arg1) {
set_obj_animation_and_sound_state(arg0);
return obj_check_anim_frame(arg1);
}
static s32 func_802F932C(s32 arg0) {
if (func_8029F828()) {
set_obj_animation_and_sound_state(arg0);
return TRUE;
}
return FALSE;
}
static s32 func_802F9378(s8 arg0, s8 arg1, u32 sound) {
s32 val04;
if ((val04 = o->header.gfx.unk38.animAccel / 0x10000) <= 0) {
val04 = 1;
}
if (obj_check_anim_frame_in_range(arg0, val04) || obj_check_anim_frame_in_range(arg1, val04)) {
PlaySound2(sound);
return TRUE;
}
return FALSE;
}
static s16 obj_turn_pitch_toward_mario(f32 targetOffsetY, s16 turnAmount) {
s16 targetPitch;
o->oPosY -= targetOffsetY;
targetPitch = obj_turn_toward_object(o, gMarioObject, O_MOVE_ANGLE_PITCH_INDEX, turnAmount);
o->oPosY += targetOffsetY;
return targetPitch;
}
static s32 approach_f32_ptr(f32 *px, f32 target, f32 delta) {
if (*px > target) {
delta = -delta;
}
*px += delta;
if ((*px - target) * delta >= 0) {
*px = target;
return TRUE;
}
return FALSE;
}
static s32 obj_forward_vel_approach(f32 target, f32 delta) {
return approach_f32_ptr(&o->oForwardVel, target, delta);
}
static s32 obj_y_vel_approach(f32 target, f32 delta) {
return approach_f32_ptr(&o->oVelY, target, delta);
}
static s32 obj_move_pitch_approach(s16 target, s16 delta) {
o->oMoveAnglePitch = approach_s16_symmetric(o->oMoveAnglePitch, target, delta);
if ((s16) o->oMoveAnglePitch == target) {
return TRUE;
}
return FALSE;
}
static s32 obj_face_pitch_approach(s16 targetPitch, s16 deltaPitch) {
o->oFaceAnglePitch = approach_s16_symmetric(o->oFaceAnglePitch, targetPitch, deltaPitch);
if ((s16) o->oFaceAnglePitch == targetPitch) {
return TRUE;
}
return FALSE;
}
static s32 obj_face_yaw_approach(s16 targetYaw, s16 deltaYaw) {
o->oFaceAngleYaw = approach_s16_symmetric(o->oFaceAngleYaw, targetYaw, deltaYaw);
if ((s16) o->oFaceAngleYaw == targetYaw) {
return TRUE;
}
return FALSE;
}
static s32 obj_face_roll_approach(s16 targetRoll, s16 deltaRoll) {
o->oFaceAngleRoll = approach_s16_symmetric(o->oFaceAngleRoll, targetRoll, deltaRoll);
if ((s16) o->oFaceAngleRoll == targetRoll) {
return TRUE;
}
return FALSE;
}
static s32 obj_smooth_turn(s16 *angleVel, s32 *angle, s16 targetAngle, f32 targetSpeedProportion,
s16 accel, s16 minSpeed, s16 maxSpeed) {
s16 currentSpeed;
s16 currentAngle = (s16)(*angle);
*angleVel =
approach_s16_symmetric(*angleVel, (targetAngle - currentAngle) * targetSpeedProportion, accel);
currentSpeed = absi(*angleVel);
clamp_s16(&currentSpeed, minSpeed, maxSpeed);
*angle = approach_s16_symmetric(*angle, targetAngle, currentSpeed);
return (s16)(*angle) == targetAngle;
}
static void obj_roll_to_match_yaw_turn(s16 targetYaw, s16 maxRoll, s16 rollSpeed) {
s16 targetRoll = o->oMoveAngleYaw - targetYaw;
clamp_s16(&targetRoll, -maxRoll, maxRoll);
obj_face_roll_approach(targetRoll, rollSpeed);
}
static s16 random_linear_offset(s16 base, s16 range) {
return base + (s16)(range * RandomFloat());
}
static s16 random_mod_offset(s16 base, s16 step, s16 mod) {
return base + step * (RandomU16() % mod);
}
static s16 obj_random_fixed_turn(s16 delta) {
return o->oMoveAngleYaw + (s16) RandomSign() * delta;
}
/**
* Begin by increasing the object's scale by *scaleVel, and slowly decreasing
* scaleVel. Once the object starts to shrink, wait a bit, and then begin to
* scale the object toward endScale. The first time it reaches below
* shootFireScale during this time, return 1.
* Return -1 once it's reached endScale.
*/
static s32 obj_grow_then_shrink(f32 *scaleVel, f32 shootFireScale, f32 endScale) {
if (o->oTimer < 2) {
o->header.gfx.scale[0] += *scaleVel;
if ((*scaleVel -= 0.01f) > -0.03f) {
o->oTimer = 0;
}
} else if (o->oTimer > 10) {
if (approach_f32_ptr(&o->header.gfx.scale[0], endScale, 0.05f)) {
return -1;
} else if (*scaleVel != 0.0f && o->header.gfx.scale[0] < shootFireScale) {
*scaleVel = 0.0f;
return 1;
}
}
return 0;
}
static s32 oscillate_toward(s32 *value, f32 *vel, s32 target, f32 velCloseToZero, f32 accel,
f32 slowdown) {
s32 startValue = *value;
*value += (s32) *vel;
if (*value == target
|| ((*value - target) * (startValue - target) < 0 && *vel > -velCloseToZero
&& *vel < velCloseToZero)) {
*value = target;
*vel = 0.0f;
return TRUE;
} else {
if (*value >= target) {
accel = -accel;
}
if (*vel * accel < 0.0f) {
accel *= slowdown;
}
*vel += accel;
}
return FALSE;
}
static void obj_update_blinking(s32 *blinkTimer, s16 baseCycleLength, s16 cycleLengthRange,
s16 blinkLength) {
if (*blinkTimer != 0) {
*blinkTimer -= 1;
} else {
*blinkTimer = random_linear_offset(baseCycleLength, cycleLengthRange);
}
if (*blinkTimer > blinkLength) {
o->oAnimState = 0;
} else {
o->oAnimState = 1;
}
}
static s32 obj_resolve_object_collisions(s32 *targetYaw) {
struct Object *otherObject;
f32 dx;
f32 dz;
s16 angle;
f32 radius;
f32 otherRadius;
f32 relativeRadius;
f32 newCenterX;
f32 newCenterZ;
if (o->numCollidedObjs != 0) {
otherObject = o->collidedObjs[0];
if (otherObject != gMarioObject) {
//! If one object moves after collisions are detected and this code
// runs, the objects can move toward each other (transport cloning)
dx = otherObject->oPosX - o->oPosX;
dz = otherObject->oPosZ - o->oPosZ;
angle = atan2s(dx, dz); //! This should be atan2s(dz, dx)
radius = o->hitboxRadius;
otherRadius = otherObject->hitboxRadius;
relativeRadius = radius / (radius + otherRadius);
newCenterX = o->oPosX + dx * relativeRadius;
newCenterZ = o->oPosZ + dz * relativeRadius;
o->oPosX = newCenterX - radius * coss(angle);
o->oPosZ = newCenterZ - radius * sins(angle);
otherObject->oPosX = newCenterX + otherRadius * coss(angle);
otherObject->oPosZ = newCenterZ + otherRadius * sins(angle);
if (targetYaw != NULL && abs_angle_diff(o->oMoveAngleYaw, angle) < 0x4000) {
// Bounce off object (or it would, if the above atan2s bug
// were fixed)
*targetYaw = (s16)(angle - o->oMoveAngleYaw + angle + 0x8000);
return TRUE;
}
}
}
return FALSE;
}
static s32 obj_bounce_off_walls_edges_objects(s32 *targetYaw) {
if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
*targetYaw = obj_reflect_move_angle_off_wall();
} else if (o->oMoveFlags & OBJ_MOVE_HIT_EDGE) {
*targetYaw = (s16)(o->oMoveAngleYaw + 0x8000);
} else if (!obj_resolve_object_collisions(targetYaw)) {
return FALSE;
}
return TRUE;
}
static s32 obj_resolve_collisions_and_turn(s16 targetYaw, s16 turnSpeed) {
obj_resolve_object_collisions(NULL);
if (obj_rotate_yaw_toward(targetYaw, turnSpeed)) {
return FALSE;
} else {
return TRUE;
}
}
static void obj_die_if_health_non_positive(void) {
if (o->oHealth <= 0) {
if (o->oDeathSound == 0) {
func_802A3034(SOUND_OBJECT_DEFAULTDEATH);
} else if (o->oDeathSound > 0) {
func_802A3034(o->oDeathSound);
} else {
func_802A3004();
}
if (o->oNumLootCoins < 0) {
spawn_object(o, MODEL_BLUE_COIN, bhvMrIBlueCoin);
} else {
spawn_object_loot_yellow_coins(o, o->oNumLootCoins, 20.0f);
}
// This doesn't do anything
spawn_object_loot_yellow_coins(o, o->oNumLootCoins, 20.0f);
if (o->oHealth < 0) {
obj_hide();
obj_become_intangible();
} else {
mark_object_for_deletion(o);
}
}
}
static void obj_unused_die(void) {
o->oHealth = 0;
obj_die_if_health_non_positive();
}
static void obj_set_knockback_action(s32 attackType) {
switch (attackType) {
case ATTACK_KICK_OR_TRIP:
case ATTACK_FAST_ATTACK:
o->oAction = OBJ_ACT_VERTICAL_KNOCKBACK;
o->oForwardVel = 20.0f;
o->oVelY = 50.0f;
break;
default:
o->oAction = OBJ_ACT_HORIZONTAL_KNOCKBACK;
o->oForwardVel = 50.0f;
o->oVelY = 30.0f;
break;
}
o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
o->oMoveAngleYaw = angle_to_object(gMarioObject, o);
}
static void obj_set_squished_action(void) {
PlaySound2(SOUND_OBJECT_STOMPED);
o->oAction = OBJ_ACT_SQUISHED;
}
static s32 obj_die_if_above_lava_and_health_non_positive(void) {
if (o->oMoveFlags & OBJ_MOVE_UNDERWATER_ON_GROUND) {
if (o->oGravity + o->oBuoyancy > 0.0f
|| find_water_level(o->oPosX, o->oPosZ) - o->oPosY < 150.0f) {
return FALSE;
}
} else if (!(o->oMoveFlags & OBJ_MOVE_ABOVE_LAVA)) {
if (o->oMoveFlags & OBJ_MOVE_ENTERED_WATER) {
if (o->oWallHitboxRadius < 200.0f) {
PlaySound2(SOUND_OBJECT_DIVINGINTOWATER);
} else {
PlaySound2(SOUND_OBJECT_DIVINGINWATER);
}
}
return FALSE;
}
obj_die_if_health_non_positive();
return TRUE;
}
static s32 obj_handle_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioAction,
u8 *attackHandlers) {
s32 attackType;
set_object_hitbox(o, hitbox);
//! Die immediately if above lava
if (obj_die_if_above_lava_and_health_non_positive()) {
return 1;
} else if (o->oInteractStatus & INT_STATUS_INTERACTED) {
if (o->oInteractStatus & INT_STATUS_ATTACKED_MARIO) {
if (o->oAction != attackedMarioAction) {
o->oAction = attackedMarioAction;
o->oTimer = 0;
}
} else {
attackType = o->oInteractStatus & INT_STATUS_ATTACK_MASK;
switch (attackHandlers[attackType - 1]) {
case ATTACK_HANDLER_NOP:
break;
case ATTACK_HANDLER_DIE_IF_HEALTH_NON_POSITIVE:
obj_die_if_health_non_positive();
break;
case ATTACK_HANDLER_KNOCKBACK:
obj_set_knockback_action(attackType);
break;
case ATTACK_HANDLER_SQUISHED:
obj_set_squished_action();
break;
case ATTACK_HANDLER_SPECIAL_KOOPA_LOSE_SHELL:
shelled_koopa_attack_handler(attackType);
break;
case ATTACK_HANDLER_SET_SPEED_TO_ZERO:
obj_set_speed_to_zero();
break;
case ATTACK_HANDLER_SPECIAL_WIGGLER_JUMPED_ON:
wiggler_jumped_on_attack_handler();
break;
case ATTACK_HANDLER_SPECIAL_HUGE_GOOMBA_WEAKLY_ATTACKED:
huge_goomba_weakly_attacked();
break;
case ATTACK_HANDLER_SQUISHED_WITH_BLUE_COIN:
o->oNumLootCoins = -1;
obj_set_squished_action();
break;
}
o->oInteractStatus = 0;
return attackType;
}
}
o->oInteractStatus = 0;
return 0;
}
static void obj_act_knockback(UNUSED f32 baseScale) {
obj_update_floor_and_walls();
if (o->header.gfx.unk38.curAnim != NULL) {
func_8029F728();
}
//! Dies immediately if above lava
if ((o->oMoveFlags
& (OBJ_MOVE_MASK_ON_GROUND | OBJ_MOVE_MASK_IN_WATER | OBJ_MOVE_HIT_WALL | OBJ_MOVE_ABOVE_LAVA))
|| (o->oAction == OBJ_ACT_VERTICAL_KNOCKBACK && o->oTimer >= 9)) {
obj_die_if_health_non_positive();
}
obj_move_standard(-78);
}
static void obj_act_squished(f32 baseScale) {
f32 targetScaleY = baseScale * 0.3f;
obj_update_floor_and_walls();
if (o->header.gfx.unk38.curAnim != NULL) {
func_8029F728();
}
if (approach_f32_ptr(&o->header.gfx.scale[1], targetScaleY, baseScale * 0.14f)) {
o->header.gfx.scale[0] = o->header.gfx.scale[2] = baseScale * 2.0f - o->header.gfx.scale[1];
if (o->oTimer >= 16) {
obj_die_if_health_non_positive();
}
}
o->oForwardVel = 0.0f;
obj_move_standard(-78);
}
static s32 obj_update_standard_actions(f32 scale) {
if (o->oAction < 100) {
return TRUE;
} else {
obj_become_intangible();
switch (o->oAction) {
case OBJ_ACT_HORIZONTAL_KNOCKBACK:
case OBJ_ACT_VERTICAL_KNOCKBACK:
obj_act_knockback(scale);
break;
case OBJ_ACT_SQUISHED:
obj_act_squished(scale);
break;
}
return FALSE;
}
}
static s32 obj_check_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioAction) {
s32 attackType;
set_object_hitbox(o, hitbox);
//! Dies immediately if above lava
if (obj_die_if_above_lava_and_health_non_positive()) {
return 1;
} else if (o->oInteractStatus & INT_STATUS_INTERACTED) {
if (o->oInteractStatus & INT_STATUS_ATTACKED_MARIO) {
if (o->oAction != attackedMarioAction) {
o->oAction = attackedMarioAction;
o->oTimer = 0;
}
} else {
attackType = o->oInteractStatus & INT_STATUS_ATTACK_MASK;
obj_die_if_health_non_positive();
o->oInteractStatus = 0;
return attackType;
}
}
o->oInteractStatus = 0;
return 0;
}
static s32 obj_move_for_one_second(s32 endAction) {
obj_update_floor_and_walls();
func_8029F728();
if (o->oTimer > 30) {
o->oAction = endAction;
return TRUE;
}
obj_move_standard(-78);
return FALSE;
}
/**
* If we are far from home (> threshold away), then set oAngleToMario to the
* angle to home and oDistanceToMario to 25000.
* If we are close to home, but Mario is far from us (> threshold away), then
* keep oAngleToMario the same and set oDistanceToMario to 20000.
* If we are close to both home and Mario, then keep both oAngleToMario and
* oDistanceToMario the same.
*
* The point of this function is to avoid having to write extra code to get
* the object to return to home. When mario is far away and the object is far
* from home, it could theoretically re-use the "approach mario" logic to approach
* its home instead.
* However, most objects that use this function handle the far-from-home case
* separately anyway.
* This function causes seemingly erroneous behavior in some objects that try to
* attack mario (e.g. fly guy shooting fire or lunging), especially when combined
* with partial updates.
*/
static void treat_far_home_as_mario(f32 threshold) {
f32 dx = o->oHomeX - o->oPosX;
f32 dy = o->oHomeY - o->oPosY;
f32 dz = o->oHomeZ - o->oPosZ;
f32 distance = sqrtf(dx * dx + dy * dy + dz * dz);
if (distance > threshold) {
o->oAngleToMario = atan2s(dz, dx);
o->oDistanceToMario = 25000.0f;
} else {
dx = o->oHomeX - gMarioObject->oPosX;
dy = o->oHomeY - gMarioObject->oPosY;
dz = o->oHomeZ - gMarioObject->oPosZ;
distance = sqrtf(dx * dx + dy * dy + dz * dz);
if (distance > threshold) {
o->oDistanceToMario = 20000.0f;
}
}
}
#include "behaviors/koopa.inc.c" // TODO: Text arg field name
#include "behaviors/pokey.inc.c"
#include "behaviors/swoop.inc.c"
#include "behaviors/fly_guy.inc.c"
#include "behaviors/goomba.inc.c"
#include "behaviors/chain_chomp.inc.c" // TODO: chain_chomp_sub_act_lunge documentation
#include "behaviors/wiggler.inc.c" // TODO
#include "behaviors/spiny.inc.c"
#include "behaviors/enemy_lakitu.inc.c" // TODO
#include "behaviors/cloud.inc.c"
#include "behaviors/camera_lakitu.inc.c" // TODO: 104 label, follow cam documentation
#include "behaviors/monty_mole.inc.c" // TODO
#include "behaviors/platform_on_track.inc.c"
#include "behaviors/seesaw_platform.inc.c"
#include "behaviors/ferris_wheel.inc.c"
#include "behaviors/water_bomb.inc.c" // TODO: Shadow position
#include "behaviors/ttc_rotating_solid.inc.c"
#include "behaviors/ttc_pendulum.inc.c"
#include "behaviors/ttc_treadmill.inc.c" // TODO
#include "behaviors/ttc_moving_bar.inc.c"
#include "behaviors/ttc_cog.inc.c"
#include "behaviors/ttc_pit_block.inc.c"
#include "behaviors/ttc_elevator.inc.c"
#include "behaviors/ttc_2d_rotator.inc.c"
#include "behaviors/ttc_spinner.inc.c"
// Finished included files up to here
struct Struct80331A54 {
void *unk00;
s16 unk04;
};
struct Struct80331B30 {
s16 unk00;
s16 unk02;
};
struct RacingPenguinData {
s16 text;
f32 radius;
f32 height;
};
struct Struct80331C00 {
s16 unk00;
s16 unk02;
};
struct Struct80331C38 {
s16 unk00;
s16 unk02;
};
struct Struct80331C48 {
s16 unk00;
s16 unk02;
s16 unk04;
};
struct TripletButterflyActivationData {
s32 model;
void *behavior;
f32 scale;
};
struct Struct80331874 {
s8 unk01;
s8 unk03;
s8 unk04;
s8 unk05;
s8 unk06;
s8 unk0C;
u8 filler00[0x14 - 0x00];
};
// TODO: Finish
#include "behaviors/mr_blizzard.inc.c"
void *D_80331A24[] = {
bits_seg7_collision_0701A9A0,
bits_seg7_collision_0701AA0C,
bitfs_seg7_collision_07015714,
bitfs_seg7_collision_07015768,
rr_seg7_collision_070295F8,
rr_seg7_collision_0702967C,
NULL,
bitdw_seg7_collision_0700F688,
};
void BehSlidingPlatform2Init(void) {
s32 val04;
val04 = ((u16)(o->oBehParams >> 16) & 0x0380) >> 7;
o->collisionData = segmented_to_virtual(D_80331A24[val04]);
o->oBackAndForthPlatformUnkF8 = 50.0f * ((u16)(o->oBehParams >> 16) & 0x003F);
if (val04 < 5 || val04 > 6) {
o->oBackAndForthPlatformUnk100 = 15.0f;
if ((u16)(o->oBehParams >> 16) & 0x0040) {
o->oMoveAngleYaw += 0x8000;
}
} else {
o->oBackAndForthPlatformUnk100 = 10.0f;
if ((u16)(o->oBehParams >> 16) & 0x0040) {
o->oBackAndForthPlatformUnkF4 = -1.0f;
} else {
o->oBackAndForthPlatformUnkF4 = 1.0f;
}
}
}
void BehSlidingPlatform2Loop(void) {
if (o->oTimer > 10) {
o->oBackAndForthPlatformUnkFC += o->oBackAndForthPlatformUnk100;
if (clamp_f32(&o->oBackAndForthPlatformUnkFC, -o->oBackAndForthPlatformUnkF8, 0.0f)) {
o->oBackAndForthPlatformUnk100 = -o->oBackAndForthPlatformUnk100;
o->oTimer = 0;
}
}
obj_perform_position_op(0);
if (o->oBackAndForthPlatformUnkF4 != 0.0f) {
o->oPosY = o->oHomeY + o->oBackAndForthPlatformUnkFC * o->oBackAndForthPlatformUnkF4;
} else {
obj_set_dist_from_home(o->oBackAndForthPlatformUnkFC);
}
obj_perform_position_op(1);
}
void *D_80331A44[] = {
bits_seg7_collision_0701AA84,
rr_seg7_collision_07029508,
};
s16 D_80331A4C[] = { 300, -300, 600, -600 };
void BehOctagonalPlatformRotatingInit(void) {
o->collisionData = segmented_to_virtual(D_80331A44[(u8)(o->oBehParams >> 16)]);
o->oAngleVelYaw = D_80331A4C[(u8)(o->oBehParams >> 24)];
}
void BehOctagonalPlatformRotatingLoop(void) {
o->oFaceAngleYaw += o->oAngleVelYaw;
}
struct Struct80331A54 D_80331A54[][5] = {
{
{ bits_seg7_collision_0701B734, MODEL_BITS_STAIRCASE_FRAME4 },
{ bits_seg7_collision_0701B59C, MODEL_BITS_STAIRCASE_FRAME3 },
{ bits_seg7_collision_0701B404, MODEL_BITS_STAIRCASE_FRAME2 },
{ bits_seg7_collision_0701B26C, MODEL_BITS_STAIRCASE_FRAME1 },
{ bits_seg7_collision_0701B0D4, MODEL_BITS_STAIRCASE },
},
{
{ bitdw_seg7_collision_0700FD9C, MODEL_BITDW_STAIRCASE },
{ bitdw_seg7_collision_0700FC7C, MODEL_BITDW_STAIRCASE_FRAME1 },
{ bitdw_seg7_collision_0700FB5C, MODEL_BITDW_STAIRCASE_FRAME2 },
{ bitdw_seg7_collision_0700FA3C, MODEL_BITDW_STAIRCASE_FRAME3 },
{ bitdw_seg7_collision_0700F91C, MODEL_BITDW_STAIRCASE_FRAME4 },
},
{
{ rr_seg7_collision_0702A6B4, MODEL_RR_TRICKY_TRIANGLES_FRAME4 },
{ rr_seg7_collision_0702A32C, MODEL_RR_TRICKY_TRIANGLES_FRAME3 },
{ rr_seg7_collision_07029FA4, MODEL_RR_TRICKY_TRIANGLES_FRAME2 },
{ rr_seg7_collision_07029C1C, MODEL_RR_TRICKY_TRIANGLES_FRAME1 },
{ rr_seg7_collision_07029924, MODEL_RR_TRICKY_TRIANGLES },
},
};
s16 D_80331ACC[] = { 250, 200, 200 };
void BehAnimatesOnFloorSwitchPressInit(void) {
o->parentObj = obj_nearest_object_with_behavior(bhvFloorSwitchAnimatesObject);
}
void BehAnimatesOnFloorSwitchPressLoop(void) {
if (o->oFloorSwitchPressAnimationUnk100 != 0) {
if (o->parentObj->oAction != 2) {
o->oFloorSwitchPressAnimationUnk100 = 0;
}
if (o->oFloorSwitchPressAnimationUnkFC != 0) {
o->oFloorSwitchPressAnimationUnkF4 = D_80331ACC[o->oBehParams2ndByte];
} else {
o->oFloorSwitchPressAnimationUnkF4 = 0;
}
} else if (o->parentObj->oAction == 2) {
o->oFloorSwitchPressAnimationUnkFC ^= 1;
o->oFloorSwitchPressAnimationUnk100 = 1;
}
if (o->oFloorSwitchPressAnimationUnkF4 != 0) {
if (o->oFloorSwitchPressAnimationUnkF4 < 60) {
PlaySound(SOUND_CH8_SWITCH6);
} else {
PlaySound(SOUND_CH8_SWITCH5);
}
if (--o->oFloorSwitchPressAnimationUnkF4 == 0) {
o->oFloorSwitchPressAnimationUnkFC = 0;
}
if (o->oFloorSwitchPressAnimationUnkF8 < 9) {
o->oFloorSwitchPressAnimationUnkF8 += 1;
}
} else if ((o->oFloorSwitchPressAnimationUnkF8 -= 2) < 0) {
o->oFloorSwitchPressAnimationUnkF8 = 0;
o->oFloorSwitchPressAnimationUnkFC = 1;
}
o->collisionData = segmented_to_virtual(
D_80331A54[o->oBehParams2ndByte][o->oFloorSwitchPressAnimationUnkF8 / 2].unk00);
obj_set_model(D_80331A54[o->oBehParams2ndByte][o->oFloorSwitchPressAnimationUnkF8 / 2].unk04);
}
#include "behaviors/activated_bf_plat.inc.c"
#include "behaviors/recovery_heart.inc.c"
void BehCannonBarrelBubblesLoop(void) {
struct Object *val04;
if (o->parentObj->oAction == 2) {
mark_object_for_deletion(o);
} else {
o->oMoveAngleYaw = o->parentObj->oFaceAngleYaw;
o->oMoveAnglePitch = o->parentObj->oMoveAnglePitch + 0x4000;
o->oFaceAnglePitch = o->parentObj->oMoveAnglePitch;
if ((o->oCannonBarrelBubblesUnkF4 += o->oForwardVel) > 0.0f) {
func_802A2A38();
obj_forward_vel_approach(-5.0f, 18.0f);
} else {
o->oCannonBarrelBubblesUnkF4 = 0.0f;
copy_object_pos(o, o->parentObj);
// check this
if (o->parentObj->oCannonUnkF4 != 0) {
if (o->oForwardVel == 0.0f) {
o->oForwardVel = 35.0f;
val04 = spawn_object(o, MODEL_WATER_BOMB, bhvWaterBomb);
if (val04 != NULL) {
val04->oForwardVel = -100.0f;
val04->header.gfx.scale[1] = 1.7f;
}
func_8027F440(2, o->oPosX, o->oPosY, o->oPosZ);
}
} else {
o->oForwardVel = 0.0f;
}
}
}
}
void func_80308DF0(void) {
if (o->oDistanceToMario < 2000.0f) {
spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles);
obj_unhide();
o->oAction = 1;
o->oMoveAnglePitch = o->oCannonUnkFC = 0x1C00;
}
}
void func_80308E84(void) {
if (o->oDistanceToMario > 2500.0f) {
o->oAction = 2;
} else if (o->oBehParams2ndByte == 0) {
if (o->oCannonUnkF4 != 0) {
o->oCannonUnkF4 -= 1;
} else {
obj_move_pitch_approach(o->oCannonUnkFC, 0x80);
obj_face_yaw_approach(o->oCannonUnk100, 0x100);
if ((s16) o->oFaceAngleYaw == (s16) o->oCannonUnk100) {
if (o->oCannonUnkF8 != 0) {
o->oCannonUnkF8 -= 1;
} else {
PlaySound2(SOUND_OBJECT_CANNON4);
o->oCannonUnkF4 = 70;
o->oCannonUnkFC = 0x1000 + 0x400 * (RandomU16() & 0x3);
o->oCannonUnk100 = -0x2000 + o->oMoveAngleYaw + 0x1000 * (RandomU16() % 5);
o->oCannonUnkF8 = 60;
}
}
}
}
}
void func_80309004(void) {
obj_hide();
o->oAction = 0;
}
void BehWaterBombCannonLoop(void) {
obj_push_mario_away_from_cylinder(220.0f, 300.0f);
switch (o->oAction) {
case 0:
func_80308DF0();
break;
case 1:
func_80308E84();
break;
case 2:
func_80309004();
break;
}
}
struct ObjectHitbox sUnagiHitbox = {
/* interactType: */ INTERACT_CLAM_OR_BUBBA,
/* downOffset: */ 50,
/* damageOrCoinValue: */ 3,
/* health: */ 99,
/* numLootCoins: */ 0,
/* radius: */ 150,
/* height: */ 150,
/* hurtboxRadius: */ 150,
/* hurtboxHeight: */ 150,
};
void BehUnagiInit(void) {
if (o->oBehParams2ndByte != 1) {
o->oUnagiUnkFC = segmented_to_virtual(jrb_seg7_trajectory_unagi_1);
if (o->oBehParams2ndByte == 0) {
o->oFaceAnglePitch = -7600;
} else {
o->oAction = 1;
}
} else {
o->oUnagiUnkFC = segmented_to_virtual(jrb_seg7_trajectory_unagi_2);
o->oAction = 3;
o->oAnimState = 1;
o->oUnagiUnk1B0 = o->oMoveAngleYaw;
}
o->oUnagiUnk100 = o->oUnagiUnkFC;
}
void func_803091C4(void) {
if (o->oDistanceToMario > 4500.0f && o->oSubAction != 0) {
o->oAction = 1;
o->oPosX = o->oUnagiUnkFC[1];
o->oPosY = o->oUnagiUnkFC[2];
o->oPosZ = o->oUnagiUnkFC[3];
} else if (o->oUnagiUnk1AC < 700.0f) {
o->oSubAction = 1;
}
}
void func_803092AC(s32 arg0) {
if (o->oSoundStateID == 3) {
if (obj_check_anim_frame(30)) {
o->oForwardVel = 40.0f;
}
} else {
if (func_8029F828()) {
if (o->oAction != arg0 && (o->oUnagiUnk104 & 0xFF) >= 7) {
set_obj_animation_and_sound_state(3);
} else {
set_obj_animation_and_sound_state(2);
}
}
}
if (obj_check_anim_frame(6)) {
PlaySound2(SOUND_GENERAL_MOVINGWATER);
}
if (obj_follow_path(0) == -1) {
o->oAction = arg0;
}
o->oMoveAnglePitch = o->oFaceAnglePitch =
approach_s16_symmetric(o->oMoveAnglePitch, o->oUnagiUnk108, 50);
obj_rotate_yaw_toward(o->oUnagiUnk10C, 120);
obj_roll_to_match_yaw_turn(o->oUnagiUnk10C, 0x2000, 100);
obj_forward_vel_approach(10.0f, 0.2f);
func_802A2A38();
}
void func_80309430(void) {
o->oUnagiUnk100 = o->oUnagiUnkFC;
o->oUnagiUnk104 = 0;
obj_set_pos_to_home();
o->oMoveAnglePitch = o->oFaceAnglePitch = 0;
o->oMoveAngleYaw = o->oFaceAngleYaw = o->oUnagiUnk1B0;
o->oFaceAngleRoll = 0;
o->oForwardVel = o->oVelX = o->oVelZ = o->oUnagiUnkF8 = 0.0f;
o->oUnagiUnkF4 = -800.0f;
o->oAction = 3;
}
void func_80309530(void) {
if (o->oUnagiUnkF4 < 0.0f) {
set_obj_animation_and_sound_state(6);
if ((o->oUnagiUnkF4 += 10.0f) > 0.0f) {
o->oUnagiUnkF4 = 0.0f;
}
} else {
if (o->oUnagiUnkF4 == 0.0f) {
set_obj_animation_and_sound_state(6);
if (o->oTimer > 60 && o->oUnagiUnk1AC < 1000.0f) {
PlaySound2(SOUND_OBJECT_EEL_2);
o->oUnagiUnkF8 = o->oUnagiUnk110 = 30.0f;
} else {
o->oUnagiUnk110 = 0.0f;
}
} else if (o->oUnagiUnk110 > 0.0f) {
if (func_802F92B0(5)) {
o->oUnagiUnk110 = 0.0f;
}
} else if (o->oUnagiUnk110 == 0.0f) {
set_obj_animation_and_sound_state(0);
if (func_8029F828()) {
if (o->oUnagiUnk1AC < 1000.0f) {
o->oAction = 4;
o->oForwardVel = o->oUnagiUnkF8;
set_obj_animation_and_sound_state(1);
} else {
o->oUnagiUnk110 = -50.0f;
set_obj_animation_and_sound_state(4);
}
}
}
approach_f32_ptr(&o->oUnagiUnkF8, o->oUnagiUnk110, 4.0f);
if ((o->oUnagiUnkF4 += o->oUnagiUnkF8) < 0.0f) {
o->oUnagiUnkF4 = o->oUnagiUnkF8 = 0.0f;
o->oTimer = 0;
}
}
o->oPosX = o->oHomeX + o->oUnagiUnkF4 * sins(o->oMoveAngleYaw);
o->oPosZ = o->oHomeZ + o->oUnagiUnkF4 * coss(o->oMoveAngleYaw);
}
void BehUnagiLoop(void) {
s32 val04;
if (o->oUnagiUnk1B2 == 0) {
o->oUnagiUnk1AC = 99999.0f;
if (o->oDistanceToMario < 3000.0f) {
for (val04 = -4; val04 < 4; val04++) {
spawn_object_relative(val04, 0, 0, 0, o, MODEL_NONE, bhvUnagiSubobject);
}
o->oUnagiUnk1B2 = 1;
}
} else if (o->oDistanceToMario > 4000.0f) {
o->oUnagiUnk1B2 = 0;
}
switch (o->oAction) {
case 0:
func_803091C4();
break;
case 1:
func_803092AC(o->oAction);
break;
case 2:
func_80309430();
case 3:
func_80309530();
break;
case 4:
func_803092AC(2);
break;
}
}
void BehUnagiSubobjectLoop(void) {
f32 val04;
if (o->parentObj->oUnagiUnk1B2 == 0) {
mark_object_for_deletion(o);
} else {
val04 = 300.0f * o->oBehParams2ndByte;
o->oPosY = o->parentObj->oPosY - val04 * sins(o->parentObj->oFaceAnglePitch) * 1.13f;
val04 = coss(o->parentObj->oFaceAnglePitch / 2) * val04;
o->oPosX = o->parentObj->oPosX + val04 * sins(o->parentObj->oFaceAngleYaw);
o->oPosZ = o->parentObj->oPosZ + val04 * coss(o->parentObj->oFaceAngleYaw);
if (o->oBehParams2ndByte == -4) {
if (o->parentObj->oAnimState != 0 && o->oDistanceToMario < 150.0f) {
o->oBehParams = o->parentObj->oBehParams;
CreateStar(6833.0f, -3654.0f, 2230.0f);
o->parentObj->oAnimState = 0;
}
} else {
obj_check_attacks(&sUnagiHitbox, o->oAction);
if (o->oBehParams2ndByte == 3) {
o->parentObj->oUnagiUnk1AC = o->oDistanceToMario;
}
}
}
}
#include "behaviors/dorrie.inc.c"
struct ObjectHitbox sHauntedChairHitbox = {
/* interactType: */ INTERACT_MR_BLIZZARD,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 2,
/* health: */ 0,
/* numLootCoins: */ 0,
/* radius: */ 50,
/* height: */ 50,
/* hurtboxRadius: */ 50,
/* hurtboxHeight: */ 50,
};
void BehHauntedChairInit(void) {
struct Object *val04;
f32 val00;
val04 = obj_find_nearest_object_with_behavior(bhvMadPiano, &val00);
if (val04 != NULL && val00 < 300.0f) {
o->parentObj = val04;
} else {
o->oHauntedChairUnkF4 = 1;
}
}
void func_8030A5D8(void) {
s16 val0E;
f32 val08;
if (o->parentObj != o) {
if (o->oHauntedChairUnk104 == 0) {
if (lateral_dist_between_objects(o, o->parentObj) < 250.0f) {
val0E = angle_to_object(o, o->parentObj) - o->oFaceAngleYaw + 0x2000;
if (val0E & 0x4000) {
o->oHauntedChairUnk100 = &o->oFaceAngleRoll;
if (val0E > 0) {
o->oHauntedChairUnk104 = 0x4000;
} else {
o->oHauntedChairUnk104 = -0x4000;
}
} else {
o->oHauntedChairUnk100 = &o->oFaceAnglePitch;
if (val0E < 0) {
o->oHauntedChairUnk104 = 0x5000;
} else {
o->oHauntedChairUnk104 = -0x4000;
}
}
if (o->oHauntedChairUnk104 < 0) {
o->oHauntedChairUnkF8 = -1500.0f;
} else {
o->oHauntedChairUnkF8 = 1500.0f;
}
}
} else {
oscillate_toward(o->oHauntedChairUnk100, &o->oHauntedChairUnkF8, o->oHauntedChairUnk104,
4000.0f, 20.0f, 2.0f);
}
} else if (o->oHauntedChairUnkF4 != 0) {
if (o->oDistanceToMario < 500.0f) {
o->oHauntedChairUnkF4 -= 1;
}
o->oTimer = 0.0f;
} else {
if ((o->oTimer & 0x8) != 0) {
if (o->oFaceAnglePitch < 0) {
PlaySound2(SOUND_GENERAL_MOVINGBOOMAYBE);
val08 = 4.0f;
} else {
val08 = -4.0f;
}
o->oHomeX -= val08;
o->oHomeZ -= val08;
o->oFaceAnglePitch = o->oFaceAngleRoll = (s32)(50.0f * val08);
;
} else {
o->oFaceAnglePitch = o->oFaceAngleRoll = 0;
}
if (o->oTimer > 30) {
o->oAction = 1;
o->oHauntedChairUnkF8 = 0.0f;
o->oHauntedChairUnkFC = 200.0f;
o->oHauntedChairUnkF4 = 40;
}
}
obj_push_mario_away_from_cylinder(80.0f, 120.0f);
}
void func_8030A968(void) {
obj_update_floor_and_walls();
if (o->oTimer < 70) {
if (o->oTimer < 50) {
o->oVelY = 6.0f;
} else {
o->oVelY = 0.0f;
}
o->oGravity = 0.0f;
oscillate_toward(&o->oFaceAnglePitch, &o->oHauntedChairUnkF8, -4000, 200.0f, 20.0f, 2.0f);
oscillate_toward(&o->oFaceAngleRoll, &o->oHauntedChairUnkFC, 0, 0.0f, 20.0f, 1.0f);
} else {
if (o->oHauntedChairUnkF4 != 0) {
if (--o->oHauntedChairUnkF4 == 0) {
PlaySound2(SOUND_GENERAL_HAUNTEDCHAIR);
o->oMoveAnglePitch = obj_turn_pitch_toward_mario(120.0f, 0);
o->oMoveAngleYaw = o->oAngleToMario;
obj_compute_vel_from_move_pitch(50.0f);
} else if (o->oHauntedChairUnkF4 > 20) {
if (gGlobalTimer % 4 == 0) {
PlaySound2(SOUND_GENERAL_SWISHAIR_2);
}
o->oFaceAngleYaw += 0x2710;
}
} else if (o->oMoveFlags & 0x00000203) {
obj_die_if_health_non_positive();
}
}
obj_check_attacks(&sHauntedChairHitbox, o->oAction);
obj_move_standard(78);
}
void BehHauntedChairLoop(void) {
if (!(o->activeFlags & 0x0008)) {
switch (o->oAction) {
case 0:
func_8030A5D8();
break;
case 1:
func_8030A968();
break;
}
func_802F8D78(30.0f, 30.0f);
}
}
#include "behaviors/mad_piano.inc.c"
struct ObjectHitbox sFlyingBookendHitbox = {
/* interactType: */ INTERACT_HIT_FROM_BELOW,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 2,
/* health: */ 0,
/* numLootCoins: */ -1,
/* radius: */ 60,
/* height: */ 30,
/* hurtboxRadius: */ 40,
/* hurtboxHeight: */ 30,
};
struct Struct80331B30 D_80331B30[] = {
{ 52, 150 },
{ 135, 3 },
{ -75, 78 },
};
struct ObjectHitbox sBookSwitchHitbox = {
/* interactType: */ INTERACT_BREAKABLE,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 0,
/* health: */ 99,
/* numLootCoins: */ 0,
/* radius: */ 20,
/* height: */ 30,
/* hurtboxRadius: */ 20,
/* hurtboxHeight: */ 30,
};
void func_8030AF6C(void) {
if (obj_is_near_to_and_facing_mario(400.0f, 0x3000)) {
PlaySound2(SOUND_OBJECT_DEFAULTDEATH);
o->oAction = 1;
o->oBookendUnkF4 = o->oFaceAnglePitch + 0x7FFF;
o->oBookendUnkF8 = o->oFaceAngleRoll - 0x7FFF;
obj_set_model(MODEL_BOOKEND_PART);
}
}
void func_8030AFF0(void) {
if (obj_forward_vel_approach(3.0f, 1.0f)) {
if (func_802F92B0(2)) {
o->oAction = 2;
o->oForwardVel = 0.0f;
} else {
o->oForwardVel = 3.0f;
if (o->oTimer > 5) {
obj_face_pitch_approach(o->oBookendUnkF4, 2000);
if (o->oTimer >= 10) {
obj_face_roll_approach(o->oBookendUnkF8, 2000);
if (o->oTimer >= 20) {
approach_f32_ptr(&o->header.gfx.scale[0], 3.0f, 0.2f);
}
}
}
}
}
obj_move_using_fvel_and_gravity();
}
void func_8030B110(void) {
set_obj_animation_and_sound_state(1);
obj_update_floor_and_walls();
if (o->oForwardVel == 0.0f) {
obj_turn_pitch_toward_mario(120.0f, 1000);
o->oFaceAnglePitch = o->oMoveAnglePitch + 0x7FFF;
obj_rotate_yaw_toward(o->oAngleToMario, 1000);
if (o->oTimer > 30) {
obj_compute_vel_from_move_pitch(50.0f);
}
}
obj_move_standard(78);
}
void func_8030B1C8(void) {
o->oDamageOrCoinValue = 1;
o->oNumLootCoins = 0;
if (o->oTimer >= 4) {
o->oAction = 2;
o->oForwardVel = 50.0f;
}
obj_forward_vel_approach(50.0f, 2.0f);
obj_move_using_fvel_and_gravity();
}
void BehFlyingBookendLoop(void) {
if (!(o->activeFlags & 0x0008)) {
o->oDeathSound = SOUND_OBJECT_POUNDING1;
obj_scale(o->header.gfx.scale[0]);
switch (o->oAction) {
case 0:
func_8030AF6C();
break;
case 1:
func_8030AFF0();
break;
case 2:
func_8030B110();
break;
case 3:
func_8030B1C8();
break;
}
obj_check_attacks(&sFlyingBookendHitbox, -1);
if (o->oAction == -1 || (o->oMoveFlags & 0x00000203)) {
o->oNumLootCoins = 0;
obj_die_if_health_non_positive();
}
o->oGraphYOffset = 30.0f * o->header.gfx.scale[0];
}
}
void BehBookendSpawnLoop(void) {
struct Object *sp1C;
if (!(o->activeFlags & 0x0008)) {
if (o->oTimer > 40 && obj_is_near_to_and_facing_mario(600.0f, 0x2000)) {
sp1C = spawn_object(o, MODEL_BOOKEND, bhvFlyingBookend);
if (sp1C != NULL) {
sp1C->oAction = 3;
PlaySound2(SOUND_OBJECT_DEFAULTDEATH);
}
o->oTimer = 0;
}
}
}
void func_8030B464(void) {
s32 val04;
if (!(o->activeFlags & 0x0008)) {
for (val04 = 0; val04 < 3; val04++) {
spawn_object_relative(val04, D_80331B30[val04].unk00, D_80331B30[val04].unk02, 0, o,
MODEL_BOOKEND, bhvBookSwitch);
}
o->oAction = 1;
}
}
void func_8030B50C(void) {
if (o->oBookSwitchManagerUnkF8 == 0) {
if (obj_is_near_to_and_facing_mario(500.0f, 0x3000)) {
o->oBookSwitchManagerUnkF8 = 1;
}
} else if (o->oTimer > 60) {
o->oAction = 2;
o->oBookSwitchManagerUnkF8 = 0;
}
}
void func_8030B5A4(void) {
if (!(o->activeFlags & 0x0008)) {
if (o->oBookSwitchManagerUnkF4 < 0) {
if (o->oTimer > 30) {
o->oBookSwitchManagerUnkF4 = o->oBookSwitchManagerUnkF8 = 0;
} else if (o->oTimer > 10) {
o->oBookSwitchManagerUnkF8 = 1;
}
} else {
if (o->oBookSwitchManagerUnkF4 >= 3) {
if (o->oTimer > 100) {
o->parentObj = obj_nearest_object_with_behavior(bhvHauntedBookshelf);
o->parentObj->oAction = 1;
o->oPosX = o->parentObj->oPosX;
o->oAction = 3;
} else if (o->oTimer == 30) {
play_puzzle_jingle();
}
} else {
o->oTimer = 0;
}
}
} else {
o->oAction = 4;
}
}
void func_8030B728(void) {
if (o->oTimer > 85) {
o->oAction = 4;
} else {
o->oForwardVel = o->parentObj->oPosX - o->oPosX;
o->oPosX = o->parentObj->oPosX;
}
}
void func_8030B794(void) {
if (o->oBookSwitchManagerUnkF4 >= 3) {
mark_object_for_deletion(o);
} else {
o->oAction = 0;
}
}
void bhv_haunted_bookshelf_manager_loop(void) {
switch (o->oAction) {
case 0:
func_8030B464();
break;
case 2:
func_8030B5A4();
break;
case 1:
func_8030B50C();
break;
case 3:
func_8030B728();
break;
case 4:
func_8030B794();
break;
}
}
void bhv_book_switch_loop(void) {
s32 sp3C;
struct Object *sp38;
s16 sp36;
s16 sp34;
o->header.gfx.scale[0] = 2.0f;
o->header.gfx.scale[1] = 0.9f;
if (o->parentObj->oAction == 4) {
mark_object_for_deletion(o);
} else {
sp3C = obj_check_attacks(&sBookSwitchHitbox, o->oAction);
if (o->parentObj->oBookSwitchManagerUnkF8 != 0 || o->oAction == 1) {
if (o->oDistanceToMario < 100.0f) {
obj_become_tangible();
} else {
obj_become_intangible();
}
o->oAction = 1;
if (o->oBookSwitchUnkF4 == 0.0f) {
PlaySound2(SOUND_OBJECT_DEFAULTDEATH);
}
if (approach_f32_ptr(&o->oBookSwitchUnkF4, 50.0f, 20.0f)) {
if (o->parentObj->oBookSwitchManagerUnkF4 >= 0 && o->oTimer > 60) {
if (sp3C == 1 || sp3C == 2 || sp3C == 6) {
o->oAction = 2;
}
}
} else {
o->oTimer = 0;
}
} else {
obj_become_intangible();
if (approach_f32_ptr(&o->oBookSwitchUnkF4, 0.0f, 20.0f)) {
if (o->oAction != 0) {
if (o->parentObj->oBookSwitchManagerUnkF4 == o->oBehParams2ndByte) {
play_sound(SOUND_CH8_RIGHTANSWER, gDefaultSoundArgs);
o->parentObj->oBookSwitchManagerUnkF4 += 1;
} else {
sp36 = RandomU16() & 0x1;
sp34 = gMarioObject->oPosZ + 1.5f * gMarioStates[0].vel[2];
play_sound(SOUND_MENU_CAMERABUZZ, gDefaultSoundArgs);
if (sp34 > 0) {
sp34 = 0;
}
sp38 = spawn_object_abs_with_rot(o, 0, MODEL_BOOKEND, bhvFlyingBookend,
0x1FC * sp36 - 0x8CA, 890, sp34, 0,
0x8000 * sp36 + 0x4000, 0);
if (sp38 != NULL) {
sp38->oAction = 3;
}
o->parentObj->oBookSwitchManagerUnkF4 = -1;
}
o->oAction = 0;
}
}
}
o->oPosX += o->parentObj->oForwardVel;
o->oPosZ = o->oHomeZ - o->oBookSwitchUnkF4;
obj_push_mario_away_from_cylinder(70.0f, 70.0f);
}
}
void obj_spit_fire(s16 relativePosX, s16 relativePosY, s16 relativePosZ, f32 scale, s32 model,
f32 startSpeed, f32 endSpeed, s16 movePitch) {
struct Object *sp2C;
sp2C = spawn_object_relative_with_scale(1, relativePosX, relativePosY, relativePosZ, scale, o,
model, bhvSmallPiranhaFlame);
if (sp2C != NULL) {
sp2C->oSmallPiranhaFlameUnkF4 = startSpeed;
sp2C->oSmallPiranhaFlameUnkF8 = endSpeed;
sp2C->oSmallPiranhaFlameUnkFC = model;
sp2C->oMoveAnglePitch = movePitch;
}
}
#include "behaviors/fire_piranha_plant.inc.c"
#include "behaviors/fire_spitter.inc.c"
void BehSmallPiranhaFlameLoop(void) {
f32 sp2C;
if ((u16)(o->oBehParams >> 16) == 0) {
if (o->oTimer > 0) {
mark_object_for_deletion(o);
} else {
sp2C = RandomFloat() - 0.5f;
o->header.gfx.scale[1] = o->header.gfx.scale[2] * (1.0f + 0.7f * sp2C);
o->header.gfx.scale[0] = o->header.gfx.scale[2] * (0.9f - 0.5f * sp2C);
o->oAnimState = RandomU16();
}
} else {
obj_update_floor_and_walls();
if (approach_f32_ptr(&o->oSmallPiranhaFlameUnkF4, o->oSmallPiranhaFlameUnkF8, 0.6f)) {
obj_rotate_yaw_toward(o->oAngleToMario, 0x200);
}
obj_compute_vel_from_move_pitch(o->oSmallPiranhaFlameUnkF4);
obj_move_standard(-78);
spawn_object_with_scale(o, o->oSmallPiranhaFlameUnkFC, bhvSmallPiranhaFlame,
0.4f * o->header.gfx.scale[0]);
if (o->oTimer > o->oSmallPiranhaFlameUnk100) {
spawn_object_relative_with_scale(1, 0, o->oGraphYOffset, 0, o->header.gfx.scale[0], o,
o->oSmallPiranhaFlameUnkFC, bhvFlyguyFlame);
o->oSmallPiranhaFlameUnk100 = random_linear_offset(8, 15);
o->oTimer = 0;
}
obj_check_attacks(&sPiranhaPlantFireHitbox, o->oAction);
o->oSmallPiranhaFlameUnk104 += o->oSmallPiranhaFlameUnkF4;
if (o->oSmallPiranhaFlameUnk104 > 1500.0f || (o->oMoveFlags & 0x00000278)) {
obj_die_if_health_non_positive();
}
}
o->oGraphYOffset = 15.0f * o->header.gfx.scale[1];
}
void BehFlyGuyFlameLoop(void) {
obj_move_using_fvel_and_gravity();
if (approach_f32_ptr(&o->header.gfx.scale[0], 0.0f, 0.6f)) {
mark_object_for_deletion(o);
}
obj_scale(o->header.gfx.scale[0]);
}
Gfx *Geo18_8030D93C(s32 arg0, struct GraphNode *node, UNUSED void *arg2) {
struct Object *sp4;
struct GraphNodeTranslationRotation *sp0;
if (arg0 == 1) {
sp4 = (struct Object *) gCurGraphNodeObject;
sp0 = (struct GraphNodeTranslationRotation *) node->next;
sp0->translation[0] = sp4->OBJECT_FIELD_S16(0x49, 0);
sp0->translation[1] = sp4->OBJECT_FIELD_S16(0x49, 1);
sp0->translation[2] = sp4->OBJECT_FIELD_S16(0x4A, 0);
}
return NULL;
}
Gfx *Geo18_8030D9AC(s32 arg0, struct GraphNode *node, UNUSED void *arg2) {
struct Object *sp4;
struct GraphNodeScale *sp0;
if (arg0 == 1) {
sp4 = (struct Object *) gCurGraphNodeObject;
sp0 = (struct GraphNodeScale *) node->next;
sp0->scale = sp4->OBJECT_FIELD_S16(0x4A, 1) / 1000.0f;
}
return NULL;
}
struct ObjectHitbox sSnufitHitbox = {
/* interactType: */ INTERACT_HIT_FROM_BELOW,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 2,
/* health: */ 0,
/* numLootCoins: */ 2,
/* radius: */ 100,
/* height: */ 60,
/* hurtboxRadius: */ 70,
/* hurtboxHeight: */ 50,
};
struct ObjectHitbox sSnufitBulletHitbox = {
/* interactType: */ INTERACT_SNUFIT_BULLET,
/* downOffset: */ 50,
/* damageOrCoinValue: */ 1,
/* health: */ 0,
/* numLootCoins: */ 0,
/* radius: */ 100,
/* height: */ 50,
/* hurtboxRadius: */ 100,
/* hurtboxHeight: */ 50,
};
void func_8030C914(void) {
s32 sp1C;
sp1C = (s32)(o->oDistanceToMario / 10.0f);
if (o->oTimer > sp1C && o->oDistanceToMario < 800.0f) {
o->oSnufitUnk104 = approach_s16_symmetric(o->oSnufitUnk104, 0, 1500);
o->oSnufitUnk108 = approach_s16_symmetric(o->oSnufitUnk108, 600, 15);
if ((s16) o->oSnufitUnk104 == 0 && o->oSnufitUnk108 == 600) {
o->oAction = 1;
o->oSnufitUnk10C = 0;
}
} else {
o->oSnufitUnk100 += 400;
}
}
void func_8030CA38(void) {
o->oSnufitUnk104 = approach_s16_symmetric(o->oSnufitUnk104, -0x8000, 3000);
o->oSnufitUnk108 = approach_s16_symmetric(o->oSnufitUnk108, 0xA7, 20);
if ((u16) o->oSnufitUnk104 == 0x8000 && o->oSnufitUnk108 == 0xA7) {
o->oAction = 0;
} else if (o->oSnufitUnk10C < 3 && o->oTimer >= 3) {
o->oSnufitUnk10C += 1;
PlaySound2(SOUND_OBJECT_SNUFITSHOOT);
spawn_object_relative(0, 0, -20, 40, o, MODEL_BOWLING_BALL, bhvSnufitBalls);
o->oSnufitUnkF4 = -30;
o->oTimer = 0;
}
}
void BehSnufitLoop(void) {
if (!(o->activeFlags & 0x0008)) {
o->oDeathSound = SOUND_OBJECT_SNUFITDEATH;
if (o->oDistanceToMario < 800.0f) {
obj_turn_pitch_toward_mario(120.0f, 2000);
if ((s16) o->oMoveAnglePitch > 0x2000) {
o->oMoveAnglePitch = 0x2000;
} else if ((s16) o->oMoveAnglePitch < -0x2000) {
o->oMoveAnglePitch = -0x2000;
}
obj_rotate_yaw_toward(o->oAngleToMario, 2000);
} else {
obj_move_pitch_approach(0, 0x200);
o->oMoveAngleYaw += 200;
}
o->oFaceAnglePitch = o->oMoveAnglePitch;
switch (o->oAction) {
case 0:
func_8030C914();
break;
case 1:
func_8030CA38();
break;
}
o->oPosX = o->oHomeX + 100.0f * coss(o->oSnufitUnk100);
o->oPosY = o->oHomeY + 8.0f * coss(4000 * gGlobalTimer);
o->oPosZ = o->oHomeZ + 100.0f * sins(o->oSnufitUnk100);
o->oSnufitUnk1AE = -0x20;
o->oSnufitUnk1B0 = o->oSnufitUnkF4 + 180;
o->oSnufitUnk1B2 = (s16)(o->oSnufitUnk108 + 666 + o->oSnufitUnk108 * coss(o->oSnufitUnk104));
if (o->oSnufitUnk1B2 > 1000) {
o->oSnufitUnkF8 = (o->oSnufitUnk1B2 - 1000) / 1000.0f + 1.0f;
o->oSnufitUnk1B2 = 1000;
} else {
o->oSnufitUnkF8 = 1.0f;
}
obj_scale(o->oSnufitUnkF8);
obj_check_attacks(&sSnufitHitbox, o->oAction);
}
}
void BehSnufitBallsLoop(void) {
if ((o->activeFlags & 0x0008) || (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) {
mark_object_for_deletion(o);
}
if (o->oGravity == 0.0f) {
obj_update_floor_and_walls();
obj_compute_vel_from_move_pitch(40.0f);
if (obj_check_attacks(&sSnufitBulletHitbox, 1)) {
o->oMoveAngleYaw += 0x8000;
o->oForwardVel *= 0.05f;
o->oVelY = 30.0f;
o->oGravity = -4.0f;
obj_become_intangible();
} else if (o->oAction == 1 || (o->oMoveFlags & 0x00000203)) {
o->oDeathSound = -1;
obj_die_if_health_non_positive();
}
obj_move_standard(78);
} else {
obj_move_using_fvel_and_gravity();
}
}
#include "behaviors/horizontal_grindel.inc.c"
#include "behaviors/eyerok.inc.c"
#include "behaviors/klepto.inc.c"
#include "behaviors/bird.inc.c"
#include "behaviors/racing_penguin.inc.c"
struct Struct80331C00 D_80331C00[] = {
{ 0x019C, 0xFF6A }, { 0x02FA, 0xFF6A }, { 0x0458, 0xFF6A },
{ 0x019C, 0x0096 }, { 0x02FA, 0x0096 }, { 0x0458, 0x0096 },
};
void BehHauntedRoomCheckLoop(void) {
struct Object *val0C;
s32 val08;
s16 val06;
if (o->oAction == 0) {
if (!(o->activeFlags & 0x0008)) {
for (val08 = 0; val08 < 6; val08++) {
val06 = D_80331C00[val08].unk02;
val0C = spawn_object_relative(val08 & 0x00000001, D_80331C00[val08].unk00, 0, val06, o,
MODEL_BBH_WOODEN_TOMB, bhvHauntedRoomCheckSubobject);
if (val0C != NULL) {
if (val06 > 0) {
val0C->oFaceAngleYaw = 0x8000;
}
}
}
o->oAction += 1;
}
} else if (o->activeFlags & 0x0008) {
o->oAction = 0;
}
}
void func_80311264(void) {
f32 val14;
f32 val10;
f32 val0C;
f32 val08;
f32 val04;
f32 val00;
if (o->oBehParams2ndByte != 0) {
if (o->oFaceAnglePitch != 0) {
o->oAngleVelPitch = approach_s16_symmetric(o->oAngleVelPitch, -2000, 200);
if (obj_face_pitch_approach(0, -o->oAngleVelPitch)) {
PlaySound2(SOUND_GENERAL_ELEVATORMOVE_2);
obj_perform_position_op(0);
o->oMoveAngleYaw = o->oFaceAngleYaw - 0x4000;
obj_set_dist_from_home(200.0f);
func_802ADA94();
obj_perform_position_op(2);
}
o->oTimer = 0;
} else {
val14 = coss(o->oFaceAngleYaw);
val10 = sins(o->oFaceAngleYaw);
val0C = gMarioObject->oPosX - o->oPosX;
val08 = gMarioObject->oPosZ - o->oPosZ;
val04 = val0C * val14 + val08 * val10;
val00 = val08 * val14 - val0C * val10;
if (o->oTimer > 60
&& (o->oDistanceToMario > 100.0f || gMarioState->action == ACT_SQUISHED)) {
if (gMarioObject->oPosY - o->oPosY < 200.0f && absf(val04) < 140.0f) {
if (val00 < 150.0f && val00 > -450.0f) {
PlaySound2(SOUND_GENERAL_BUTTONPRESS_2_LOWPRIO);
o->oAction = 1;
}
}
}
o->oAngleVelPitch = 0;
}
}
}
void func_80311520(void) {
if (o->oFaceAnglePitch != 0x4000) {
o->oAngleVelPitch = approach_s16_symmetric(o->oAngleVelPitch, 1000, 200);
obj_face_pitch_approach(0x4000, o->oAngleVelPitch);
} else {
if (o->oTimer > 60) {
o->oAction = 0;
o->oFaceAngleRoll = 0;
} else if (o->oTimer > 30) {
if (gGlobalTimer % 4 == 0) {
PlaySound2(SOUND_GENERAL_ELEVATORMOVE_2);
}
o->oFaceAngleRoll = 400 * (gGlobalTimer % 2) - 200;
}
o->oAngleVelPitch = 0;
}
}
void BehHauntedRoomCheckSubobjectLoop(void) {
if (o->parentObj->oAction == 0) {
mark_object_for_deletion(o);
} else {
o->header.gfx.scale[1] = 1.1f;
switch (o->oAction) {
case 0:
func_80311264();
break;
case 1:
func_80311520();
break;
}
load_object_collision_model();
}
}
struct ObjectHitbox sClamShellHitbox = {
/* interactType: */ INTERACT_CLAM_OR_BUBBA,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 2,
/* health: */ 99,
/* numLootCoins: */ 0,
/* radius: */ 150,
/* height: */ 80,
/* hurtboxRadius: */ 150,
/* hurtboxHeight: */ 80,
};
void func_803116F8(void) {
if (func_802F92EC(0, 25)) {
PlaySound2(SOUND_GENERAL_CLAMSHELL4);
func_802ADA94();
obj_become_tangible();
o->oClamUnkF4 = 10;
o->oTimer = 0;
} else if (o->oTimer > 150 && o->oDistanceToMario < 500.0f) {
PlaySound2(SOUND_GENERAL_CLAMSHELL3);
o->oAction = 1;
} else if (o->oClamUnkF4 != 0) {
o->oClamUnkF4 -= 1;
obj_shake_y(3.0f);
}
}
void func_803117F4(void) {
s16 val06;
s16 val04;
s16 val02;
if (o->oTimer > 150) {
o->oAction = 0;
} else if (obj_is_rendering_enabled() && func_802F92EC(1, 8)) {
for (val06 = -0x2000; val06 < 0x2000; val06 += 0x555) {
val04 = (s16)(100.0f * sins(val06));
val02 = (s16)(100.0f * coss(val06));
spawn_object_relative(0, val04, 30, val02, o, MODEL_BUBBLE, bhvBubbleMaybe);
}
} else if (obj_check_anim_frame(30)) {
obj_become_intangible();
}
}
void BehClamShellLoop(void) {
o->header.gfx.scale[1] = 1.5f;
switch (o->oAction) {
case 0:
func_803116F8();
break;
case 1:
func_803117F4();
break;
}
obj_check_attacks(&sClamShellHitbox, o->oAction);
}
#include "behaviors/skeeter.inc.c"
#include "behaviors/swing_platform.inc.c"
#include "behaviors/donut_platform.inc.c"
#include "behaviors/ddd_pole.inc.c"
#include "behaviors/reds_star_marker.inc.c"
#include "behaviors/triplet_butterfly.inc.c"
static struct ObjectHitbox sBubbaHitbox = {
/* interactType: */ INTERACT_CLAM_OR_BUBBA,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 1,
/* health: */ 99,
/* numLootCoins: */ 0,
/* radius: */ 300,
/* height: */ 200,
/* hurtboxRadius: */ 300,
/* hurtboxHeight: */ 200,
};
void func_80312F8C(void) {
f32 sp24;
sp24 = obj_lateral_dist_to_home();
treat_far_home_as_mario(2000.0f);
o->oAnimState = 0;
o->oBubbaUnk1AC = obj_get_pitch_to_home(sp24);
approach_f32_ptr(&o->oBubbaUnkF4, 5.0f, 0.5f);
if (o->oBubbaUnkFC != 0) {
if (abs_angle_diff(o->oMoveAngleYaw, o->oBubbaUnk1AE) < 800) {
o->oBubbaUnkFC = 0;
}
} else {
if (o->oDistanceToMario >= 25000.0f) {
o->oBubbaUnk1AE = o->oAngleToMario;
o->oBubbaUnkF8 = random_linear_offset(20, 30);
}
if ((o->oBubbaUnkFC = o->oMoveFlags & 0x00000200) != 0) {
o->oBubbaUnk1AE = obj_reflect_move_angle_off_wall();
} else if (o->oTimer > 30 && o->oDistanceToMario < 2000.0f) {
o->oAction = 1;
} else if (o->oBubbaUnkF8 != 0) {
o->oBubbaUnkF8 -= 1;
} else {
o->oBubbaUnk1AE = obj_random_fixed_turn(0x2000);
o->oBubbaUnkF8 = random_linear_offset(100, 100);
}
}
}
void func_80313170(void) {
s16 val06;
s16 val04;
treat_far_home_as_mario(2500.0f);
if (o->oDistanceToMario > 2500.0f) {
o->oAction = 0;
} else if (o->oBubbaUnk100 != 0) {
if (--o->oBubbaUnk100 == 0) {
PlaySound2(SOUND_OBJECT_CHOMPINGSOUND);
o->oAction = 0;
} else if (o->oBubbaUnk100 < 15) {
o->oAnimState = 1;
} else if (o->oBubbaUnk100 == 20) {
val06 = 10000 - (s16)(20.0f * (find_water_level(o->oPosX, o->oPosZ) - o->oPosY));
o->oBubbaUnk1AC -= val06;
o->oMoveAnglePitch = o->oBubbaUnk1AC;
o->oBubbaUnkF4 = 40.0f;
obj_compute_vel_from_move_pitch(o->oBubbaUnkF4);
o->oAnimState = 0;
;
} else {
o->oBubbaUnk1AE = o->oAngleToMario;
o->oBubbaUnk1AC = o->oBubbaUnk104;
obj_rotate_yaw_toward(o->oBubbaUnk1AE, 400);
obj_move_pitch_approach(o->oBubbaUnk1AC, 400);
}
} else {
if (abs_angle_diff(gMarioObject->oFaceAngleYaw, o->oAngleToMario) < 0x3000) {
val04 = 0x4000 - atan2s(800.0f, o->oDistanceToMario - 800.0f);
if ((s16)(o->oMoveAngleYaw - o->oAngleToMario) < 0) {
val04 = -val04;
}
o->oBubbaUnk1AE = o->oAngleToMario + val04;
;
} else {
o->oBubbaUnk1AE = o->oAngleToMario;
}
o->oBubbaUnk1AC = o->oBubbaUnk104;
if (obj_is_near_to_and_facing_mario(500.0f, 3000)
&& abs_angle_diff(o->oBubbaUnk1AC, o->oMoveAnglePitch) < 3000) {
o->oBubbaUnk100 = 30;
o->oBubbaUnkF4 = 0;
o->oAnimState = 1;
} else {
approach_f32_ptr(&o->oBubbaUnkF4, 20.0f, 0.5f);
}
}
}
void BehBubbaLoop(void) {
UNUSED s32 unused;
struct Object *sp38;
s16 sp36;
o->oUnk190 &= ~0x00002000;
o->oBubbaUnk104 = obj_turn_pitch_toward_mario(120.0f, 0);
if (abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw) < 0x1000
&& abs_angle_diff(o->oBubbaUnk104 + 0x800, o->oMoveAnglePitch) < 0x2000) {
if (o->oAnimState != 0 && o->oDistanceToMario < 250.0f) {
o->oUnk190 |= 0x00002000;
}
o->hurtboxRadius = 100.0f;
} else {
o->hurtboxRadius = 150.0f;
}
obj_update_floor_and_walls();
switch (o->oAction) {
case 0:
func_80312F8C();
break;
case 1:
func_80313170();
break;
}
if (o->oMoveFlags & 0x00000078) {
if (o->oMoveFlags & 0x00000008) {
sp38 = spawn_object(o, MODEL_WATER_SPLASH, bhvWaterSplash);
if (sp38 != NULL) {
scale_object(sp38, 3.0f);
}
o->oBubbaUnk108 = o->oVelY;
o->oBubbaUnk10C = 0.0f;
;
} else {
approach_f32_ptr(&o->oBubbaUnk108, 0.0f, 4.0f);
if ((o->oBubbaUnk10C -= o->oBubbaUnk108) > 1.0f) {
sp36 = RandomU16();
o->oBubbaUnk10C -= 1.0f;
spawn_object_relative(0, 150.0f * coss(sp36), 0x64, 150.0f * sins(sp36), o,
MODEL_WHITE_PARTICLE_SMALL, bhvSmallParticleSnow);
}
}
obj_smooth_turn(&o->oBubbaUnk1B0, &o->oMoveAnglePitch, o->oBubbaUnk1AC, 0.05f, 10, 50, 2000);
obj_smooth_turn(&o->oBubbaUnk1B2, &o->oMoveAngleYaw, o->oBubbaUnk1AE, 0.05f, 10, 50, 2000);
obj_compute_vel_from_move_pitch(o->oBubbaUnkF4);
} else {
o->oBubbaUnkF4 = sqrtf(o->oForwardVel * o->oForwardVel + o->oVelY * o->oVelY);
o->oMoveAnglePitch = obj_get_pitch_from_vel();
obj_face_pitch_approach(o->oMoveAnglePitch, 400);
o->oBubbaUnk1B0 = 0;
}
obj_face_pitch_approach(o->oMoveAnglePitch, 400);
obj_check_attacks(&sBubbaHitbox, o->oAction);
obj_move_standard(78);
o->oFloorHeight += 150.0f;
if (o->oPosY < o->oFloorHeight) {
o->oPosY = o->oFloorHeight;
}
}