#include #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(¤tSpeed, 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; } }