use crate::{ fighter::{ animation::AnimKind, state::{ CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext, UpdateContext, air::{self, AirborneStateCommon}, fall::FallState, land::LandingType, }, }, input, math::vec::FGVec2, }; #[derive(Debug, Clone, Hash)] pub struct AirDodgeState { pub direction: FGVec2, } impl FighterStateImpl for AirDodgeState { const NAME: &'static str = "AirDodge"; fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option { if state_context.is_current_animation_finished() { Some(FighterState::Fall(FallState::new(AirborneStateCommon::default()))) } else { None } } fn update(&mut self, state_context: &mut UpdateContext) { state_context.velocity.0 *= state_context.fighter_attributes.air_dodge_decay; air::apply_air_motion( state_context.velocity, state_context.translation, state_context.prev_translation, ); } fn check_collision_interrupt( &mut self, state_context: &mut CollisionContext, ) -> Option { air::collision_interrupt(state_context, LandingType::Special) } fn on_enter(&mut self, state_context: &mut EnterContext) { state_context.play_animation(AnimKind::AirDodge, false); state_context.velocity.0 = self.direction * state_context.fighter_attributes.air_dodge_velocity; } fn is_visually_grounded<'a>( &'a self, ) -> bool { false } } pub fn check_input(state_context: &InterruptContext) -> Option { state_context .input .has_command(input::FighterCommands::Shield) .then(|| { FighterState::AirDodge(AirDodgeState { direction: state_context .input .get_last_frame() .movement .normalize_or_zero(), }) }) }