123 lines
4.7 KiB
Rust
123 lines
4.7 KiB
Rust
use crate::{
|
|
fighter::{
|
|
FighterInvulnerabilityState, animation::AnimKind, state::{
|
|
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext, UpdateContext, air::{self, AirborneStateCommon}, fall::FallState, land::{LandingState, LandingType},
|
|
},
|
|
}, input, math::vec::FGVec2, stage::line::{StageLineID, StagePolyLineSegmentType},
|
|
};
|
|
|
|
#[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<FighterState> {
|
|
if state_context.is_current_animation_finished() {
|
|
Some(FighterState::Fall(FallState::new(
|
|
AirborneStateCommon::default(),
|
|
)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, state_context: &mut UpdateContext) {
|
|
let airdodge_intangible_end = state_context.fighter_attributes.air_dodge_intangible_start + state_context.fighter_attributes.air_dodge_intangible_duration;
|
|
if state_context.animation_frame == state_context.fighter_attributes.air_dodge_intangible_start {
|
|
*state_context.vulnerability = FighterInvulnerabilityState::Intangible { frames_remaining: Some(state_context.fighter_attributes.air_dodge_intangible_duration) };
|
|
} else if state_context.animation_frame >= airdodge_intangible_end {
|
|
*state_context.vulnerability = FighterInvulnerabilityState::Vulnerable;
|
|
}
|
|
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<FighterState> {
|
|
let can_wavesnap = self.direction.y.is_zero() || self.direction.y.is_negative();
|
|
|
|
if can_wavesnap {
|
|
for (poly_idx, poly) in state_context.stage_collision.stage_polys.iter().enumerate() {
|
|
for (segment_idx, segment) in poly.segments.iter().enumerate() {
|
|
if !matches!(segment.segment_type, StagePolyLineSegmentType::Floor) {
|
|
continue;
|
|
}
|
|
let bottom_point = state_context.translation.0 + state_context.ecb.get_bottom_point();
|
|
let point_in_segment = segment.segment.closest_point(bottom_point);
|
|
let dist_to_segment = point_in_segment.distance(bottom_point);
|
|
if dist_to_segment <= state_context.waveland_snap_distance {
|
|
state_context.ecb.bottom = point_in_segment;
|
|
state_context.ecb.rebase_onto_bottom();
|
|
state_context.surface.land(StageLineID { polygon: poly_idx, segment: segment_idx }, state_context.ledge_info);
|
|
return Some(FighterState::Land(LandingState::create(LandingType::Special)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
air::collision_interrupt(state_context, LandingType::Special, false)
|
|
}
|
|
|
|
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
|
state_context.play_animation(AnimKind::AirDodge, false);
|
|
|
|
state_context
|
|
.input
|
|
.clear_command(input::FighterCommands::Shield);
|
|
state_context
|
|
.input
|
|
.clear_command(input::FighterCommands::GuardHold);
|
|
|
|
state_context.velocity.0 =
|
|
self.direction * state_context.fighter_attributes.air_dodge_velocity;
|
|
}
|
|
|
|
fn is_visually_grounded<'a>(&'a self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn surface_policy(&self) -> super::SurfacePolicy {
|
|
super::SurfacePolicy::Airborne(crate::fighter::surface::EcbLockPolicy::StartOnDetach)
|
|
}
|
|
|
|
fn on_exit(&mut self, state_context: &mut super::ExitContext) {
|
|
*state_context.vulnerability = FighterInvulnerabilityState::Vulnerable;
|
|
}
|
|
|
|
fn name(&self) -> String {
|
|
Self::NAME.to_owned()
|
|
}
|
|
}
|
|
|
|
pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
|
|
state_context
|
|
.input
|
|
.has_command(input::FighterCommands::Shield)
|
|
.then_some(())
|
|
.or_else(|| {
|
|
state_context
|
|
.input
|
|
.has_command(input::FighterCommands::GuardHold)
|
|
.then_some(())
|
|
})
|
|
.map(|_| {
|
|
FighterState::AirDodge(AirDodgeState {
|
|
direction: state_context
|
|
.input
|
|
.get_last_frame()
|
|
.movement
|
|
.normalize_or_zero(),
|
|
})
|
|
})
|
|
}
|