Compare commits

..
Author SHA1 Message Date
EIREXE 37b5d2f974 Implement hitstop shake
CI / build-and-test (pull_request) Successful in 1m30s
2026-09-18 13:54:06 +02:00
17 changed files with 65 additions and 229 deletions
+1 -2
View File
@@ -22,8 +22,7 @@
air_jump_horizontal_velocity: "0.96", air_jump_horizontal_velocity: "0.96",
air_jump_multiplier: "1.0", air_jump_multiplier: "1.0",
dash_initial_velocity: "1.4", dash_initial_velocity: "1.4",
terminal_velocity: "3.0", terminal_velocity: "1.6",
fast_fall_speed: "3.5",
gravity: "0.084", gravity: "0.084",
weight: "100.0", weight: "100.0",
jumpsquat_duration: 5, jumpsquat_duration: 5,
+1 -2
View File
@@ -24,8 +24,7 @@
visual_shake_table: [ visual_shake_table: [
-3.0, -1.5, 1.5, 3.0 -3.0, -1.5, 1.5, 3.0
], ],
attacker_visual_shake_multiplier: 0.4, attacker_visual_shake_multiplier: 0.4
di_max_angle: "18.0"
), ),
stage_manifest_paths: [ stage_manifest_paths: [
"stages/battlefield/test.stage.ron" "stages/battlefield/test.stage.ron"
@@ -1,69 +0,0 @@
use fixed::traits::ToFixed;
use fixed_math::SinCos;
use crate::math::{
int::{FGWide, FGi32},
vec::FGVec2,
};
/// Applies DI to a given attack direction
pub fn apply_directional_influence(
receiver_stick: FGVec2,
attack_angle: FGi32,
di_max_angle: FGi32,
) -> FGVec2 {
let (sin, cos) = attack_angle.sin_cos();
let base_launch_direction = FGVec2::new(cos, sin);
let Some(base_launch_direction) = base_launch_direction.normalize() else {
return FGVec2::ZERO;
};
if receiver_stick.length_squared().is_zero() {
return base_launch_direction;
}
let normal = FGVec2::new(-base_launch_direction.y, base_launch_direction.x);
let di_amount = receiver_stick.dot(normal);
let angle_change = di_amount * di_max_angle.to_fixed::<FGWide>();
let angle_change = angle_change.clamp(
-di_max_angle.to_fixed::<FGWide>(),
di_max_angle.to_fixed::<FGWide>(),
);
let (sin, cos) = (attack_angle + angle_change.to_fixed::<FGi32>()).sin_cos();
FGVec2::new(cos, sin).normalize_or_zero()
}
#[cfg(test)]
mod test {
use crate::{
fighter::attack::directional_influence::apply_directional_influence,
math::{int::FGi32, vec::FGVec2},
};
#[test]
fn di_zero_passes_unmodified() {
let di = apply_directional_influence(FGVec2::ZERO, FGi32::lit("0.0"), FGi32::lit("18.0"));
const EPSILON: FGi32 = FGi32::from_bits(4);
assert!(di.x.abs_diff(FGi32::ONE) < EPSILON);
assert!(di.y.abs_diff(FGi32::ZERO) < EPSILON);
}
#[test]
fn di_applies_on_the_correct_side() {
let di = apply_directional_influence(
FGVec2::lit("0.0", "1.0"),
FGi32::lit("0.0"),
FGi32::lit("18.0"),
);
assert!(di.x > FGi32::ZERO);
assert!(di.y > FGi32::ZERO);
let di = apply_directional_influence(
FGVec2::lit("0.0", "-1.0"),
FGi32::lit("0.0"),
FGi32::lit("18.0"),
);
assert!(di.x > FGi32::ZERO);
assert!(di.y < FGi32::ZERO);
}
}
+1 -2
View File
@@ -1,7 +1,6 @@
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
mod reaction; mod reaction;
pub mod directional_influence;
use bevy::ecs::entity::{EntityMapper, MapEntities}; use bevy::ecs::entity::{EntityMapper, MapEntities};
use bevy::prelude::*; use bevy::prelude::*;
@@ -43,7 +42,7 @@ pub enum HitReactionKind {
#[derive(Debug, Hash, Clone)] #[derive(Debug, Hash, Clone)]
pub struct FighterHitReaction { pub struct FighterHitReaction {
pub launch_velocity: FGi32, pub launch_velocity: FGVec2,
pub launch_angle: FGi32, pub launch_angle: FGi32,
pub hitstop_frames: HitStop, pub hitstop_frames: HitStop,
pub hitstun_frames: u32, pub hitstun_frames: u32,
+5 -3
View File
@@ -1,11 +1,12 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_ggrs::prelude::GgrsSchedule; use bevy_ggrs::prelude::GgrsSchedule;
use fixed_math::SinCos;
use super::*; use super::*;
use crate::{ use crate::{
fighter::{ fighter::{
Fighter, FighterFacingDirection, FighterHitboxes, FighterHitstop, FighterHitstopShake, FighterHitstopShakeKind, FighterHitstopShakeRole, FighterPendingHitReaction, FighterStaleMoveQueue, errors::FighterErrors, manifest::FighterManifest, state::FighterState, Fighter, FighterFacingDirection, FighterHitboxes, FighterHitstop, FighterHitstopShake, FighterHitstopShakeKind, FighterHitstopShakeRole, FighterPendingHitReaction, FighterStaleMoveQueue, errors::FighterErrors, manifest::FighterManifest, state::FighterState,
}, game_settings::GameSettings, math::{int::FGi32}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, }, game_settings::GameSettings, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript,
}; };
pub fn resolve_fighter_hits( pub fn resolve_fighter_hits(
@@ -89,13 +90,14 @@ pub fn resolve_fighter_hits(
} else { } else {
angle angle
}; };
let (sin, cos) = launch_angle.sin_cos();
let launch_direction = FGVec2::new(cos, sin);
let hitstop = HitStop::calculate(FighterDamage(attack_damage), false); let hitstop = HitStop::calculate(FighterDamage(attack_damage), false);
reactions.push(( reactions.push((
attacker_entity, attacker_entity,
attacker_slot, attacker_slot,
FighterHitReaction { FighterHitReaction {
launch_velocity: knockback.get_launch_speed(&game_settings), launch_velocity: launch_direction * knockback.get_launch_speed(&game_settings),
launch_angle, launch_angle,
hitstun_frames: knockback.get_hitstun_frames(&game_settings), hitstun_frames: knockback.get_hitstun_frames(&game_settings),
hitstop_frames: hitstop, hitstop_frames: hitstop,
-1
View File
@@ -37,7 +37,6 @@ pub struct FighterAttributes {
pub ground_friction: FGi32, pub ground_friction: FGi32,
pub terminal_velocity: FGi32, pub terminal_velocity: FGi32,
pub gravity: FGi32, pub gravity: FGi32,
pub fast_fall_speed: FGi32,
pub weight: FGi32, pub weight: FGi32,
pub air_friction: FGi32, pub air_friction: FGi32,
+5 -11
View File
@@ -9,7 +9,7 @@ use bevy_egui::{EguiContexts, egui};
use crate::debug_tools::{DebugSettings, FighterDebugRowKind}; use crate::debug_tools::{DebugSettings, FighterDebugRowKind};
use crate::fighter::animation::FighterAnimationFrame; use crate::fighter::animation::FighterAnimationFrame;
use crate::fighter::attack::{ use crate::fighter::attack::{
self, FighterKnockbackVelocity, FighterLastAttackDebug, FighterPendingHitReaction, FighterKnockbackVelocity, FighterLastAttackDebug, FighterPendingHitReaction,
FighterSolvedHurtboxes, FighterSolvedHurtboxes,
}; };
use crate::fighter::baked_animation::{BakedFighterAnimations, FighterBoneMatrices}; use crate::fighter::baked_animation::{BakedFighterAnimations, FighterBoneMatrices};
@@ -382,14 +382,14 @@ pub fn attack_debug_gather(
&FighterPendingHitReaction, &FighterPendingHitReaction,
&FighterTranslation, &FighterTranslation,
&Fighter, &Fighter,
&FighterInput,
)>, )>,
mut commands: Commands, mut commands: Commands,
manifests: Res<Assets<FighterManifest>>, manifests: Res<Assets<FighterManifest>>,
game_settings: Res<GameSettings>, game_settings: Res<GameSettings>,
) -> Result { ) -> Result {
for (entity, reaction, translation, fighter, input) in query { for (entity, reaction, translation, fighter) in query {
let Some(ref hit) = reaction.0 else { let Some(ref hit) = reaction.0 else {
commands.entity(entity).remove::<FighterLastAttackDebug>();
continue; continue;
}; };
@@ -397,13 +397,7 @@ pub fn attack_debug_gather(
.get(&fighter.manifest) .get(&fighter.manifest)
.ok_or(FighterErrors::FighterManifestNotFound)?; .ok_or(FighterErrors::FighterManifestNotFound)?;
let launch_dir = attack::directional_influence::apply_directional_influence( let mut knockback_vel = FighterKnockbackVelocity(hit.launch_velocity);
input.get_last_frame().movement,
hit.launch_angle,
game_settings.fighter_common.di_max_angle,
);
let mut knockback_vel = FighterKnockbackVelocity(launch_dir * hit.launch_velocity);
let mut fighter_vel = FighterVelocity(FGVec2::ZERO); let mut fighter_vel = FighterVelocity(FGVec2::ZERO);
let mut translation = Vec3::new(translation.x.to_num(), translation.y.to_num(), 0.0); let mut translation = Vec3::new(translation.x.to_num(), translation.y.to_num(), 0.0);
@@ -412,7 +406,7 @@ pub fn attack_debug_gather(
points.reserve(hit.hitstun_frames as usize); points.reserve(hit.hitstun_frames as usize);
for _ in 1..hit.hitstun_frames { for _ in 1..hit.hitstun_frames {
air::integrate_gravity(&mut fighter_vel, &manifest.attributes, false); air::integrate_gravity(&mut fighter_vel, &manifest.attributes);
let vel = knockback_vel.length(); let vel = knockback_vel.length();
let new_vel = vel let new_vel = vel
+2 -2
View File
@@ -38,7 +38,7 @@ use crate::{
AttackKind, FighterAttackPlugin, FighterAttackScriptAssets, FighterDamage, AttackKind, FighterAttackPlugin, FighterAttackScriptAssets, FighterDamage,
FighterKnockbackVelocity, FighterPendingHitReaction, FighterSolvedHurtboxes, FighterKnockbackVelocity, FighterPendingHitReaction, FighterSolvedHurtboxes,
FighterStaleMoveQueue, FighterStaleMoveQueue,
}, baked_animation::{FighterBoneIndexMap, FighterBoneMatrices}, manifest::FighterManifest, state::{FighterState, air::AirborneStateCommon, fall::FallState}, visual::FighterAnimations, }, baked_animation::{FighterBoneIndexMap, FighterBoneMatrices}, manifest::FighterManifest, state::{FighterState, fall::FallState}, visual::FighterAnimations,
}, input::FighterInput, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, stage::line::StageCollision, }, input::FighterInput, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, stage::line::StageCollision,
}; };
use state::state_interrupt_system; use state::state_interrupt_system;
@@ -299,7 +299,7 @@ pub fn spawn_fighter(
visual_root, visual_root,
animations, animations,
attack_scripts, attack_scripts,
FighterState::Fall(FallState::new(AirborneStateCommon::default())), FighterState::Fall(FallState),
animation::FighterAnimationFrame::new(animation::AnimKind::Wait, true), animation::FighterAnimationFrame::new(animation::AnimKind::Wait, true),
( (
baked_animation::FighterBoneMatrices::default(), baked_animation::FighterBoneMatrices::default(),
+4 -43
View File
@@ -2,44 +2,10 @@ use crate::fighter::state::ground::GroundedStateCommon;
use crate::fighter::state::land::{LandingState, LandingType}; use crate::fighter::state::land::{LandingState, LandingType};
use crate::fighter::state::{CollisionContext, FighterState}; use crate::fighter::state::{CollisionContext, FighterState};
use crate::fighter::*; use crate::fighter::*;
use crate::input::FighterCommands;
#[derive(Default, Debug, Clone, Copy, Hash)] pub fn integrate_gravity(velocity: &mut FighterVelocity, attributes: &FighterAttributes) {
pub struct AirborneStateCommon {
pub is_fast_falling: bool,
}
pub fn process_fast_fall(
airborne_state_common: &mut AirborneStateCommon,
input: &mut FighterInput,
velocity: &mut FighterVelocity,
attributes: &FighterAttributes,
) {
if velocity.y.is_negative() {
if input.has_command(FighterCommands::SmashMoveDown) {
airborne_state_common.is_fast_falling = true;
}
input.clear_command(FighterCommands::SmashMoveDown);
}
if airborne_state_common.is_fast_falling {
velocity.y = -attributes.fast_fall_speed;
}
}
pub fn integrate_gravity(
velocity: &mut FighterVelocity,
attributes: &FighterAttributes,
fast_fall: bool
) {
velocity.y -= attributes.gravity; velocity.y -= attributes.gravity;
velocity.y = velocity.y.max(-attributes.terminal_velocity);
let terminal_vel = if fast_fall {
-attributes.fast_fall_speed
} else {
-attributes.terminal_velocity
};
velocity.y = velocity.y.max(terminal_vel);
} }
pub fn apply_air_drift( pub fn apply_air_drift(
@@ -90,12 +56,8 @@ pub fn apply_air_motion(
translation.0 += velocity.0; translation.0 += velocity.0;
} }
pub fn update_standard_motion( pub fn update_standard_motion(ctx: &mut crate::fighter::state::UpdateContext) {
ctx: &mut crate::fighter::state::UpdateContext, integrate_gravity(ctx.velocity, ctx.fighter_attributes);
airborne_common: &mut AirborneStateCommon,
) {
process_fast_fall(airborne_common, ctx.input, &mut ctx.velocity, ctx.fighter_attributes);
integrate_gravity(ctx.velocity, ctx.fighter_attributes, airborne_common.is_fast_falling);
apply_air_drift(ctx.velocity, ctx.fighter_attributes, ctx.input); apply_air_drift(ctx.velocity, ctx.fighter_attributes, ctx.input);
apply_air_motion(ctx.velocity, ctx.translation, ctx.prev_translation); apply_air_motion(ctx.velocity, ctx.translation, ctx.prev_translation);
} }
@@ -148,7 +110,6 @@ mod tests {
air_drift_max: FGi32::lit("1.2"), air_drift_max: FGi32::lit("1.2"),
ground_friction: FGi32::ZERO, ground_friction: FGi32::ZERO,
terminal_velocity: FGi32::ZERO, terminal_velocity: FGi32::ZERO,
fast_fall_speed: FGi32::ZERO,
gravity: FGi32::ZERO, gravity: FGi32::ZERO,
weight: FGi32::lit("1"), weight: FGi32::lit("1"),
air_friction: FGi32::lit("0.001"), air_friction: FGi32::lit("0.001"),
+8 -4
View File
@@ -1,9 +1,13 @@
use crate::{ use crate::{
fighter::{ fighter::{
animation::AnimKind, state::{ animation::AnimKind,
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext, UpdateContext, air::{self, AirborneStateCommon}, fall::FallState, land::LandingType, state::{
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext,
UpdateContext, air, fall::FallState, land::LandingType,
}, },
}, input, math::vec::FGVec2, },
input,
math::vec::FGVec2,
}; };
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
@@ -16,7 +20,7 @@ impl FighterStateImpl for AirDodgeState {
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> { fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
if state_context.is_current_animation_finished() { if state_context.is_current_animation_finished() {
Some(FighterState::Fall(FallState::new(AirborneStateCommon::default()))) Some(FighterState::Fall(FallState))
} else { } else {
None None
} }
+11 -15
View File
@@ -1,24 +1,12 @@
use super::{CollisionContext, EnterContext, FighterStateImpl, InterruptContext, UpdateContext}; use super::{CollisionContext, EnterContext, FighterStateImpl, InterruptContext, UpdateContext};
use crate::fighter::animation::AnimKind; use crate::fighter::animation::AnimKind;
use crate::fighter::state::air::AirborneStateCommon;
use crate::fighter::state::{FighterState, air, jump}; use crate::fighter::state::{FighterState, air, jump};
#[derive(Debug, Clone, Copy, Hash, Default)] #[derive(Debug, Clone, Copy, Hash)]
pub struct FallState { pub struct FallState;
airborne_state_common: AirborneStateCommon
}
impl FallState {
pub fn new(airborne_state_common: AirborneStateCommon) -> Self {
Self {
airborne_state_common
}
}
}
impl FighterStateImpl for FallState { impl FighterStateImpl for FallState {
const NAME: &'static str = "Fall"; const NAME: &'static str = "Fall";
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> { fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
jump::check_input_double_jump(state_context) jump::check_input_double_jump(state_context)
} }
@@ -28,7 +16,7 @@ impl FighterStateImpl for FallState {
} }
fn update(&mut self, state_context: &mut UpdateContext) { fn update(&mut self, state_context: &mut UpdateContext) {
air::update_standard_motion(state_context, &mut self.airborne_state_common); air::update_standard_motion(state_context);
} }
fn check_collision_interrupt( fn check_collision_interrupt(
@@ -48,3 +36,11 @@ impl FighterStateImpl for FallState {
} }
} }
pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
let input_frame = state_context.input.get_last_frame();
if input_frame.movement.x.abs() < state_context.game_settings.input_common.stick_deadzone {
return Some(FighterState::Fall(FallState));
}
None
}
+2 -2
View File
@@ -3,7 +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, walk, CollisionContext, FighterState, InterruptContext, dash, fall, ground_attack, turn, wait, walk,
}; };
use crate::fighter::{ use crate::fighter::{
FighterAttributes, FighterPreviousTranslation, FighterTranslation, FighterVelocity, FighterAttributes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
@@ -233,7 +233,7 @@ pub fn collision_interrupt(
) -> Option<FighterState> { ) -> Option<FighterState> {
match collide_with_stage_grounded(ctx, common, can_walk_off) { match collide_with_stage_grounded(ctx, common, can_walk_off) {
GroundedMotionResult::InGround => None, GroundedMotionResult::InGround => None,
GroundedMotionResult::InAir => Some(FighterState::Fall(fall::FallState::new(air::AirborneStateCommon::default()))), GroundedMotionResult::InAir => Some(FighterState::Fall(fall::FallState)),
} }
} }
+8 -35
View File
@@ -1,11 +1,10 @@
use crate::{ use crate::{
fighter::{ fighter::{
animation::AnimKind, animation::AnimKind,
attack::{self, FighterHitReaction}, attack::FighterHitReaction,
state::{ state::{
CollisionContext, EnterContext, ExitContext, FighterState, FighterStateImpl, CollisionContext, EnterContext, ExitContext, FighterState, FighterStateImpl,
InterruptContext, UpdateContext, InterruptContext, UpdateContext, air,
air::{self, AirborneStateCommon},
fall::FallState, fall::FallState,
ground::{GroundedStateCommon, collision_interrupt}, ground::{GroundedStateCommon, collision_interrupt},
land::LandingType, land::LandingType,
@@ -25,17 +24,6 @@ pub enum HitstopStateKind {
pub struct HitstunState { pub struct HitstunState {
pub hit_reaction: FighterHitReaction, pub hit_reaction: FighterHitReaction,
pub hitstop_kind: HitstopStateKind, pub hitstop_kind: HitstopStateKind,
knockback_applied: bool,
}
impl HitstunState {
pub fn new(hit_reaction: FighterHitReaction, hitstop_kind: HitstopStateKind) -> Self {
Self {
hit_reaction,
hitstop_kind,
knockback_applied: false,
}
}
} }
impl FighterStateImpl for HitstunState { impl FighterStateImpl for HitstunState {
@@ -63,9 +51,7 @@ impl FighterStateImpl for HitstunState {
grounded_common: grounded_state_common.clone(), grounded_common: grounded_state_common.clone(),
})) }))
} }
HitstopStateKind::Airborne => { HitstopStateKind::Airborne => Some(FighterState::Fall(FallState)),
Some(FighterState::Fall(FallState::new(AirborneStateCommon::default())))
}
} }
} else { } else {
None None
@@ -73,23 +59,7 @@ impl FighterStateImpl for HitstunState {
} }
fn update(&mut self, ctx: &mut UpdateContext) { fn update(&mut self, ctx: &mut UpdateContext) {
if !self.knockback_applied { air::integrate_gravity(ctx.velocity, ctx.fighter_attributes);
let launch_direction = attack::directional_influence::apply_directional_influence(
ctx.input.get_last_frame().movement,
self.hit_reaction.launch_angle,
ctx.game_settings.fighter_common.di_max_angle,
);
ctx.knockback_velocity.0 = launch_direction * self.hit_reaction.launch_velocity;
self.knockback_applied = true;
}
match self.hitstop_kind {
HitstopStateKind::Grounded(_) => {}
HitstopStateKind::Airborne => {
air::integrate_gravity(ctx.velocity, ctx.fighter_attributes, false);
}
}
let speed = ctx.knockback_velocity.length(); let speed = ctx.knockback_velocity.length();
let next_speed = speed let next_speed = speed
@@ -107,6 +77,7 @@ impl FighterStateImpl for HitstunState {
HitstopStateKind::Airborne => AnimKind::FlinchAir, HitstopStateKind::Airborne => AnimKind::FlinchAir,
}; };
state_context.play_animation(animation, false); state_context.play_animation(animation, false);
state_context.knockback_velocity.0 = self.hit_reaction.launch_velocity;
} }
fn on_exit(&mut self, _state_context: &mut ExitContext) {} fn on_exit(&mut self, _state_context: &mut ExitContext) {}
@@ -125,7 +96,9 @@ impl FighterStateImpl for HitstunState {
} }
} }
fn is_visually_grounded<'a>(&'a self) -> bool { fn is_visually_grounded<'a>(
&'a self,
) -> bool {
match self.hitstop_kind { match self.hitstop_kind {
HitstopStateKind::Grounded(_) => true, HitstopStateKind::Grounded(_) => true,
HitstopStateKind::Airborne => false, HitstopStateKind::Airborne => false,
+11 -8
View File
@@ -2,10 +2,16 @@ use bevy::prelude::*;
use crate::{ use crate::{
fighter::{ fighter::{
animation::AnimKind, state::{ animation::AnimKind,
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext, UpdateContext, air::{self, AirborneStateCommon}, air_dodge::{self, AirDodgeState}, ground::{self, GroundedStateCommon}, state::{
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext,
UpdateContext, air,
air_dodge::{self, AirDodgeState},
ground::{self, GroundedStateCommon},
}, },
}, input::FighterCommands, math::{int::FGi32, vec::FGVec2}, },
input::FighterCommands,
math::{int::FGi32, vec::FGVec2},
}; };
#[derive(Debug, Hash, Clone, Copy)] #[derive(Debug, Hash, Clone, Copy)]
pub enum JumpType { pub enum JumpType {
@@ -17,7 +23,6 @@ pub enum JumpType {
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct JumpState { pub struct JumpState {
jump_type: JumpType, jump_type: JumpType,
airborne_state_common: AirborneStateCommon,
} }
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
@@ -69,7 +74,7 @@ impl FighterStateImpl for JumpSquatState {
} else { } else {
JumpType::ShortHop JumpType::ShortHop
}; };
Some(FighterState::Jump(JumpState { jump_type, airborne_state_common: AirborneStateCommon::default() })) Some(FighterState::Jump(JumpState { jump_type }))
} else { } else {
None None
} }
@@ -105,7 +110,6 @@ impl FighterStateImpl for JumpState {
if state_context.input.has_command(FighterCommands::Jump) { if state_context.input.has_command(FighterCommands::Jump) {
Some(FighterState::Jump(JumpState { Some(FighterState::Jump(JumpState {
jump_type: JumpType::DoubleJump, jump_type: JumpType::DoubleJump,
airborne_state_common: AirborneStateCommon::default()
})) }))
} else { } else {
air_dodge::check_input(state_context) air_dodge::check_input(state_context)
@@ -113,7 +117,7 @@ impl FighterStateImpl for JumpState {
} }
fn update(&mut self, state_context: &mut UpdateContext) { fn update(&mut self, state_context: &mut UpdateContext) {
air::update_standard_motion(state_context, &mut self.airborne_state_common); air::update_standard_motion(state_context);
} }
fn on_enter(&mut self, state_context: &mut EnterContext) { fn on_enter(&mut self, state_context: &mut EnterContext) {
@@ -201,7 +205,6 @@ pub fn check_input_double_jump(state_context: &InterruptContext) -> Option<Fight
.then(|| { .then(|| {
FighterState::Jump(JumpState { FighterState::Jump(JumpState {
jump_type: JumpType::DoubleJump, jump_type: JumpType::DoubleJump,
airborne_state_common: AirborneStateCommon::default()
}) })
}) })
} }
+5 -5
View File
@@ -148,7 +148,7 @@ pub struct ExitContext<'a> {
pub struct UpdateContext<'a> { pub struct UpdateContext<'a> {
pub translation: &'a mut FighterTranslation, pub translation: &'a mut FighterTranslation,
pub prev_translation: &'a mut FighterPreviousTranslation, pub prev_translation: &'a mut FighterPreviousTranslation,
pub input: &'a mut FighterInput, pub input: &'a FighterInput,
pub hitboxes: &'a mut FighterHitboxes, pub hitboxes: &'a mut FighterHitboxes,
pub velocity: &'a mut FighterVelocity, pub velocity: &'a mut FighterVelocity,
pub knockback_velocity: &'a mut FighterKnockbackVelocity, pub knockback_velocity: &'a mut FighterKnockbackVelocity,
@@ -289,7 +289,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
UpdateContext { UpdateContext {
translation: &mut self.translation, translation: &mut self.translation,
prev_translation: &mut self.prev_translation, prev_translation: &mut self.prev_translation,
input: &mut self.input, input: &self.input,
hitboxes: &mut self.hitboxes, hitboxes: &mut self.hitboxes,
velocity: &mut self.velocity, velocity: &mut self.velocity,
knockback_velocity: &mut self.knockback_velocity, knockback_velocity: &mut self.knockback_velocity,
@@ -451,10 +451,10 @@ pub fn state_interrupt_system(
} }
info!("Entering hitstop kind: {:?}", hitstop_kind); info!("Entering hitstop kind: {:?}", hitstop_kind);
Some(FighterState::Hitstun(HitstunState::new( Some(FighterState::Hitstun(HitstunState {
reaction, hit_reaction: reaction,
hitstop_kind, hitstop_kind,
))) }))
} else if hitstop.remaining_frames > 0 { } else if hitstop.remaining_frames > 0 {
None None
} else { } else {
-3
View File
@@ -36,9 +36,6 @@ pub struct FighterSettingsCommon {
pub visual_shake_table: Vec<f32>, pub visual_shake_table: Vec<f32>,
/// The attacker will always get less hitstop /// The attacker will always get less hitstop
pub attacker_visual_shake_multiplier: f32, pub attacker_visual_shake_multiplier: f32,
/// Maximum angle changed caused by directional influence
pub di_max_angle: FGi32,
} }
#[derive(Reflect, Serialize, Deserialize, Clone)] #[derive(Reflect, Serialize, Deserialize, Clone)]
#[reflect(opaque)] #[reflect(opaque)]
-21
View File
@@ -20,8 +20,6 @@ pub enum FighterCommands {
Jump, Jump,
Shield, Shield,
Attack, Attack,
SmashMoveUp,
SmashMoveDown,
SmashMoveLeft, SmashMoveLeft,
SmashMoveRight, SmashMoveRight,
} }
@@ -62,8 +60,6 @@ impl FighterAttackCommandType {
pub struct FighterCommandLifetimes { pub struct FighterCommandLifetimes {
smash_move_left: u8, smash_move_left: u8,
smash_move_right: u8, smash_move_right: u8,
smash_move_down: u8,
smash_move_up: u8,
jump: u8, jump: u8,
shield: u8, shield: u8,
attack: u8, attack: u8,
@@ -76,8 +72,6 @@ impl Index<FighterCommands> for FighterCommandLifetimes {
match command { match command {
FighterCommands::SmashMoveLeft => &self.smash_move_left, FighterCommands::SmashMoveLeft => &self.smash_move_left,
FighterCommands::SmashMoveRight => &self.smash_move_right, FighterCommands::SmashMoveRight => &self.smash_move_right,
FighterCommands::SmashMoveDown => &self.smash_move_down,
FighterCommands::SmashMoveUp => &self.smash_move_up,
FighterCommands::Jump => &self.jump, FighterCommands::Jump => &self.jump,
FighterCommands::Shield => &self.shield, FighterCommands::Shield => &self.shield,
FighterCommands::Attack => &self.attack, FighterCommands::Attack => &self.attack,
@@ -90,8 +84,6 @@ impl IndexMut<FighterCommands> for FighterCommandLifetimes {
match command { match command {
FighterCommands::SmashMoveLeft => &mut self.smash_move_left, FighterCommands::SmashMoveLeft => &mut self.smash_move_left,
FighterCommands::SmashMoveRight => &mut self.smash_move_right, FighterCommands::SmashMoveRight => &mut self.smash_move_right,
FighterCommands::SmashMoveDown => &mut self.smash_move_down,
FighterCommands::SmashMoveUp => &mut self.smash_move_up,
FighterCommands::Jump => &mut self.jump, FighterCommands::Jump => &mut self.jump,
FighterCommands::Shield => &mut self.shield, FighterCommands::Shield => &mut self.shield,
FighterCommands::Attack => &mut self.attack, FighterCommands::Attack => &mut self.attack,
@@ -229,19 +221,6 @@ pub fn postprocess_input(
let mut x_flick_detected = let mut x_flick_detected =
x_abs >= axis_threshold && input.frames_in_x_flick_deadzone <= frame_threshold; x_abs >= axis_threshold && input.frames_in_x_flick_deadzone <= frame_threshold;
if y_flick_detected {
input.clear_command(FighterCommands::SmashMoveUp);
input.clear_command(FighterCommands::SmashMoveDown);
let command_to_set = if input.current_frame.movement.y > 0.0 {
FighterCommands::SmashMoveUp
} else {
FighterCommands::SmashMoveDown
};
input.set_lifetime(command_to_set, game_settings.input_common.input_buffer_size);
}
// if both flicks are detected, only one wins // if both flicks are detected, only one wins
if x_flick_detected && y_flick_detected { if x_flick_detected && y_flick_detected {