use std::fs::File;
use std::io::ErrorKind;
use std::path::Path;
use std::time::Duration;
use crate::TnuaMotor;
use crate::action_state::TnuaActionStateInterface;
use crate::ghost_overrides::TnuaGhostOverwrite;
use crate::sensor_sets::TnuaSensors;
use bevy::prelude::*;
use bevy::time::Stopwatch;
use bevy_tnua_physics_integration_layer::data_for_backends::TnuaGhostSensor;
use bevy_tnua_physics_integration_layer::data_for_backends::TnuaProximitySensor;
use bevy_tnua_physics_integration_layer::data_for_backends::TnuaRigidBodyTracker;
use serde::Deserialize;
use serde::Serialize;
use crate::math::*;
pub trait TnuaScheme: 'static + Send + Sync + Sized {
type Basis: TnuaBasis;
type Config: TnuaSchemeConfig<Scheme = Self> + Asset;
type ActionDiscriminant: TnuaActionDiscriminant;
type ActionState: TnuaActionState<Basis = Self::Basis, Discriminant = Self::ActionDiscriminant>;
#[doc(hidden)]
const NUM_VARIANTS: usize;
fn discriminant(&self) -> Self::ActionDiscriminant;
#[doc(hidden)]
fn variant_idx(&self) -> usize {
self.discriminant().variant_idx()
}
#[doc(hidden)]
fn into_action_state_variant(self, config: &Self::Config) -> Self::ActionState;
#[doc(hidden)]
fn update_in_action_state(
self,
action_state_enum: &mut Self::ActionState,
) -> TnuaUpdateInActionStateResult<Self>;
#[doc(hidden)]
fn initiation_decision(
&self,
config: &Self::Config,
sensors: &<Self::Basis as TnuaBasis>::Sensors<'_>,
ctx: TnuaActionContext<Self::Basis>,
being_fed_for: &Stopwatch,
) -> TnuaActionInitiationDirective;
}
#[doc(hidden)]
pub enum TnuaUpdateInActionStateResult<S: TnuaScheme> {
Success,
WrongVariant(S),
}
pub trait TnuaSchemeConfig: Serialize + for<'de> Deserialize<'de> {
type Scheme: TnuaScheme<Config = Self>;
#[doc(hidden)]
fn basis_config(&self) -> &<<Self::Scheme as TnuaScheme>::Basis as TnuaBasis>::Config;
fn write_if_not_exist(&self, path: impl AsRef<Path>) -> std::io::Result<()> {
let serialized = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::new())
.expect("Should be able to serialize all configs to RON");
let file = File::options().write(true).create_new(true).open(path);
if let Err(err) = &file
&& err.kind() == ErrorKind::AlreadyExists
{
return Ok(());
}
use std::io::Write;
write!(file?, "{}", serialized)
}
}
#[derive(Debug)]
pub struct TnuaBasisContext<'a> {
pub frame_duration: Float,
pub tracker: &'a TnuaRigidBodyTracker,
pub up_direction: Dir3,
}
pub trait TnuaBasis: Default + 'static + Send + Sync {
type Config: Send + Sync + Clone + Serialize + for<'de> Deserialize<'de>;
type Memory: Send + Sync + Default;
type Sensors<'a>: TnuaSensors<'a>;
fn apply(
&self,
config: &Self::Config,
memory: &mut Self::Memory,
sensors: &Self::Sensors<'_>,
ctx: TnuaBasisContext,
motor: &mut TnuaMotor,
);
#[allow(clippy::too_many_arguments)]
fn get_or_create_sensors<'a: 'b, 'b>(
up_direction: Dir3,
config: &'a Self::Config,
memory: &Self::Memory,
entities: &'a mut <Self::Sensors<'static> as TnuaSensors<'static>>::Entities,
proximity_sensors_query: &'b Query<(&TnuaProximitySensor, Has<TnuaGhostSensor>)>,
controller_entity: Entity,
commands: &mut Commands,
has_ghost_overwrites: bool,
) -> Option<Self::Sensors<'b>>;
fn ghost_sensor_overwrites<'a>(
ghost_overwrites: &'a mut <Self::Sensors<'static> as TnuaSensors<'static>>::GhostOverwrites,
entities: &<Self::Sensors<'static> as TnuaSensors<'static>>::Entities,
) -> impl Iterator<Item = (&'a mut TnuaGhostOverwrite, Entity)>;
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum TnuaActionLifecycleStatus {
Initiated,
CancelledFrom,
StillFed,
NoLongerFed,
CancelledInto,
}
impl TnuaActionLifecycleStatus {
pub fn directive_simple(&self) -> TnuaActionLifecycleDirective {
match self {
TnuaActionLifecycleStatus::Initiated => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::CancelledFrom => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::StillFed => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::NoLongerFed => TnuaActionLifecycleDirective::Finished,
TnuaActionLifecycleStatus::CancelledInto => TnuaActionLifecycleDirective::Finished,
}
}
pub fn directive_simple_reschedule(
&self,
after_seconds: Float,
) -> TnuaActionLifecycleDirective {
match self {
TnuaActionLifecycleStatus::Initiated => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::CancelledFrom => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::StillFed => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::NoLongerFed => {
TnuaActionLifecycleDirective::Reschedule { after_seconds }
}
TnuaActionLifecycleStatus::CancelledInto => {
TnuaActionLifecycleDirective::Reschedule { after_seconds }
}
}
}
pub fn directive_linger(&self) -> TnuaActionLifecycleDirective {
match self {
TnuaActionLifecycleStatus::Initiated => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::CancelledFrom => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::StillFed => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::NoLongerFed => TnuaActionLifecycleDirective::StillActive,
TnuaActionLifecycleStatus::CancelledInto => TnuaActionLifecycleDirective::Finished,
}
}
pub fn just_started(&self) -> bool {
match self {
TnuaActionLifecycleStatus::Initiated => true,
TnuaActionLifecycleStatus::CancelledFrom => true,
TnuaActionLifecycleStatus::StillFed => false,
TnuaActionLifecycleStatus::NoLongerFed => false,
TnuaActionLifecycleStatus::CancelledInto => false,
}
}
pub fn is_active(&self) -> bool {
match self {
TnuaActionLifecycleStatus::Initiated => true,
TnuaActionLifecycleStatus::CancelledFrom => true,
TnuaActionLifecycleStatus::StillFed => true,
TnuaActionLifecycleStatus::NoLongerFed => false,
TnuaActionLifecycleStatus::CancelledInto => false,
}
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum TnuaActionLifecycleDirective {
StillActive,
Finished,
Reschedule {
after_seconds: Float,
},
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum TnuaActionInitiationDirective {
Reject,
Delay,
Allow,
}
pub trait TnuaAction<B: TnuaBasis>: 'static + Send + Sync {
type Config: Send + Sync + Clone + Serialize + for<'de> Deserialize<'de>;
type Memory: Send + Sync + Default;
fn initiation_decision(
&self,
config: &Self::Config,
sensors: &B::Sensors<'_>,
ctx: TnuaActionContext<B>,
being_fed_for: &Stopwatch,
) -> TnuaActionInitiationDirective;
fn apply(
&self,
config: &Self::Config,
memory: &mut Self::Memory,
sensors: &B::Sensors<'_>,
ctx: TnuaActionContext<B>,
lifecycle_status: TnuaActionLifecycleStatus,
motor: &mut TnuaMotor,
) -> TnuaActionLifecycleDirective;
#[allow(unused_variables)]
fn influence_basis(
&self,
config: &Self::Config,
memory: &Self::Memory,
ctx: TnuaBasisContext,
basis_input: &B,
basis_config: &B::Config,
basis_memory: &mut B::Memory,
) {
}
}
pub trait TnuaActionDiscriminant:
'static + Send + Sync + Copy + Clone + PartialEq + Eq + core::fmt::Debug
{
#[doc(hidden)]
fn variant_idx(&self) -> usize;
}
pub trait TnuaActionState: 'static + Send + Sync {
type Basis: TnuaBasis;
type Discriminant: TnuaActionDiscriminant;
fn discriminant(&self) -> Self::Discriminant;
#[doc(hidden)]
fn variant_idx(&self) -> usize {
self.discriminant().variant_idx()
}
#[doc(hidden)]
fn interface(&self) -> &dyn TnuaActionStateInterface<Self::Basis>;
#[doc(hidden)]
fn interface_mut(&mut self) -> &mut dyn TnuaActionStateInterface<Self::Basis>;
#[doc(hidden)]
fn modify_basis_config(&self, basis_config: &mut <Self::Basis as TnuaBasis>::Config);
}
#[derive(Clone)]
pub struct TnuaBasisAccess<'a, B: TnuaBasis> {
pub input: &'a B,
pub config: &'a B::Config,
pub memory: &'a B::Memory,
}
pub struct TnuaActionContext<'a, B: TnuaBasis> {
pub frame_duration: Float,
pub tracker: &'a TnuaRigidBodyTracker,
pub up_direction: Dir3,
pub basis: &'a TnuaBasisAccess<'a, B>,
}
impl<'a, B: TnuaBasis> TnuaActionContext<'a, B> {
pub fn as_basis_context(&self) -> TnuaBasisContext<'a> {
TnuaBasisContext {
frame_duration: self.frame_duration,
tracker: self.tracker,
up_direction: self.up_direction,
}
}
pub fn frame_duration_as_duration(&self) -> Duration {
#[allow(clippy::useless_conversion)]
Duration::from_secs_f64(self.frame_duration.into())
}
}
pub trait TnuaConfigModifier<C> {
fn modify_config(&self, config: &mut C);
}