use bevy::prelude::*;
use crate::basis_capabilities::TnuaBasisWithGround;
use crate::controller::TnuaActionFlowStatus;
use crate::{TnuaActionDiscriminant, prelude::*};
#[derive(Default)]
pub struct TnuaAirActionsTracker {
considered_in_air: bool,
}
pub trait TnuaAirActionDefinition: TnuaScheme {
fn is_air_action(action: Self::ActionDiscriminant) -> bool;
}
impl TnuaAirActionsTracker {
pub fn update<S>(
&mut self,
controller: &TnuaController<S>,
) -> TnuaAirActionsUpdate<S::ActionDiscriminant>
where
S: TnuaScheme + TnuaAirActionDefinition,
S::Basis: TnuaBasisWithGround,
{
match controller.action_flow_status() {
TnuaActionFlowStatus::NoAction => self.update_regardless_of_action(controller),
TnuaActionFlowStatus::ActionOngoing(action_discriminant) => {
if controller
.action_discriminant()
.is_some_and(S::is_air_action)
{
if self.considered_in_air {
TnuaAirActionsUpdate::NoChange
} else {
self.considered_in_air = true;
TnuaAirActionsUpdate::AirActionStarted(*action_discriminant)
}
} else {
self.update_regardless_of_action(controller)
}
}
TnuaActionFlowStatus::ActionStarted(action_discriminant)
| TnuaActionFlowStatus::Cancelled {
old: _,
new: action_discriminant,
} => {
if controller
.action_discriminant()
.is_some_and(S::is_air_action)
{
self.considered_in_air = true;
TnuaAirActionsUpdate::AirActionStarted(*action_discriminant)
} else {
self.update_regardless_of_action(controller)
}
}
TnuaActionFlowStatus::ActionEnded(_) => {
let result = self.update_regardless_of_action(controller);
if self.considered_in_air {
TnuaAirActionsUpdate::ActionFinishedInAir
} else {
result
}
}
}
}
fn update_regardless_of_action<S>(
&mut self,
controller: &TnuaController<S>,
) -> TnuaAirActionsUpdate<S::ActionDiscriminant>
where
S: TnuaScheme,
S::Basis: TnuaBasisWithGround,
{
if let Ok(basis_access) = controller.basis_access() {
if S::Basis::is_airborne(&basis_access) {
if self.considered_in_air {
TnuaAirActionsUpdate::NoChange
} else {
self.considered_in_air = true;
TnuaAirActionsUpdate::FreeFallStarted
}
} else if self.considered_in_air {
self.considered_in_air = false;
TnuaAirActionsUpdate::JustLanded
} else {
TnuaAirActionsUpdate::NoChange
}
} else {
TnuaAirActionsUpdate::NoChange
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum TnuaAirActionsUpdate<D: TnuaActionDiscriminant> {
NoChange,
FreeFallStarted,
AirActionStarted(D),
ActionFinishedInAir,
JustLanded,
}
#[derive(Component)]
pub struct TnuaSimpleAirActionsCounter<S: TnuaScheme> {
tracker: TnuaAirActionsTracker,
current_action: Option<(S::ActionDiscriminant, usize)>,
air_actions_count: usize,
}
impl<S: TnuaScheme> Default for TnuaSimpleAirActionsCounter<S> {
fn default() -> Self {
Self {
tracker: Default::default(),
current_action: None,
air_actions_count: 0,
}
}
}
impl<S> TnuaSimpleAirActionsCounter<S>
where
S: TnuaScheme + TnuaAirActionDefinition,
S::Basis: TnuaBasisWithGround,
{
pub fn update(&mut self, controller: &TnuaController<S>)
where
S: TnuaScheme + TnuaAirActionDefinition,
S::Basis: TnuaBasisWithGround,
{
let update = self.tracker.update(controller);
match update {
TnuaAirActionsUpdate::NoChange => {}
TnuaAirActionsUpdate::FreeFallStarted => {
self.current_action = None;
self.air_actions_count += 1;
}
TnuaAirActionsUpdate::AirActionStarted(action_discriminant) => {
self.current_action = Some((action_discriminant, self.air_actions_count));
self.air_actions_count += 1;
}
TnuaAirActionsUpdate::ActionFinishedInAir => {
self.current_action = None;
}
TnuaAirActionsUpdate::JustLanded => {
self.current_action = None;
self.air_actions_count = 0;
}
}
}
pub fn reset_count_to(&mut self, count: usize) {
self.air_actions_count = count;
}
pub fn get_count_mut(&mut self) -> Option<&mut usize> {
if self.air_actions_count == 0 {
None
} else {
Some(&mut self.air_actions_count)
}
}
pub fn reset_count(&mut self) {
if let Some(count) = self.get_count_mut() {
*count = 1;
}
}
pub fn air_count_for(&self, action: S::ActionDiscriminant) -> usize {
if let Some((current_action, actions)) = self.current_action
&& current_action == action
{
return actions;
}
self.air_actions_count
}
}