Implement hitstop shake (#42)
CI / build-and-test (push) Successful in 1m22s

Reviewed-on: #42
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #42.
This commit is contained in:
2026-09-18 11:56:53 +00:00
committed by EIREXE
parent b9f1a52bbc
commit 2b5af49e15
19 changed files with 201 additions and 57 deletions
+4
View File
@@ -82,6 +82,10 @@ impl HitStop {
Self(frames)
}
pub fn calculate_shake_frames(&self, game_settings: &GameSettings) -> u32 {
(FGi32::from_num(self.0) * game_settings.fighter_common.visual_shake_frames_per_hitstop_frame).floor().to_num()
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+21 -14
View File
@@ -5,15 +5,8 @@ use fixed_math::SinCos;
use super::*;
use crate::{
fighter::{
Fighter, FighterFacingDirection, FighterHitboxes, FighterHitstop,
FighterPendingHitReaction, FighterStaleMoveQueue, errors::FighterErrors,
manifest::FighterManifest,
},
game_settings::GameSettings,
math::{int::FGi32, vec::FGVec2},
player::Player,
schedule::GameplaySet,
scripting::FighterAttackScript,
Fighter, FighterFacingDirection, FighterHitboxes, FighterHitstop, FighterHitstopShake, FighterHitstopShakeKind, FighterHitstopShakeRole, FighterPendingHitReaction, FighterStaleMoveQueue, errors::FighterErrors, manifest::FighterManifest, state::FighterState,
}, game_settings::GameSettings, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript,
};
pub fn resolve_fighter_hits(
@@ -24,8 +17,9 @@ pub fn resolve_fighter_hits(
&FighterHitboxes,
&Player,
&mut FighterHitstop,
&mut FighterHitstopShake,
)>,
mut receiver: Query<(&mut FighterDamage, &Fighter, &mut FighterPendingHitReaction)>,
mut receiver: Query<(&mut FighterDamage, &Fighter, &mut FighterPendingHitReaction, &FighterState)>,
attack_scripts: Res<Assets<FighterAttackScript>>,
game_settings: Res<GameSettings>,
fighter_manifests: Res<Assets<FighterManifest>>,
@@ -40,7 +34,7 @@ pub fn resolve_fighter_hits(
.position(|hit| hit.defender != defender)
.map_or(hits.len(), |offset| hit_offset + offset);
let Ok((mut receiver_damage, fighter, mut pending_reaction)) = receiver.get_mut(defender)
let Ok((mut receiver_damage, fighter, mut pending_reaction, receiver_state)) = receiver.get_mut(defender)
else {
hit_offset = next_defender;
continue;
@@ -49,7 +43,7 @@ pub fn resolve_fighter_hits(
let damage_data = hits[hit_offset..next_defender]
.iter()
.filter_map(|hit| {
let Ok((stale_move_queue, facing_dir, _hitboxes, player, _)) =
let Ok((stale_move_queue, facing_dir, _hitboxes, player, _, _)) =
attacker.get(hit.attacker)
else {
return None;
@@ -115,10 +109,23 @@ pub fn resolve_fighter_hits(
receiver_damage.0 += total_damage_received_this_frame;
for (attacker_entity, _, reaction) in &reactions {
let Ok((.., mut hitstop)) = attacker.get_mut(*attacker_entity) else {
let Ok((.., mut hitstop, mut hitstop_shake)) = attacker.get_mut(*attacker_entity) else {
continue;
};
hitstop.remaining_frames = reaction.hitstop_frames.max(hitstop.remaining_frames);
if hitstop.remaining_frames < reaction.hitstop_frames.0 {
let shake_kind = if receiver_state.is_visually_grounded() {
FighterHitstopShakeKind::Horizontal
} else {
FighterHitstopShakeKind::Vertical
};
hitstop.remaining_frames = reaction.hitstop_frames.0;
let shake_frames = reaction.hitstop_frames.calculate_shake_frames(&game_settings);
hitstop_shake.total_frames = shake_frames;
hitstop_shake.remaining_frames = shake_frames;
hitstop_shake.kind = shake_kind;
hitstop_shake.role = FighterHitstopShakeRole::Attacker;
}
}
pending_reaction.0 = reactions