use std::marker::PhantomData;
use oxijolt_sys::*;
use super::settings::MAX_PITCH_ROLL_RULE;
use super::{install_tester, VehicleCollisionTester, VehicleEntry, VehicleKind, WheeledVehicle};
use crate::{limits, Vec3, VehicleError, VehicleRef};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct DriverInput {
pub forward: f32,
pub right: f32,
pub brake: f32,
pub hand_brake: f32,
}
impl DriverInput {
pub(super) fn validate(&self) -> Result<(), VehicleError> {
let within = |value: f32, low: f32| value.is_finite() && (low..=1.0).contains(&value);
let valid = within(self.forward, -1.0)
&& within(self.right, -1.0)
&& within(self.brake, 0.0)
&& within(self.hand_brake, 0.0);
if valid {
Ok(())
} else {
Err(VehicleError::InvalidValue(
"forward and right must be in [-1, 1], brake and hand brake in [0, 1]",
))
}
}
pub(super) unsafe fn apply(&self, controller: *mut JPH_WheeledVehicleController) {
unsafe {
JPH_WheeledVehicleController_SetDriverInput(
controller,
self.forward,
self.right,
self.brake,
self.hand_brake,
);
}
}
pub(super) unsafe fn of(controller: *mut JPH_WheeledVehicleController) -> Self {
unsafe {
Self {
forward: JPH_WheeledVehicleController_GetForwardInput(controller),
right: JPH_WheeledVehicleController_GetRightInput(controller),
brake: JPH_WheeledVehicleController_GetBrakeInput(controller),
hand_brake: JPH_WheeledVehicleController_GetHandBrakeInput(controller),
}
}
}
}
impl VehicleRef<'_, WheeledVehicle> {
pub fn driver_input(&self) -> DriverInput {
unsafe { DriverInput::of(self.controller().cast()) }
}
}
pub struct VehicleMut<'w, K: VehicleKind = WheeledVehicle> {
pub(super) entry: &'w mut VehicleEntry,
pub(super) object_layer_count: u32,
pub(super) kind: PhantomData<fn() -> K>,
}
impl<K: VehicleKind> std::fmt::Debug for VehicleMut<'_, K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VehicleMut").finish_non_exhaustive()
}
}
impl<K: VehicleKind> VehicleMut<'_, K> {
pub(super) fn ptr(&self) -> *mut JPH_VehicleConstraint {
self.entry.constraint.as_ptr()
}
pub(super) fn controller(&mut self) -> *mut JPH_VehicleController {
unsafe { JPH_VehicleConstraint_GetController(self.ptr()) }
}
pub fn set_gravity(&mut self, gravity: Vec3) -> Result<(), VehicleError> {
if !limits::is_acceleration(gravity) {
return Err(VehicleError::InvalidValue(limits::GRAVITY_RULE));
}
let gravity = gravity.to_jph();
unsafe { JPH_VehicleConstraint_OverrideGravity(self.ptr(), &gravity) };
Ok(())
}
pub fn set_max_pitch_roll_angle(&mut self, radians: f32) -> Result<(), VehicleError> {
if !(radians.is_finite() && (0.0..=std::f32::consts::PI).contains(&radians)) {
return Err(VehicleError::InvalidValue(MAX_PITCH_ROLL_RULE));
}
unsafe { JPH_VehicleConstraint_SetMaxPitchRollAngle(self.ptr(), radians) };
Ok(())
}
pub fn set_collision_tester(
&mut self,
tester: VehicleCollisionTester,
) -> Result<(), VehicleError> {
tester.validate(self.object_layer_count, self.entry.wheels.iter().copied())?;
install_tester(self.entry, tester);
Ok(())
}
}
impl VehicleMut<'_, WheeledVehicle> {
pub fn set_driver_input(&mut self, input: DriverInput) -> Result<(), VehicleError> {
input.validate()?;
unsafe { input.apply(self.controller().cast()) };
Ok(())
}
}