subtr-actor 0.7.3

Rocket League replay transformer
Documentation
use super::*;

fn rigid_body(position: glam::Vec3, velocity: 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: Some(glam_to_vec(&velocity)),
        angular_velocity: Some(glam_to_vec(&glam::Vec3::ZERO)),
    }
}

fn ball(x: f32, y: f32) -> BallFrameState {
    BallFrameState::Present(BallSample {
        rigid_body: rigid_body(glam::Vec3::new(x, y, BALL_RADIUS_Z), glam::Vec3::ZERO),
    })
}

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

fn possession(player_id: &PlayerId, is_team_0: bool) -> PossessionState {
    PossessionState {
        active_team_before_sample: Some(is_team_0),
        current_team_is_team_0: Some(is_team_0),
        active_player_before_sample: Some(player_id.clone()),
        current_player: Some(player_id.clone()),
    }
}

#[test]
fn credits_ball_travel_and_goal_advancement_to_possession_player() {
    let player_id = boxcars::RemoteId::Steam(1);
    let mut calculator = TouchCalculator::new();
    let touch_state = TouchState {
        last_touch_player: Some(player_id.clone()),
        last_touch_team_is_team_0: Some(true),
        ..TouchState::default()
    };

    calculator
        .update(
            &frame(1),
            &ball(0.0, 0.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &possession(&player_id, true),
            &FiftyFiftyState::default(),
            true,
        )
        .unwrap();
    calculator
        .update(
            &frame(2),
            &ball(0.0, 100.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &possession(&player_id, true),
            &FiftyFiftyState::default(),
            true,
        )
        .unwrap();
    calculator
        .update(
            &frame(3),
            &ball(40.0, 70.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &possession(&player_id, true),
            &FiftyFiftyState::default(),
            true,
        )
        .unwrap();

    let stats = calculator.player_stats().get(&player_id).unwrap();
    assert_eq!(stats.total_ball_travel_distance, 150.0);
    assert_eq!(stats.total_ball_advance_distance, 100.0);
    assert_eq!(stats.total_ball_retreat_distance, 30.0);
}

#[test]
fn skips_ball_movement_without_a_possession_player() {
    let player_id = boxcars::RemoteId::Steam(1);
    let mut calculator = TouchCalculator::new();
    let touch_state = TouchState {
        last_touch_player: Some(player_id.clone()),
        last_touch_team_is_team_0: Some(true),
        ..TouchState::default()
    };

    calculator
        .update(
            &frame(1),
            &ball(0.0, 0.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &possession(&player_id, true),
            &FiftyFiftyState::default(),
            true,
        )
        .unwrap();
    calculator
        .update(
            &frame(2),
            &ball(0.0, 100.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &PossessionState::default(),
            &FiftyFiftyState::default(),
            true,
        )
        .unwrap();
    calculator
        .update(
            &frame(3),
            &ball(0.0, 160.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &possession(&player_id, true),
            &FiftyFiftyState::default(),
            true,
        )
        .unwrap();

    let stats = calculator.player_stats().get(&player_id).unwrap();
    assert_eq!(stats.total_ball_travel_distance, 60.0);
    assert_eq!(stats.total_ball_advance_distance, 60.0);
}

#[test]
fn credits_fifty_fifty_direction_to_resolved_winner_not_last_touch() {
    let blue_player = boxcars::RemoteId::Steam(1);
    let orange_player = boxcars::RemoteId::Steam(2);
    let mut calculator = TouchCalculator::new();
    let touch_state = TouchState {
        last_touch_player: Some(orange_player.clone()),
        last_touch_team_is_team_0: Some(false),
        ..TouchState::default()
    };

    let active_fifty = ActiveFiftyFifty {
        start_time: 0.1,
        start_frame: 1,
        last_touch_time: 0.1,
        last_touch_frame: 1,
        is_kickoff: false,
        team_zero_player: Some(blue_player.clone()),
        team_one_player: Some(orange_player.clone()),
        team_zero_position: [0.0, -100.0, 0.0],
        team_one_position: [0.0, 100.0, 0.0],
        midpoint: [0.0, 0.0, 0.0],
        plane_normal: [0.0, 1.0, 0.0],
    };
    let resolved_fifty = FiftyFiftyEvent {
        start_time: active_fifty.start_time,
        start_frame: active_fifty.start_frame,
        resolve_time: 0.3,
        resolve_frame: 3,
        is_kickoff: false,
        team_zero_player: active_fifty.team_zero_player.clone(),
        team_one_player: active_fifty.team_one_player.clone(),
        team_zero_position: active_fifty.team_zero_position,
        team_one_position: active_fifty.team_one_position,
        midpoint: active_fifty.midpoint,
        plane_normal: active_fifty.plane_normal,
        winning_team_is_team_0: Some(true),
        possession_team_is_team_0: None,
    };

    calculator
        .update(
            &frame(1),
            &ball(0.0, 0.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &PossessionState::default(),
            &FiftyFiftyState {
                active_event: Some(active_fifty.clone()),
                ..FiftyFiftyState::default()
            },
            true,
        )
        .unwrap();
    calculator
        .update(
            &frame(2),
            &ball(0.0, 100.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &PossessionState::default(),
            &FiftyFiftyState {
                active_event: Some(active_fifty),
                ..FiftyFiftyState::default()
            },
            true,
        )
        .unwrap();
    calculator
        .update(
            &frame(3),
            &ball(0.0, 170.0),
            &PlayerVerticalState::default(),
            &touch_state,
            &PossessionState::default(),
            &FiftyFiftyState {
                resolved_events: vec![resolved_fifty],
                ..FiftyFiftyState::default()
            },
            true,
        )
        .unwrap();

    let blue_stats = calculator.player_stats().get(&blue_player).unwrap();
    assert_eq!(blue_stats.total_ball_travel_distance, 170.0);
    assert_eq!(blue_stats.total_ball_advance_distance, 170.0);
    assert_eq!(blue_stats.total_ball_retreat_distance, 0.0);
    assert!(calculator.player_stats().get(&orange_player).is_none());
}