Add authoritative fighter surface state (#60)
CI / build-and-test (push) Successful in 1m28s
CI / production-build (push) Successful in 11m59s

## Summary
- add a rollback-tracked `FighterSurfaceState` as the single source of truth for stage contact
- move active stage line ownership out of individual grounded fighter states
- update landing, airborne motion, ledge departure, hit reactions, and debug save states to maintain surface contact
- add tests for contact lifecycle, connected-segment traversal, and ledge departure

Reviewed-on: #60
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #60.
This commit is contained in:
2026-09-25 10:45:27 +00:00
committed by EIREXE
parent 3daf8f2e21
commit d521461625
18 changed files with 411 additions and 301 deletions
+10 -16
View File
@@ -3,7 +3,7 @@ use crate::fighter::{
state::{
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext,
UpdateContext, dash,
ground::{self, GroundedStateCommon, grounded_movement_standstill_common_interrupts},
ground::{self, grounded_movement_standstill_common_interrupts},
walk,
},
};
@@ -16,16 +16,12 @@ pub enum LandingType {
#[derive(Debug, Clone, Hash)]
pub struct LandingState {
pub grounded_common: GroundedStateCommon,
pub landing_type: LandingType,
}
impl LandingState {
pub fn create(grounded_common: GroundedStateCommon, landing_type: LandingType) -> Self {
Self {
grounded_common,
landing_type,
}
pub fn create(landing_type: LandingType) -> Self {
Self { landing_type }
}
}
@@ -34,14 +30,14 @@ impl FighterStateImpl for LandingState {
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
if state_context.is_current_animation_finished() {
grounded_movement_standstill_common_interrupts(state_context, &self.grounded_common)
grounded_movement_standstill_common_interrupts(state_context)
} else if state_context.get_current_animation_frame()
>= state_context.fighter_attributes.landing_iasa
&& let LandingType::Normal = self.landing_type
{
dash::check_input(state_context, &self.grounded_common).or_else(|| {
ground::grounded_movement_common_interrupts(state_context, &self.grounded_common)
.or_else(|| walk::check_input(state_context, &self.grounded_common))
dash::check_input(state_context).or_else(|| {
ground::grounded_movement_common_interrupts(state_context)
.or_else(|| walk::check_input(state_context))
})
} else {
None
@@ -56,7 +52,7 @@ impl FighterStateImpl for LandingState {
&mut self,
state_context: &mut CollisionContext,
) -> Option<FighterState> {
ground::collision_interrupt(state_context, &mut self.grounded_common, true)
ground::collision_interrupt(state_context, true)
}
fn on_enter(&mut self, state_context: &mut EnterContext) {
@@ -69,10 +65,8 @@ impl FighterStateImpl for LandingState {
}
};
}
fn is_visually_grounded<'a>(
&'a self,
) -> bool {
fn is_visually_grounded<'a>(&'a self) -> bool {
true
}
}