Implement Melee-style knockback stacking (#97)
## Summary - stack residual airborne knockback per axis after the configured launch-frame threshold - apply DI after combining the launch vectors while retaining the second hit's hitstun - preserve and decay residual knockback after hitstun - make stacking state rollback-safe and include it in debug save states - clear launch state on landing, ledge interactions, KO, and respawn ## Testing - `cargo check --tests` - `cargo test` (159 tests passed) - `git diff --check` ## Asset configuration For Melee timing, `knockback_stacking_window` should be set to `10`. The local ignored asset package has been updated accordingly. Reviewed-on: #97 Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #97.
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user