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
+8 -21
View File
@@ -4,23 +4,17 @@ use super::{
};
use crate::fighter::animation::AnimKind;
use crate::fighter::state::FighterState;
use crate::fighter::state::ground::GroundedStateCommon;
#[derive(Debug, Clone, Hash)]
pub struct WalkState {
pub grounded_common: GroundedStateCommon,
}
pub struct WalkState;
impl FighterStateImpl for WalkState {
const NAME: &'static str = "Walk";
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
if let Some(state) =
ground::grounded_movement_common_interrupts(state_context, &self.grounded_common)
{
if let Some(state) = ground::grounded_movement_common_interrupts(state_context) {
return Some(state);
}
wait::check_input(state_context, &self.grounded_common)
.or_else(|| dash::check_input(state_context, &self.grounded_common))
wait::check_input(state_context).or_else(|| dash::check_input(state_context))
}
fn on_enter(&mut self, state_context: &mut EnterContext) {
@@ -54,26 +48,19 @@ impl FighterStateImpl for WalkState {
&mut self,
state_context: &mut CollisionContext,
) -> Option<FighterState> {
ground::collision_interrupt(state_context, &mut self.grounded_common, false)
ground::collision_interrupt(state_context, false)
}
fn is_visually_grounded<'a>(
&'a self,
) -> bool {
fn is_visually_grounded<'a>(&'a self) -> bool {
true
}
}
pub fn check_input(
state_context: &InterruptContext,
ground_state_common: &GroundedStateCommon,
) -> Option<FighterState> {
pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
let input_frame = state_context.input.get_last_frame();
if !input_frame.movement.x.is_zero() {
return Some(FighterState::Walk(WalkState {
grounded_common: ground_state_common.clone(),
}));
return Some(FighterState::Walk(WalkState));
}
None