use crate::*;
use boxcars;
use std::sync::Arc;
fn or_zero_boxcars_3f() -> boxcars::Vector3f {
boxcars::Vector3f {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
type RigidBodyArrayResult<F> = SubtrActorResult<[F; 12]>;
type RigidBodyQuaternionArrayResult<F> = SubtrActorResult<[F; 13]>;
type RigidBodyBasisArrayResult<F> = SubtrActorResult<[F; 15]>;
pub fn get_rigid_body_properties<F: TryFrom<f32>>(
rigid_body: &boxcars::RigidBody,
) -> RigidBodyArrayResult<F>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
let linear_velocity = rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f);
let angular_velocity = rigid_body
.angular_velocity
.unwrap_or_else(or_zero_boxcars_3f);
let rotation = rigid_body.rotation;
let location = rigid_body.location;
let (rx, ry, rz) =
glam::quat(rotation.x, rotation.y, rotation.z, rotation.w).to_euler(glam::EulerRot::XYZ);
convert_all_floats!(
location.x,
location.y,
location.z,
rx,
ry,
rz,
linear_velocity.x,
linear_velocity.y,
linear_velocity.z,
angular_velocity.x,
angular_velocity.y,
angular_velocity.z,
)
}
pub fn get_rigid_body_properties_quaternion<F: TryFrom<f32>>(
rigid_body: &boxcars::RigidBody,
) -> RigidBodyQuaternionArrayResult<F>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
let linear_velocity = rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f);
let angular_velocity = rigid_body
.angular_velocity
.unwrap_or_else(or_zero_boxcars_3f);
let rotation = rigid_body.rotation;
let location = rigid_body.location;
convert_all_floats!(
location.x,
location.y,
location.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
linear_velocity.x,
linear_velocity.y,
linear_velocity.z,
angular_velocity.x,
angular_velocity.y,
angular_velocity.z,
)
}
pub fn get_rigid_body_properties_basis<F: TryFrom<f32>>(
rigid_body: &boxcars::RigidBody,
) -> RigidBodyBasisArrayResult<F>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
let linear_velocity = rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f);
let angular_velocity = rigid_body
.angular_velocity
.unwrap_or_else(or_zero_boxcars_3f);
let rotation = rigid_body.rotation;
let location = rigid_body.location;
let quat = glam::quat(rotation.x, rotation.y, rotation.z, rotation.w);
let forward = quat.mul_vec3(glam::Vec3::X);
let up = quat.mul_vec3(glam::Vec3::Z);
convert_all_floats!(
location.x,
location.y,
location.z,
forward.x,
forward.y,
forward.z,
up.x,
up.y,
up.z,
linear_velocity.x,
linear_velocity.y,
linear_velocity.z,
angular_velocity.x,
angular_velocity.y,
angular_velocity.z,
)
}
pub fn get_rigid_body_properties_no_velocities<F: TryFrom<f32>>(
rigid_body: &boxcars::RigidBody,
) -> SubtrActorResult<[F; 7]>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
let rotation = rigid_body.rotation;
let location = rigid_body.location;
convert_all_floats!(
location.x, location.y, location.z, rotation.x, rotation.y, rotation.z, rotation.w
)
}
fn default_rb_state<F: TryFrom<f32>>() -> RigidBodyArrayResult<F>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
convert_all!(
convert_float_conversion_error,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
)
}
fn default_rb_state_quaternion<F: TryFrom<f32>>() -> RigidBodyQuaternionArrayResult<F>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,)
}
fn default_rb_state_basis<F: TryFrom<f32>>() -> RigidBodyBasisArrayResult<F>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
convert_all_floats!(0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,)
}
fn default_rb_state_no_velocities<F: TryFrom<f32>>() -> SubtrActorResult<[F; 7]>
where
<F as TryFrom<f32>>::Error: std::fmt::Debug,
{
convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,)
}
build_global_feature_adder!(
SecondsRemaining,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
convert_all_floats!(processor.get_seconds_remaining().unwrap_or(0) as f32)
},
"seconds remaining"
);
build_global_feature_adder!(
CurrentTime,
|_, _processor, _frame, _index, current_time: f32| { convert_all_floats!(current_time) },
"current time"
);
build_global_feature_adder!(
ReplicatedStateName,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
convert_all_floats!(processor.get_replicated_state_name().unwrap_or(0) as f32)
},
"game state"
);
build_global_feature_adder!(
ReplicatedGameStateTimeRemaining,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
convert_all_floats!(processor
.get_replicated_game_state_time_remaining()
.unwrap_or(0) as f32)
},
"kickoff countdown"
);
build_global_feature_adder!(
BallHasBeenHit,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
convert_all_floats!(if processor.get_ball_has_been_hit().unwrap_or(false) {
1.0
} else {
0.0
})
},
"ball has been hit"
);
build_global_feature_adder!(
FrameTime,
|_, _processor, frame: &boxcars::Frame, _index, _current_time| {
convert_all_floats!(frame.time)
},
"frame time"
);
build_global_feature_adder!(
BallRigidBody,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
processor
.get_normalized_ball_rigid_body()
.and_then(|rb| get_rigid_body_properties(&rb))
.or_else(|_| default_rb_state())
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - rotation x",
"Ball - rotation y",
"Ball - rotation z",
"Ball - linear velocity x",
"Ball - linear velocity y",
"Ball - linear velocity z",
"Ball - angular velocity x",
"Ball - angular velocity y",
"Ball - angular velocity z",
);
build_global_feature_adder!(
BallRigidBodyNoVelocities,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
processor
.get_normalized_ball_rigid_body()
.and_then(|rb| get_rigid_body_properties_no_velocities(&rb))
.or_else(|_| default_rb_state_no_velocities())
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - rotation x",
"Ball - rotation y",
"Ball - rotation z",
"Ball - rotation w",
);
build_global_feature_adder!(
VelocityAddedBallRigidBodyNoVelocities,
|_, processor: &dyn ProcessorView, _frame, _index, current_time: f32| {
processor
.get_velocity_applied_ball_rigid_body(current_time)
.and_then(|rb| get_rigid_body_properties_no_velocities(&rb))
.or_else(|_| default_rb_state_no_velocities())
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - rotation x",
"Ball - rotation y",
"Ball - rotation z",
"Ball - rotation w",
);
#[derive(derive_new::new)]
pub struct InterpolatedBallRigidBodyNoVelocities<F> {
close_enough_to_frame_time: f32,
_zero: std::marker::PhantomData<F>,
}
impl<F> InterpolatedBallRigidBodyNoVelocities<F> {
pub fn arc_new(close_enough_to_frame_time: f32) -> Arc<Self> {
Arc::new(Self::new(close_enough_to_frame_time))
}
}
global_feature_adder!(
InterpolatedBallRigidBodyNoVelocities,
|s: &InterpolatedBallRigidBodyNoVelocities<F>,
processor: &dyn ProcessorView,
_frame: &boxcars::Frame,
_index,
current_time: f32| {
processor
.get_interpolated_ball_rigid_body(current_time, s.close_enough_to_frame_time)
.map(|v| get_rigid_body_properties_no_velocities(&v))
.unwrap_or_else(|_| default_rb_state_no_velocities())
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - rotation x",
"Ball - rotation y",
"Ball - rotation z",
"Ball - rotation w",
);
build_player_feature_adder!(
PlayerRigidBody,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
get_rigid_body_properties(&rb)
} else {
default_rb_state()
}
},
"position x",
"position y",
"position z",
"rotation x",
"rotation y",
"rotation z",
"linear velocity x",
"linear velocity y",
"linear velocity z",
"angular velocity x",
"angular velocity y",
"angular velocity z",
);
build_player_feature_adder!(
PlayerRigidBodyNoVelocities,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
get_rigid_body_properties_no_velocities(&rb)
} else {
default_rb_state_no_velocities()
}
},
"position x",
"position y",
"position z",
"rotation x",
"rotation y",
"rotation z",
"rotation w"
);
build_player_feature_adder!(
VelocityAddedPlayerRigidBodyNoVelocities,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, current_time: f32| {
if let Ok(rb) = processor.get_velocity_applied_player_rigid_body(player_id, current_time) {
get_rigid_body_properties_no_velocities(&rb)
} else {
default_rb_state_no_velocities()
}
},
"position x",
"position y",
"position z",
"rotation x",
"rotation y",
"rotation z",
"rotation w"
);
#[derive(derive_new::new)]
pub struct InterpolatedPlayerRigidBodyNoVelocities<F> {
close_enough_to_frame_time: f32,
_zero: std::marker::PhantomData<F>,
}
impl<F> InterpolatedPlayerRigidBodyNoVelocities<F> {
pub fn arc_new(close_enough_to_frame_time: f32) -> Arc<Self> {
Arc::new(Self::new(close_enough_to_frame_time))
}
}
player_feature_adder!(
InterpolatedPlayerRigidBodyNoVelocities,
|s: &InterpolatedPlayerRigidBodyNoVelocities<F>,
player_id: &PlayerId,
processor: &dyn ProcessorView,
_frame: &boxcars::Frame,
_index,
current_time: f32| {
processor
.get_interpolated_player_rigid_body(
player_id,
current_time,
s.close_enough_to_frame_time,
)
.map(|v| get_rigid_body_properties_no_velocities(&v))
.unwrap_or_else(|_| default_rb_state_no_velocities())
},
"i position x",
"i position y",
"i position z",
"i rotation x",
"i rotation y",
"i rotation z",
"i rotation w"
);
build_player_feature_adder!(
PlayerRelativeBallPosition,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
let relative_position = processor
.get_normalized_player_rigid_body(player_id)
.ok()
.zip(processor.get_normalized_ball_rigid_body().ok())
.map(|(player_rigid_body, ball_rigid_body)| {
vec_to_glam(&ball_rigid_body.location) - vec_to_glam(&player_rigid_body.location)
})
.unwrap_or(glam::f32::Vec3::ZERO);
convert_all_floats!(
relative_position.x,
relative_position.y,
relative_position.z
)
},
"relative ball position x",
"relative ball position y",
"relative ball position z"
);
build_player_feature_adder!(
PlayerRelativeBallVelocity,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
let relative_velocity = processor
.get_normalized_player_rigid_body(player_id)
.ok()
.zip(processor.get_normalized_ball_rigid_body().ok())
.map(|(player_rigid_body, ball_rigid_body)| {
vec_to_glam(
&ball_rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f),
) - vec_to_glam(
&player_rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f),
)
})
.unwrap_or(glam::f32::Vec3::ZERO);
convert_all_floats!(
relative_velocity.x,
relative_velocity.y,
relative_velocity.z
)
},
"relative ball velocity x",
"relative ball velocity y",
"relative ball velocity z"
);
build_player_feature_adder!(
PlayerLocalRelativeBallPosition,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
let local_relative_position = processor
.get_normalized_player_rigid_body(player_id)
.ok()
.zip(processor.get_normalized_ball_rigid_body().ok())
.map(|(player_rigid_body, ball_rigid_body)| {
let player_rotation = player_rigid_body.rotation;
let player_quat = glam::quat(
player_rotation.x,
player_rotation.y,
player_rotation.z,
player_rotation.w,
);
let world_relative_position = vec_to_glam(&ball_rigid_body.location)
- vec_to_glam(&player_rigid_body.location);
player_quat.inverse().mul_vec3(world_relative_position)
})
.unwrap_or(glam::f32::Vec3::ZERO);
convert_all_floats!(
local_relative_position.x,
local_relative_position.y,
local_relative_position.z
)
},
"local relative ball position x",
"local relative ball position y",
"local relative ball position z"
);
build_player_feature_adder!(
PlayerLocalRelativeBallVelocity,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
let local_relative_velocity = processor
.get_normalized_player_rigid_body(player_id)
.ok()
.zip(processor.get_normalized_ball_rigid_body().ok())
.map(|(player_rigid_body, ball_rigid_body)| {
let player_rotation = player_rigid_body.rotation;
let player_quat = glam::quat(
player_rotation.x,
player_rotation.y,
player_rotation.z,
player_rotation.w,
);
let world_relative_velocity = vec_to_glam(
&ball_rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f),
) - vec_to_glam(
&player_rigid_body
.linear_velocity
.unwrap_or_else(or_zero_boxcars_3f),
);
player_quat.inverse().mul_vec3(world_relative_velocity)
})
.unwrap_or(glam::f32::Vec3::ZERO);
convert_all_floats!(
local_relative_velocity.x,
local_relative_velocity.y,
local_relative_velocity.z
)
},
"local relative ball velocity x",
"local relative ball velocity y",
"local relative ball velocity z"
);
build_player_feature_adder!(
PlayerBallDistance,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
let distance = processor
.get_normalized_player_rigid_body(player_id)
.ok()
.zip(processor.get_normalized_ball_rigid_body().ok())
.map(|(player_rigid_body, ball_rigid_body)| {
(vec_to_glam(&player_rigid_body.location) - vec_to_glam(&ball_rigid_body.location))
.length()
})
.unwrap_or(0.0);
convert_all_floats!(distance)
},
"distance to ball"
);
build_player_feature_adder!(
PlayerBoost,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
convert_all_floats!(processor.get_player_boost_level(player_id).unwrap_or(0.0))
},
"boost level (raw replay units)"
);
fn u8_get_f32(v: u8) -> SubtrActorResult<f32> {
Ok(v.into())
}
build_player_feature_adder!(
PlayerJump,
|_,
player_id: &PlayerId,
processor: &dyn ProcessorView,
_frame,
_frame_number,
_current_time: f32| {
convert_all_floats!(
processor
.get_dodge_active(player_id)
.and_then(u8_get_f32)
.unwrap_or(0.0),
processor
.get_jump_active(player_id)
.and_then(u8_get_f32)
.unwrap_or(0.0),
processor
.get_double_jump_active(player_id)
.and_then(u8_get_f32)
.unwrap_or(0.0),
)
},
"dodge active",
"jump active",
"double jump active"
);
build_player_feature_adder!(
PlayerAnyJump,
|_,
player_id: &PlayerId,
processor: &dyn ProcessorView,
_frame,
_frame_number,
_current_time: f32| {
let dodge_is_active = processor.get_dodge_active(player_id).unwrap_or(0) % 2;
let jump_is_active = processor.get_jump_active(player_id).unwrap_or(0) % 2;
let double_jump_is_active = processor.get_double_jump_active(player_id).unwrap_or(0) % 2;
let value: f32 = [dodge_is_active, jump_is_active, double_jump_is_active]
.into_iter()
.enumerate()
.map(|(index, is_active)| (1 << index) * is_active)
.sum::<u8>() as f32;
convert_all_floats!(value)
},
"any_jump_active"
);
build_player_feature_adder!(
PlayerDodgeRefreshed,
|_,
player_id: &PlayerId,
processor: &dyn ProcessorView,
_frame,
_frame_number,
_current_time: f32| {
let dodge_refresh_count = processor
.current_frame_dodge_refreshed_events()
.iter()
.filter(|event| &event.player == player_id)
.count() as f32;
convert_all_floats!(dodge_refresh_count)
},
"dodge refresh count"
);
const DEMOLISH_APPEARANCE_FRAME_COUNT: usize = 30;
build_player_feature_adder!(
PlayerDemolishedBy,
|_,
player_id: &PlayerId,
processor: &dyn ProcessorView,
_frame,
frame_number,
_current_time: f32| {
let demolisher_index = processor
.demolishes()
.iter()
.find(|demolish_info| {
&demolish_info.victim == player_id
&& frame_number - demolish_info.frame < DEMOLISH_APPEARANCE_FRAME_COUNT
})
.map(|demolish_info| {
processor
.iter_player_ids_in_order()
.position(|player_id| player_id == &demolish_info.attacker)
.unwrap_or_else(|| processor.iter_player_ids_in_order().count())
})
.and_then(|v| i32::try_from(v).ok())
.unwrap_or(-1);
convert_all_floats!(demolisher_index as f32)
},
"player demolished by"
);
build_player_feature_adder!(
PlayerRigidBodyQuaternions,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
let rotation = rb.rotation;
let location = rb.location;
convert_all_floats!(
location.x, location.y, location.z, rotation.x, rotation.y, rotation.z, rotation.w
)
} else {
convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)
}
},
"position x",
"position y",
"position z",
"quaternion x",
"quaternion y",
"quaternion z",
"quaternion w"
);
build_player_feature_adder!(
PlayerRigidBodyQuaternionVelocities,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
get_rigid_body_properties_quaternion(&rb)
} else {
default_rb_state_quaternion()
}
},
"position x",
"position y",
"position z",
"quaternion x",
"quaternion y",
"quaternion z",
"quaternion w",
"linear velocity x",
"linear velocity y",
"linear velocity z",
"angular velocity x",
"angular velocity y",
"angular velocity z",
);
build_player_feature_adder!(
PlayerRigidBodyBasis,
|_, player_id: &PlayerId, processor: &dyn ProcessorView, _frame, _index, _current_time: f32| {
if let Ok(rb) = processor.get_normalized_player_rigid_body(player_id) {
get_rigid_body_properties_basis(&rb)
} else {
default_rb_state_basis()
}
},
"position x",
"position y",
"position z",
"forward x",
"forward y",
"forward z",
"up x",
"up y",
"up z",
"linear velocity x",
"linear velocity y",
"linear velocity z",
"angular velocity x",
"angular velocity y",
"angular velocity z",
);
build_global_feature_adder!(
BallRigidBodyQuaternions,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
match processor.get_normalized_ball_rigid_body() {
Ok(rb) => {
let rotation = rb.rotation;
let location = rb.location;
convert_all_floats!(
location.x, location.y, location.z, rotation.x, rotation.y, rotation.z,
rotation.w
)
}
Err(_) => convert_all_floats!(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0),
}
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - quaternion x",
"Ball - quaternion y",
"Ball - quaternion z",
"Ball - quaternion w"
);
build_global_feature_adder!(
BallRigidBodyQuaternionVelocities,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
processor
.get_normalized_ball_rigid_body()
.and_then(|rb| get_rigid_body_properties_quaternion(&rb))
.or_else(|_| default_rb_state_quaternion())
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - quaternion x",
"Ball - quaternion y",
"Ball - quaternion z",
"Ball - quaternion w",
"Ball - linear velocity x",
"Ball - linear velocity y",
"Ball - linear velocity z",
"Ball - angular velocity x",
"Ball - angular velocity y",
"Ball - angular velocity z",
);
build_global_feature_adder!(
BallRigidBodyBasis,
|_, processor: &dyn ProcessorView, _frame, _index, _current_time| {
processor
.get_normalized_ball_rigid_body()
.and_then(|rb| get_rigid_body_properties_basis(&rb))
.or_else(|_| default_rb_state_basis())
},
"Ball - position x",
"Ball - position y",
"Ball - position z",
"Ball - forward x",
"Ball - forward y",
"Ball - forward z",
"Ball - up x",
"Ball - up y",
"Ball - up z",
"Ball - linear velocity x",
"Ball - linear velocity y",
"Ball - linear velocity z",
"Ball - angular velocity x",
"Ball - angular velocity y",
"Ball - angular velocity z",
);