
/**
 * Behavior for bhvBird. These are the birds in the castle grounds
 * that fly away and scatter when Mario comes near them. There are
 * 2 types of birds; spawner birds and spawned birds. Spawner birds
 * are loaded by the level, and are inactive until Mario comes within
 * 2000 units of them, when they spawn 6 spawned birds and start flying.
 * Spawned birds are only spawned by a spawner bird, and start flying
 * immediately after spawning.
 */

/**
 * If the object is a spawned bird, start flying; if it's a spawner bird,
 * spawn spawned birds if Mario comes within 2000 units of it.
 */
static void bird_act_inactive(void) {
    // Start flying if the object is a spawned bird or if it's a spawner bird
    // and Mario is within 2000 units.
    if (o->oBhvParams2ndByte == BIRD_BP_SPAWNED || o->oDistanceToMario < 2000.0f) {
        // If the object is a spawner bird, play the sound of birds flying away,
        // and spawn 6 spawned birds (which will start flying on the next frame).
        if (o->oBhvParams2ndByte != BIRD_BP_SPAWNED) {
            s32 i;

            cur_obj_play_sound_2(SOUND_GENERAL_BIRDS_FLY_AWAY);

            for (i = 0; i < 6; i++) {
                spawn_object(o, MODEL_BIRDS, bhvBird);
            }

            // The spawner bird's home acts as its target location.
            o->oHomeX = -20.0f;
            o->oHomeZ = -3990.0f;
        }

        // Start flying
        o->oAction = BIRD_ACT_FLY;

        // Start with a random yaw, and a random pitch from 1000 to 5000.
        // Positive pitch is downwards.
        o->oMoveAnglePitch = 5000 - (s32)(4000.0f * random_float());
        o->oMoveAngleYaw = random_u16();

        o->oBirdSpeed = 40.0f;

        cur_obj_unhide();
    }
}

/**
 * Make the bird fly.
 * The bird flies laterally towards a target; (-20, -3990) if it's a spawner bird,
 * and the parent spawner bird if it's a spawned bird.
 */
static void bird_act_fly(void) {
    UNUSED u8 filler[4];
    f32 distance;

    // Compute forward velocity and vertical velocity from oBirdSpeed and pitch
    obj_compute_vel_from_move_pitch(o->oBirdSpeed);

    // If the bird's parent is higher than 8000 units, despawn the bird.
    // A spawned bird's parent is its spawner bird. A spawner bird's parent
    // is itself. In other words, when a group of birds has its spawner bird
    // fly past Y=8000, they will all despawn simultaneously. Otherwise, fly.
    if (o->parentObj->oPosY > 8000.0f) {
        obj_mark_for_deletion(o);
    } else {
        // If the bird is a spawner bird, fly towards its home; otherwise,
        // fly towards the bird's spawner bird.
        if (o->oBhvParams2ndByte != BIRD_BP_SPAWNED) {
            distance = cur_obj_lateral_dist_to_home();

            // The spawner bird will start with its downwards (positive) pitch
            // and will continuously decrease its pitch (i.e. make itself face more upwards)
            // until it reaches its home, at which point it will face directly up.
            // This is done by making its target pitch the arctangent of its distance
            // to its home and its position - 10,000 (which is always negative).
            o->oBirdTargetPitch = atan2s(distance, o->oPosY - 10000.0f);
            o->oBirdTargetYaw = cur_obj_angle_to_home();
        } else {
            distance = lateral_dist_between_objects(o, o->parentObj);

            // The bird's target pitch will face directly to its spawner bird.
            o->oBirdTargetPitch = atan2s(distance, o->oPosY - o->parentObj->oPosY);
            o->oBirdTargetYaw = obj_angle_to_object(o, o->parentObj);

            // The bird goes faster the farther it is from its spawner bird so it can catch up.
            o->oBirdSpeed = 0.04f * dist_between_objects(o, o->parentObj) + 20.0f;
        }

        // Approach to match the bird's target yaw and pitch.
        obj_move_pitch_approach(o->oBirdTargetPitch, 140);
        cur_obj_rotate_yaw_toward(o->oBirdTargetYaw, 800);
        obj_roll_to_match_yaw_turn(o->oBirdTargetYaw, 0x3000, 600);
    }

    // The bird has no gravity, so this function only
    // moves the bird using its forward velocity.
    // Even if it did have gravity, it would only act as
    // a constant added to its Y position every frame since
    // its Y velocity is reset every frame by
    // obj_compute_vel_from_move_pitch.
    cur_obj_move_using_fvel_and_gravity();
}

/**
 * Update function for bhvBird.
 */
void bhv_bird_update(void) {
    switch (o->oAction) {
        case BIRD_ACT_INACTIVE:
            bird_act_inactive();
            break;
        case BIRD_ACT_FLY:
            bird_act_fly();
            break;
    }
}
