Implement Melee-style knockback stacking
CI / build-and-test (pull_request) Successful in 13m1s
CI / production-build (pull_request) Successful in 31m59s

This commit is contained in:
2026-10-22 15:20:12 +02:00
parent 7f720119fa
commit fd89261f78
13 changed files with 260 additions and 30 deletions
+28 -2
View File
@@ -7,8 +7,8 @@ use bevy::prelude::*;
use crate::fighter::animation::{AnimKind, FighterAnimationFrame};
use crate::fighter::attack::{
FighterAttackScriptAssets, FighterKnockbackVelocity, FighterPendingHitReaction,
FighterStaleMoveQueue, HitStop,
FighterAttackScriptAssets, FighterKnockbackState, FighterKnockbackVelocity,
FighterPendingHitReaction, FighterStaleMoveQueue, HitStop,
};
use crate::fighter::attributes::FighterAttributes;
use crate::fighter::baked_animation::{BakedFighterAnimations, FighterBoneMatrices};
@@ -167,6 +167,7 @@ pub struct EnterContext<'a> {
pub velocity: &'a mut FighterVelocity,
pub translation: &'a mut FighterTranslation,
pub knockback_velocity: &'a mut FighterKnockbackVelocity,
pub knockback_state: &'a mut FighterKnockbackState,
pub stale_move_queue: &'a mut FighterStaleMoveQueue,
pub facing_direction: &'a mut FighterFacingDirection,
pub animation_frame: &'a mut FighterAnimationFrame,
@@ -204,6 +205,8 @@ impl EnterContext<'_> {
pub struct ExitContext<'a> {
pub input: &'a mut FighterInput,
pub hitboxes: &'a mut FighterHitboxes,
pub knockback_velocity: &'a mut FighterKnockbackVelocity,
pub knockback_state: &'a mut FighterKnockbackState,
pub surface: &'a mut FighterSurfaceState,
pub vulnerability: &'a mut FighterInvulnerabilityState,
}
@@ -216,6 +219,7 @@ pub struct UpdateContext<'a> {
pub velocity: &'a mut FighterVelocity,
pub surface: &'a mut FighterSurfaceState,
pub knockback_velocity: &'a mut FighterKnockbackVelocity,
pub knockback_state: &'a mut FighterKnockbackState,
pub vulnerability: &'a mut FighterInvulnerabilityState,
pub ledge_info: &'a mut FighterLedgeInformation,
pub game_settings: &'a GameSettings,
@@ -251,6 +255,8 @@ pub struct CollisionContext<'a> {
pub prev_translation: &'a FighterPreviousTranslation,
pub input: &'a FighterInput,
pub velocity: &'a mut FighterVelocity,
pub knockback_velocity: &'a mut FighterKnockbackVelocity,
pub knockback_state: &'a mut FighterKnockbackState,
pub surface: &'a mut FighterSurfaceState,
pub ecb: &'a mut FighterECB,
pub desired_ecb: &'a mut FighterDesiredECB,
@@ -335,6 +341,7 @@ pub struct FighterFrameQuery {
pub velocity: &'static mut FighterVelocity,
pub surface: &'static mut FighterSurfaceState,
pub knockback_velocity: &'static mut FighterKnockbackVelocity,
pub knockback_state: &'static mut FighterKnockbackState,
pub stale_move_queue: &'static mut FighterStaleMoveQueue,
pub ecb: &'static mut FighterECB,
pub desired_ecb: &'static mut FighterDesiredECB,
@@ -403,6 +410,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
hitboxes: &mut self.hitboxes,
velocity: &mut self.velocity,
knockback_velocity: &mut self.knockback_velocity,
knockback_state: &mut self.knockback_state,
translation: &mut self.translation,
stale_move_queue: &mut self.stale_move_queue,
facing_direction: &mut self.facing_direction,
@@ -424,6 +432,8 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
ExitContext {
input: &mut self.input,
hitboxes: &mut self.hitboxes,
knockback_velocity: &mut self.knockback_velocity,
knockback_state: &mut self.knockback_state,
surface: &mut self.surface,
vulnerability: &mut self.vulnerability,
}
@@ -453,6 +463,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
velocity: &mut self.velocity,
surface: &mut self.surface,
knockback_velocity: &mut self.knockback_velocity,
knockback_state: &mut self.knockback_state,
game_settings,
animation_root_motion_delta,
animation_root_position,
@@ -483,6 +494,8 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
presentation_snap: &mut self.presentation_snap,
input: &self.input,
velocity: &mut self.velocity,
knockback_velocity: &mut self.knockback_velocity,
knockback_state: &mut self.knockback_state,
surface: &mut self.surface,
ecb: &mut self.ecb,
desired_ecb: &mut self.desired_ecb,
@@ -655,9 +668,16 @@ pub fn state_interrupt_system(
}
info!("Entering hitstop kind: {:?}", hitstop_kind);
let stack_previous_knockback = !frame.surface.is_grounded()
&& !frame.knockback_velocity.0.length_squared().is_zero()
&& frame
.knockback_state
.can_stack(game_settings.fighter_common.knockback_stacking_window);
Some(FighterState::Hitstun(HitstunState::new(
reaction,
hitstop_kind,
stack_previous_knockback,
)))
} else if hitstop.remaining_frames > 0 {
None
@@ -753,6 +773,8 @@ pub fn state_collision_interrupt_system(
// Clear hitstop
hitstop.remaining_frames = 0;
frame.knockback_velocity.0 = FGVec2::ZERO;
frame.knockback_state.clear();
// subtract one stock
@@ -846,6 +868,8 @@ mod tests {
let input = FighterInput::default();
let mut translation = FighterTranslation::default();
let mut velocity = FighterVelocity::default();
let mut knockback_velocity = FighterKnockbackVelocity::default();
let mut knockback_state = FighterKnockbackState::default();
let mut surface = FighterSurfaceState::default();
let mut ecb = FighterECB::default();
let mut desired_ecb = FighterDesiredECB {
@@ -865,6 +889,8 @@ mod tests {
prev_translation: &previous_translation,
input: &input,
velocity: &mut velocity,
knockback_velocity: &mut knockback_velocity,
knockback_state: &mut knockback_state,
surface: &mut surface,
ecb: &mut ecb,
desired_ecb: &mut desired_ecb,