subtr-actor 0.9.2

Rocket League replay transformer
Documentation
use super::*;

fn rigid_body(position: glam::Vec3) -> boxcars::RigidBody {
    boxcars::RigidBody {
        sleeping: false,
        location: glam_to_vec(&position),
        rotation: boxcars::Quaternion {
            x: 0.0,
            y: 0.0,
            z: 0.0,
            w: 1.0,
        },
        linear_velocity: None,
        angular_velocity: None,
    }
}

fn ball(position: glam::Vec3) -> BallFrameState {
    BallFrameState::Present(BallSample {
        rigid_body: rigid_body(position),
    })
}

fn player(player_id: PlayerId, is_team_0: bool, position: glam::Vec3) -> PlayerSample {
    PlayerSample {
        player_id,
        is_team_0,
        rigid_body: Some(rigid_body(position)),
        boost_amount: None,
        last_boost_amount: None,
        boost_active: false,
        dodge_active: false,
        powerslide_active: false,
        match_goals: None,
        match_assists: None,
        match_saves: None,
        match_shots: None,
        match_score: None,
    }
}

fn frame(frame_number: usize, time: f32, dt: f32) -> FrameInfo {
    FrameInfo {
        frame_number,
        time,
        dt,
        seconds_remaining: None,
    }
}

fn gameplay_2v2() -> GameplayState {
    GameplayState {
        ball_has_been_hit: Some(true),
        current_in_game_team_player_counts: [2, 2],
        ..GameplayState::default()
    }
}

fn update_team_zero(
    calculator: &mut RotationCalculator,
    frame_number: usize,
    time: f32,
    blue_a_position: glam::Vec3,
    blue_b_position: glam::Vec3,
) {
    calculator
        .update(
            &frame(frame_number, time, 0.1),
            &gameplay_2v2(),
            &ball(glam::Vec3::ZERO),
            &PlayerFrameState {
                players: vec![
                    player(PlayerId::Steam(1), true, blue_a_position),
                    player(PlayerId::Steam(2), true, blue_b_position),
                    player(
                        PlayerId::Steam(3),
                        false,
                        glam::Vec3::new(3000.0, 3000.0, 0.0),
                    ),
                    player(
                        PlayerId::Steam(4),
                        false,
                        glam::Vec3::new(-3000.0, 3000.0, 0.0),
                    ),
                ],
            },
            &FrameEventsState::default(),
            true,
        )
        .expect("rotation update should succeed");
}

#[test]
fn debounces_first_man_changes_before_counting_rotations() {
    let mut calculator = RotationCalculator::with_config(RotationCalculatorConfig {
        first_man_debounce_seconds: 0.3,
        first_man_ambiguity_margin: 50.0,
        ..RotationCalculatorConfig::default()
    });

    update_team_zero(
        &mut calculator,
        1,
        0.1,
        glam::Vec3::new(100.0, 0.0, 0.0),
        glam::Vec3::new(1000.0, 0.0, 0.0),
    );
    assert_eq!(calculator.team_zero_stats().rotation_count, 0);

    update_team_zero(
        &mut calculator,
        2,
        0.2,
        glam::Vec3::new(1000.0, 0.0, 0.0),
        glam::Vec3::new(100.0, 0.0, 0.0),
    );
    update_team_zero(
        &mut calculator,
        3,
        0.3,
        glam::Vec3::new(1000.0, 0.0, 0.0),
        glam::Vec3::new(100.0, 0.0, 0.0),
    );
    assert_eq!(calculator.team_zero_stats().rotation_count, 0);

    update_team_zero(
        &mut calculator,
        4,
        0.4,
        glam::Vec3::new(1000.0, 0.0, 0.0),
        glam::Vec3::new(100.0, 0.0, 0.0),
    );
    assert_eq!(calculator.team_zero_stats().rotation_count, 1);
    assert_eq!(
        calculator
            .player_stats()
            .get(&PlayerId::Steam(1))
            .expect("player one stats")
            .lost_first_man_count,
        1
    );
    assert_eq!(
        calculator
            .player_stats()
            .get(&PlayerId::Steam(2))
            .expect("player two stats")
            .became_first_man_count,
        1
    );
}

#[test]
fn ambiguous_first_man_frames_do_not_create_rotations() {
    let mut calculator = RotationCalculator::with_config(RotationCalculatorConfig {
        first_man_debounce_seconds: 0.2,
        first_man_ambiguity_margin: 300.0,
        ..RotationCalculatorConfig::default()
    });

    update_team_zero(
        &mut calculator,
        1,
        0.1,
        glam::Vec3::new(100.0, 0.0, 0.0),
        glam::Vec3::new(1000.0, 0.0, 0.0),
    );
    update_team_zero(
        &mut calculator,
        2,
        0.2,
        glam::Vec3::new(100.0, 0.0, 0.0),
        glam::Vec3::new(250.0, 0.0, 0.0),
    );
    update_team_zero(
        &mut calculator,
        3,
        0.3,
        glam::Vec3::new(100.0, 0.0, 0.0),
        glam::Vec3::new(250.0, 0.0, 0.0),
    );

    assert_eq!(calculator.team_zero_stats().rotation_count, 0);
    let player_one_stats = calculator
        .player_stats()
        .get(&PlayerId::Steam(1))
        .expect("player one stats");
    assert_eq!(player_one_stats.current_role_state, RoleState::Ambiguous);
    assert_eq!(player_one_stats.time_ambiguous_role, 0.2);
}

#[test]
fn records_role_and_depth_time() {
    let mut calculator = RotationCalculator::with_config(RotationCalculatorConfig {
        first_man_ambiguity_margin: 50.0,
        ..RotationCalculatorConfig::default()
    });

    update_team_zero(
        &mut calculator,
        1,
        0.1,
        glam::Vec3::new(0.0, -500.0, 0.0),
        glam::Vec3::new(1000.0, 500.0, 0.0),
    );

    let first_man = calculator
        .player_stats()
        .get(&PlayerId::Steam(1))
        .expect("first man stats");
    assert_eq!(first_man.current_role_state, RoleState::FirstMan);
    assert_eq!(first_man.current_depth_state, PlayDepthState::BehindPlay);
    assert_eq!(first_man.time_first_man, 0.1);
    assert_eq!(first_man.time_behind_play, 0.1);

    let second_man = calculator
        .player_stats()
        .get(&PlayerId::Steam(2))
        .expect("second man stats");
    assert_eq!(second_man.current_role_state, RoleState::SecondMan);
    assert_eq!(second_man.current_depth_state, PlayDepthState::AheadOfPlay);
    assert_eq!(second_man.time_second_man, 0.1);
    assert_eq!(second_man.time_ahead_of_play, 0.1);
}