
/**
 * Behavior for bhvSwoop.
 * Has a native room.
 */

/**
 * Hitbox for swoop.
 */
static struct ObjectHitbox sSwoopHitbox = {
    /* interactType:      */ INTERACT_HIT_FROM_BELOW,
    /* downOffset:        */ 0,
    /* damageOrCoinValue: */ 1,
    /* health:            */ 0,
    /* numLootCoins:      */ 1,
    /* radius:            */ 100,
    /* height:            */ 80,
    /* hurtboxRadius:     */ 70,
    /* hurtboxHeight:     */ 70,
};

/**
 * If necessary, grow to full size. Wait for mario to enter range, then turn
 * toward him and enter the move action.
 */
static void swoop_act_idle(void) {
    cur_obj_init_animation_with_sound(1);

    if (approach_f32_ptr(&o->header.gfx.scale[0], 1.0f, 0.05f) && o->oDistanceToMario < 1500.0f) {
        if (cur_obj_rotate_yaw_toward(o->oAngleToMario, 800)) {
            cur_obj_play_sound_2(SOUND_OBJ2_SWOOP);
            o->oAction = SWOOP_ACT_MOVE;
            o->oVelY = -12.0f;
        }
    }

    o->oFaceAngleRoll = 0x8000;
}

/**
 * Swoop downward toward mario. Stop moving downward and speed up after reaching
 * him. Return to home once mario is far away.
 */
static void swoop_act_move(void) {
    cur_obj_init_animation_with_accel_and_sound(0, 2.0f);
    if (cur_obj_check_if_near_animation_end()) {
        cur_obj_play_sound_2(SOUND_OBJ_UNKNOWN6);
    }

    if (o->oForwardVel == 0.0f) {
        // If we haven't started moving yet, begin swooping
        if (obj_face_roll_approach(0, 2500)) {
            o->oForwardVel = 10.0f;
            o->oVelY = -10.0f;
        }
    } else if (cur_obj_mario_far_away()) {
        // If mario far away, reset
        o->oAction = SWOOP_ACT_IDLE;
        cur_obj_set_pos_to_home();
        o->header.gfx.scale[0] = o->oForwardVel = o->oVelY = 0.0f;
        o->oFaceAngleRoll = 0;
    } else {
        if (o->oSwoopBonkCountdown != 0) {
            o->oSwoopBonkCountdown--;
        } else if (o->oVelY != 0.0f) {
            // If we're not done swooping, turn toward mario. When between
            // 0 and 200 units above mario, increase speed and stop swooping
            o->oSwoopTargetYaw = o->oAngleToMario;
            if (o->oPosY < gMarioObject->oPosY + 200.0f) {
                if (obj_y_vel_approach(0.0f, 0.5f)) {
                    o->oForwardVel *= 2.0f;
                }
            } else {
                obj_y_vel_approach(-10.0f, 0.5f);
            }
        } else if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
            // Bounce off a wall and don't bounce again for 30 frames.
            o->oSwoopTargetYaw = cur_obj_reflect_move_angle_off_wall();
            o->oSwoopBonkCountdown = 30;
        }

        // Tilt upward when approaching mario
        if ((o->oSwoopTargetPitch = obj_get_pitch_from_vel()) == 0) {
            o->oSwoopTargetPitch += o->oForwardVel * 500;
        }
        obj_move_pitch_approach(o->oSwoopTargetPitch, 140);

        // Jitter yaw a bit
        cur_obj_rotate_yaw_toward(o->oSwoopTargetYaw + (s32)(3000 * coss(4000 * gGlobalTimer)), 1200);
        obj_roll_to_match_yaw_turn(o->oSwoopTargetYaw, 0x3000, 500);

        // Jitter roll a bit
        o->oFaceAngleRoll += (s32)(1000 * coss(20000 * gGlobalTimer));
    }
}

/**
 * Update function for swoop.
 */
void bhv_swoop_update(void) {
    // No partial update (only appears in roomed levels)

    if (!(o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)) {
        o->oDeathSound = SOUND_OBJ_SWOOP_DEATH;

        cur_obj_update_floor_and_walls();

        switch (o->oAction) {
            case SWOOP_ACT_IDLE:
                swoop_act_idle();
                break;
            case SWOOP_ACT_MOVE:
                swoop_act_move();
                break;
        }

        cur_obj_scale(o->header.gfx.scale[0]);
        cur_obj_move_standard(78);

        obj_check_attacks(&sSwoopHitbox, o->oAction);
    }
}
