Implement crouching
CI / build-and-test (pull_request) Successful in 1m29s
CI / production-build (pull_request) Successful in 10m9s

This commit is contained in:
2026-09-25 19:59:53 +02:00
parent b56b7b0421
commit 46c2af8114
4 changed files with 134 additions and 2 deletions
+3
View File
@@ -84,6 +84,9 @@ pub enum AnimKind {
AirDodge, AirDodge,
Fall, Fall,
Turn, Turn,
CrouchEnter,
CrouchIdle,
CrouchExit,
FlinchGround, FlinchGround,
FlinchAir, FlinchAir,
+126
View File
@@ -0,0 +1,126 @@
use super::{
CollisionContext, EnterContext, FighterStateImpl, InterruptContext, UpdateContext, ground,
};
use crate::fighter::animation::AnimKind;
use crate::fighter::state::{FighterState, dash, ground_attack, turn, walk};
use crate::fighter::state::ground::{grounded_movement_common_interrupts, grounded_movement_standstill_common_interrupts};
#[derive(Debug, Clone, Hash)]
pub enum CrouchSubState {
Entering,
Idle,
Exiting
}
#[derive(Debug, Clone, Hash)]
pub struct CrouchState {
sub_state: CrouchSubState
}
pub fn crouch_idle_interrupts(
state_context: &InterruptContext,
) -> Option<FighterState> {
if let Some(state) = ground_attack::check_interrupt(state_context) {
Some(state)
} else if let Some(state) = dash::check_input(state_context) {
Some(state)
} else if let Some(state) = turn::check_input(state_context) {
Some(state)
} else if let Some(state) = walk::check_input(state_context) {
Some(state)
} else {
None
}
}
pub fn crouch_entering_interrupts(
state_context: &InterruptContext,
) -> Option<FighterState> {
if let Some(state) = ground_attack::check_interrupt(state_context) {
Some(state)
} else {
None
}
}
impl FighterStateImpl for CrouchState {
const NAME: &'static str = "Crouch";
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
if let Some(state) = grounded_movement_common_interrupts(state_context) {
return Some(state);
}
match self.sub_state {
CrouchSubState::Entering => {
// While entering the crouch animation we do not allow movement inputs, but we do allow attacks and jumps
if state_context.is_current_animation_finished() {
return Some(FighterState::Crouch(
CrouchState {
sub_state: CrouchSubState::Idle,
}
));
} else {
return crouch_entering_interrupts(state_context);
}
},
CrouchSubState::Idle => {
if state_context.input.get_last_frame().movement.y > -state_context.game_settings.input_common.run_stick_threshold {
return Some(FighterState::Crouch (
CrouchState {
sub_state: CrouchSubState::Exiting,
}
));
} else {
return crouch_idle_interrupts(state_context);
}
},
CrouchSubState::Exiting => {
if state_context.is_current_animation_finished() {
return grounded_movement_standstill_common_interrupts(state_context);
} else {
return crouch_idle_interrupts(state_context);
}
},
}
}
fn on_enter(&mut self, state_context: &mut EnterContext) {
match self.sub_state {
CrouchSubState::Entering => {
state_context.play_animation(AnimKind::CrouchEnter, false);
},
CrouchSubState::Idle => {
state_context.play_animation(AnimKind::CrouchIdle, true);
},
CrouchSubState::Exiting => {
state_context.play_animation(AnimKind::CrouchExit, false);
},
}
}
fn update(&mut self, state_context: &mut UpdateContext) {
ground::update_friction_motion(state_context);
}
fn check_collision_interrupt(
&mut self,
state_context: &mut CollisionContext,
) -> Option<FighterState> {
ground::collision_interrupt(state_context, false)
}
fn is_visually_grounded<'a>(&'a self) -> bool {
true
}
}
pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
let input_frame = state_context.input.get_last_frame();
if input_frame.movement.y <= -state_context.game_settings.input_common.run_stick_threshold {
return Some(FighterState::Crouch(CrouchState {
sub_state: CrouchSubState::Entering
}));
}
None
}
+3 -2
View File
@@ -3,8 +3,7 @@
//! bit-for-bit deterministic for rollback. //! bit-for-bit deterministic for rollback.
use crate::fighter::state::{ use crate::fighter::state::{
CollisionContext, FighterState, InterruptContext, air, dash, fall, ground_attack, turn, wait, CollisionContext, FighterState, InterruptContext, air, crouch, dash, fall, ground_attack, turn, wait, walk,
walk,
}; };
use crate::fighter::{ use crate::fighter::{
FighterAttributes, FighterPreviousTranslation, FighterTranslation, FighterVelocity, FighterAttributes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
@@ -461,6 +460,8 @@ pub fn grounded_movement_standstill_common_interrupts(
Some(state) Some(state)
} else if let Some(state) = turn::check_input(state_context) { } else if let Some(state) = turn::check_input(state_context) {
Some(state) Some(state)
} else if let Some(state) = crouch::check_input(state_context) {
Some(state)
} else if let Some(state) = walk::check_input(state_context) { } else if let Some(state) = walk::check_input(state_context) {
Some(state) Some(state)
} else if let Some(state) = wait::check_input(state_context) { } else if let Some(state) = wait::check_input(state_context) {
+2
View File
@@ -41,6 +41,7 @@ pub mod run;
pub mod turn; pub mod turn;
pub mod wait; pub mod wait;
pub mod walk; pub mod walk;
pub mod crouch;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum SurfacePolicy { pub enum SurfacePolicy {
@@ -211,6 +212,7 @@ impl CollisionContext<'_> {
fighter_states! { fighter_states! {
Wait => wait::WaitState, Wait => wait::WaitState,
Walk => walk::WalkState, Walk => walk::WalkState,
Crouch => crouch::CrouchState,
Dash => dash::DashState, Dash => dash::DashState,
Run => run::RunState, Run => run::RunState,
Fall => fall::FallState, Fall => fall::FallState,