Add ledge getup, ledge intangibility limit & fix walk-off velocity (#93)
CI / build-and-test (push) Successful in 1m40s
CI / production-build (push) Successful in 12m14s

Reviewed-on: #93
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #93.
This commit is contained in:
2026-10-13 15:52:10 +00:00
committed by EIREXE
parent 4fe1b24b1a
commit f1da5c1172
12 changed files with 208 additions and 55 deletions
+25 -10
View File
@@ -20,7 +20,7 @@ use crate::fighter::visual::FighterAnimations;
use crate::fighter::{
Fighter, FighterDamage, FighterECB, FighterFacingDirection, FighterHitboxes, FighterHitstop,
FighterHitstopShake, FighterHitstopShakeKind, FighterHitstopShakeRole,
FighterInvulnerabilityState, FighterLedgeGrabLockout, FighterPendingRespawn,
FighterInvulnerabilityState, FighterLedgeInformation, FighterPendingRespawn,
FighterPresentationSnap, FighterPreviousTranslation, FighterSurfaceState, FighterTranslation,
FighterVelocity,
};
@@ -56,6 +56,7 @@ pub mod wait;
pub mod walk;
pub mod ledge_wait;
pub mod ledge_action;
pub mod ledge_grab;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum SurfacePolicy {
@@ -139,7 +140,7 @@ pub struct AnimationStatus {
pub struct InterruptContext<'a> {
pub input: &'a FighterInput,
pub facing_direction: &'a mut FighterFacingDirection,
pub ledge_grab_lockout: &'a mut FighterLedgeGrabLockout,
pub ledge_grab_lockout: &'a mut FighterLedgeInformation,
pub game_settings: &'a GameSettings,
pub fighter_attributes: &'a FighterAttributes,
pub surface: &'a FighterSurfaceState,
@@ -171,6 +172,7 @@ pub struct EnterContext<'a> {
pub presentation_snap: &'a mut FighterPresentationSnap,
pub surface: &'a mut FighterSurfaceState,
pub vulnerability: &'a mut FighterInvulnerabilityState,
pub ledge_info: &'a mut FighterLedgeInformation,
pub fighter_attributes: &'a FighterAttributes,
pub attack_script_assets: &'a FighterAttackScriptAssets,
pub game_settings: &'a GameSettings,
@@ -217,6 +219,7 @@ pub struct UpdateContext<'a> {
pub surface: &'a mut FighterSurfaceState,
pub knockback_velocity: &'a mut FighterKnockbackVelocity,
pub vulnerability: &'a mut FighterInvulnerabilityState,
pub ledge_info: &'a mut FighterLedgeInformation,
pub game_settings: &'a GameSettings,
/// Root motion in the fighter's gameplay plane: X is world-right and Y is
/// world-up. Animation +Z is forward and is mirrored by facing direction.
@@ -256,8 +259,8 @@ pub struct CollisionContext<'a> {
pub stage_collision: &'a StageCollision,
pub leave_ground_ecb_lock_duration: u32,
pub presentation_snap: &'a mut FighterPresentationSnap,
pub ledge_info: &'a mut FighterLedgeInformation,
pub fighter_ledge_collision_box: FGRect,
pub ledge_grab_lockout_active: bool,
pub downward_stick_threshold: FGi32,
}
@@ -282,7 +285,7 @@ impl CollisionContext<'_> {
pub fn air_collide_with_ledges(
&self,
) -> Option<crate::fighter::collision::AirCollisionWithLedgeResult> {
if self.ledge_grab_lockout_active {
if self.ledge_info.is_regrab_lockout_active() {
return None;
}
crate::fighter::collision::air_collide_with_ledges(
@@ -305,6 +308,7 @@ fighter_states! {
Land => land::LandingState,
LedgeWait => ledge_wait::LedgeWaitState,
LedgeAction => ledge_action::LedgeActionState,
LedgeGrab => ledge_grab::LedgeGrabState,
AirDodge => air_dodge::AirDodgeState,
Turn => turn::TurnState,
GroundAttack => ground_attack::GroundAttackState,
@@ -330,7 +334,7 @@ pub struct FighterFrameQuery {
pub facing_direction: &'static mut FighterFacingDirection,
pub animation_frame: &'static mut FighterAnimationFrame,
pub vulnerability: &'static mut FighterInvulnerabilityState,
pub ledge_grab_lockout: &'static mut FighterLedgeGrabLockout,
pub ledge_information: &'static mut FighterLedgeInformation,
pub fighter: &'static Fighter,
pub bone_matrices: &'static FighterBoneMatrices,
}
@@ -366,7 +370,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
InterruptContext {
input: &self.input,
facing_direction: &mut self.facing_direction,
ledge_grab_lockout: &mut self.ledge_grab_lockout,
ledge_grab_lockout: &mut self.ledge_information,
game_settings,
fighter_attributes: attributes,
animation,
@@ -398,6 +402,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
surface: &mut self.surface,
vulnerability: &mut self.vulnerability,
presentation_snap: &mut self.presentation_snap,
ledge_info: &mut self.ledge_information,
fighter_attributes: attributes,
attack_script_assets,
game_settings,
@@ -445,6 +450,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
animation_root_position,
animation_frame: self.animation_frame.frame,
vulnerability: &mut self.vulnerability,
ledge_info: &mut self.ledge_information,
fighter_attributes: attributes,
attack_scripts,
attack_script_assets,
@@ -471,6 +477,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
velocity: &mut self.velocity,
surface: &mut self.surface,
ecb: &mut self.ecb,
ledge_info: &mut self.ledge_information,
prev_ecb: &self.prev_ecb,
stage_collision,
leave_ground_ecb_lock_duration: game_settings
@@ -478,7 +485,6 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
.leave_ground_ecb_frame_lock_duration,
downward_stick_threshold: game_settings.input_common.run_stick_threshold,
fighter_ledge_collision_box,
ledge_grab_lockout_active: self.ledge_grab_lockout.is_active(),
}
}
}
@@ -494,6 +500,7 @@ pub struct StatefulFighterQuery {
pub player: &'static Player,
pub pending_hit_reaction: &'static mut FighterPendingHitReaction,
pub hitstop: &'static mut FighterHitstop,
pub ledge_info: &'static mut FighterLedgeInformation,
pub hitstop_shake: &'static mut FighterHitstopShake,
pub pending_respawn: &'static mut FighterPendingRespawn,
pub damage: &'static mut FighterDamage,
@@ -703,6 +710,7 @@ pub fn state_collision_interrupt_system(
mut pending_respawn,
player,
mut damage,
mut ledge_info,
..
} = fighter;
@@ -751,6 +759,9 @@ pub fn state_collision_interrupt_system(
// reset percentage
*damage = FighterDamage::default();
// reset ledge
*ledge_info = FighterLedgeInformation::default();
transition_state(
&mut state,
@@ -829,15 +840,20 @@ mod tests {
let mut velocity = FighterVelocity::default();
let mut surface = FighterSurfaceState::default();
let mut ecb = FighterECB::default();
let mut ledge_info = FighterLedgeInformation {
regrab_lockout_remaining_frames: 5,
..default()
};
let previous_ecb = FighterPreviousECB::from(ecb);
let mut presentation_snap = FighterPresentationSnap::default();
let mut context = CollisionContext {
let context = CollisionContext {
translation: &mut translation,
prev_translation: &previous_translation,
input: &input,
velocity: &mut velocity,
surface: &mut surface,
ecb: &mut ecb,
ledge_info: &mut ledge_info,
prev_ecb: &previous_ecb,
stage_collision: &stage_collision,
leave_ground_ecb_lock_duration: 0,
@@ -846,12 +862,11 @@ mod tests {
origin: FGVec2::lit("-1", "1"),
end: FGVec2::lit("1", "-1"),
},
ledge_grab_lockout_active: true,
downward_stick_threshold: FGi32::ONE,
};
assert!(context.air_collide_with_ledges().is_none());
context.ledge_grab_lockout_active = false;
context.ledge_info.regrab_lockout_remaining_frames = 0;
assert_eq!(
context.air_collide_with_ledges().map(|result| result.ledge),
Some(ledge_id)