use crate::{ fighter::{ animation::AnimKind, collision, state::{ FighterState, FighterStateContext, FighterStateImpl, air, fall::FallState, ground::GroundedStateCommon, land::{LandingState, 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 FighterStateContext) -> Option { if state_context.is_current_animation_finished() { Some(FighterState::Fall(FallState)) } else { None } } fn update(&mut self, state_context: &mut FighterStateContext) { state_context.velocity.0 *= state_context.fighter_manifest.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 FighterStateContext, ) -> Option { if let Some(res) = collision::air_collide_with_stage(state_context) { // Adjust translation state_context.translation.0 = res.hit_position - state_context.ecb.get_bottom_point(); let grounded_common = GroundedStateCommon { current_line_id: res.line_id, }; Some(FighterState::Land(LandingState { grounded_common, landing_type: LandingType::Special, })) } else { None } } fn on_enter(&mut self, state_context: &mut FighterStateContext) { state_context.play_animation(AnimKind::AirDodge, false); state_context.velocity.0 = self.direction * state_context.fighter_manifest.attributes.air_dodge_velocity; } } pub fn check_input(state_context: &FighterStateContext) -> Option { state_context .input .has_command(input::FighterCommands::Shield) .then(|| { FighterState::AirDodge(AirDodgeState { direction: state_context .input .get_last_frame() .movement .normalize_or_zero(), }) }) }