Implement hitstun and knockback.
This commit is contained in:
+86
-37
@@ -6,14 +6,18 @@ use bevy::ecs::query::QueryData;
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::fighter::animation::{AnimKind, FighterAnimationFrame};
|
||||
use crate::fighter::attack::FighterAttackScriptAssets;
|
||||
use crate::fighter::attack::{
|
||||
FighterAttackScriptAssets, FighterKnockbackVelocity, FighterPendingHitReaction,
|
||||
FighterStaleMoveQueue,
|
||||
};
|
||||
use crate::fighter::baked_animation::BakedFighterAnimations;
|
||||
use crate::fighter::ecb::FighterPreviousECB;
|
||||
use crate::fighter::manifest::FighterManifest;
|
||||
use crate::fighter::state::hitstun::HitstunState;
|
||||
use crate::fighter::visual::FighterAnimations;
|
||||
use crate::fighter::{
|
||||
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation,
|
||||
FighterTranslation, FighterVelocity,
|
||||
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterHitstop,
|
||||
FighterPreviousTranslation, FighterTranslation, FighterVelocity,
|
||||
};
|
||||
use crate::game_settings::GameSettings;
|
||||
use crate::input::FighterInput;
|
||||
@@ -26,6 +30,7 @@ pub mod dash;
|
||||
pub mod fall;
|
||||
pub mod ground;
|
||||
pub mod ground_attack;
|
||||
pub mod hitstun;
|
||||
pub mod jump;
|
||||
pub mod land;
|
||||
pub mod run;
|
||||
@@ -74,6 +79,9 @@ pub trait FighterStateImpl: Sized {
|
||||
|
||||
fn on_enter(&mut self, _state_context: &mut FighterStateContext) {}
|
||||
fn on_exit(&mut self, _state_context: &mut FighterStateContext) {}
|
||||
fn name(&self) -> String {
|
||||
Self::NAME.to_owned()
|
||||
}
|
||||
fn update(&mut self, _state_context: &mut FighterStateContext);
|
||||
|
||||
fn check_collision_interrupt(
|
||||
@@ -90,6 +98,8 @@ pub struct FighterStateContext<'a> {
|
||||
pub input: &'a mut FighterInput,
|
||||
pub hitboxes: &'a mut FighterHitboxes,
|
||||
pub velocity: &'a mut FighterVelocity,
|
||||
pub knockback_velocity: &'a mut FighterKnockbackVelocity,
|
||||
pub stale_move_queue: &'a mut FighterStaleMoveQueue,
|
||||
pub ecb: &'a mut FighterECB,
|
||||
pub prev_ecb: &'a mut FighterPreviousECB,
|
||||
pub facing_direction: &'a mut FighterFacingDirection,
|
||||
@@ -136,6 +146,7 @@ fighter_states! {
|
||||
AirDodge => air_dodge::AirDodgeState,
|
||||
Turn => turn::TurnState,
|
||||
GroundAttack => ground_attack::GroundAttackState,
|
||||
Hitstun => hitstun::HitstunState,
|
||||
}
|
||||
|
||||
#[derive(QueryData)]
|
||||
@@ -146,6 +157,8 @@ pub struct FighterFrameQuery {
|
||||
pub input: &'static mut FighterInput,
|
||||
pub hitboxes: &'static mut FighterHitboxes,
|
||||
pub velocity: &'static mut FighterVelocity,
|
||||
pub knockback_velocity: &'static mut FighterKnockbackVelocity,
|
||||
pub stale_move_queue: &'static mut FighterStaleMoveQueue,
|
||||
pub ecb: &'static mut FighterECB,
|
||||
pub prev_ecb: &'static mut FighterPreviousECB,
|
||||
pub facing_direction: &'static mut FighterFacingDirection,
|
||||
@@ -170,6 +183,8 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
||||
input: &mut self.input,
|
||||
hitboxes: &mut self.hitboxes,
|
||||
velocity: &mut self.velocity,
|
||||
knockback_velocity: &mut self.knockback_velocity,
|
||||
stale_move_queue: &mut self.stale_move_queue,
|
||||
ecb: &mut self.ecb,
|
||||
prev_ecb: &mut self.prev_ecb,
|
||||
facing_direction: &mut self.facing_direction,
|
||||
@@ -188,21 +203,13 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
||||
#[derive(QueryData)]
|
||||
#[query_data(mutable)]
|
||||
pub struct StatefulFighterQuery {
|
||||
pub entity: Entity,
|
||||
pub state: &'static mut FighterState,
|
||||
pub frame: FighterFrameQuery,
|
||||
pub animations: &'static FighterAnimations,
|
||||
pub fighter: &'static Fighter,
|
||||
pub attack_script_assets: &'static FighterAttackScriptAssets,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct StateNameDebug(pub &'static str);
|
||||
|
||||
impl std::fmt::Display for StateNameDebug {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
pub pending_hit_reaction: &'static mut FighterPendingHitReaction,
|
||||
pub hitstop: &'static mut FighterHitstop,
|
||||
}
|
||||
|
||||
pub fn state_update_system(
|
||||
@@ -218,9 +225,14 @@ pub fn state_update_system(
|
||||
mut state,
|
||||
mut frame,
|
||||
animations,
|
||||
hitstop,
|
||||
..
|
||||
} = fighter;
|
||||
|
||||
if hitstop.remaining_frames > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut update_ctx = frame.context(
|
||||
&game_settings,
|
||||
&stage_collision,
|
||||
@@ -236,6 +248,20 @@ pub fn state_update_system(
|
||||
}
|
||||
}
|
||||
|
||||
fn transition_state(
|
||||
current: &mut FighterState,
|
||||
mut next: FighterState,
|
||||
ctx: &mut FighterStateContext,
|
||||
) {
|
||||
let old_name = current.name();
|
||||
let new_name = next.name();
|
||||
|
||||
current.on_exit(ctx);
|
||||
next.on_enter(ctx);
|
||||
*current = next;
|
||||
info!("{} -> {}", old_name, new_name);
|
||||
}
|
||||
|
||||
pub fn state_interrupt_system(
|
||||
mut query: Query<StatefulFighterQuery>,
|
||||
stage_collision: Res<StageCollision>,
|
||||
@@ -243,16 +269,16 @@ pub fn state_interrupt_system(
|
||||
baked_animations: Res<Assets<BakedFighterAnimations>>,
|
||||
fighter_manifests: Res<Assets<FighterManifest>>,
|
||||
attack_scripts: Res<Assets<FighterAttackScript>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for fighter in &mut query {
|
||||
let StatefulFighterQueryItem {
|
||||
entity,
|
||||
mut state,
|
||||
mut frame,
|
||||
animations,
|
||||
fighter,
|
||||
attack_script_assets,
|
||||
mut pending_hit_reaction,
|
||||
mut hitstop,
|
||||
} = fighter;
|
||||
let mut state_context = frame.context(
|
||||
&game_settings,
|
||||
@@ -265,17 +291,43 @@ pub fn state_interrupt_system(
|
||||
.get(&fighter.manifest)
|
||||
.expect("Fighter manifest should be valid"),
|
||||
);
|
||||
if let Some(new_state) = state.check_interrupt(&mut state_context) {
|
||||
let mut new_state = new_state;
|
||||
let old_state_name = state.name();
|
||||
let new_state_name = new_state.name();
|
||||
state.on_exit(&mut state_context);
|
||||
new_state.on_enter(&mut state_context);
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert((new_state, StateNameDebug(new_state_name)));
|
||||
info!("State {} -> {}", old_state_name, new_state_name);
|
||||
}
|
||||
|
||||
let new_state = if let Some(reaction) = pending_hit_reaction.take() {
|
||||
hitstop.remaining_frames = *reaction.hitstop_frames;
|
||||
let hitstop_kind = match &*state {
|
||||
FighterState::Wait(value) => {
|
||||
hitstun::HitstopStateKind::Grounded(value.grounded_common.clone())
|
||||
}
|
||||
FighterState::Walk(value) => {
|
||||
hitstun::HitstopStateKind::Grounded(value.grounded_common.clone())
|
||||
}
|
||||
FighterState::Dash(value) => {
|
||||
hitstun::HitstopStateKind::Grounded(value.ground_common.clone())
|
||||
}
|
||||
FighterState::Run(value) => {
|
||||
hitstun::HitstopStateKind::Grounded(value.grounded_common.clone())
|
||||
}
|
||||
FighterState::Turn(value) => {
|
||||
hitstun::HitstopStateKind::Grounded(value.grounded_common.clone())
|
||||
}
|
||||
FighterState::GroundAttack(value) => {
|
||||
hitstun::HitstopStateKind::Grounded(value.grounded_common.clone())
|
||||
}
|
||||
_ => hitstun::HitstopStateKind::Airborne,
|
||||
};
|
||||
Some(FighterState::Hitstun(HitstunState {
|
||||
hit_reaction: reaction,
|
||||
hitstop_kind,
|
||||
}))
|
||||
} else if hitstop.remaining_frames > 0 {
|
||||
None
|
||||
} else {
|
||||
state.check_interrupt(&mut state_context)
|
||||
};
|
||||
|
||||
if let Some(new_state) = new_state {
|
||||
transition_state(&mut state, new_state, &mut state_context);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,16 +338,16 @@ pub fn state_collision_interrupt_system(
|
||||
baked_animations: Res<Assets<BakedFighterAnimations>>,
|
||||
fighter_manifests: Res<Assets<FighterManifest>>,
|
||||
attack_scripts: Res<Assets<FighterAttackScript>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for fighter in &mut query {
|
||||
let StatefulFighterQueryItem {
|
||||
entity,
|
||||
mut state,
|
||||
mut frame,
|
||||
animations,
|
||||
fighter,
|
||||
attack_script_assets,
|
||||
hitstop,
|
||||
..
|
||||
} = fighter;
|
||||
let mut state_context = frame.context(
|
||||
&game_settings,
|
||||
@@ -308,16 +360,13 @@ pub fn state_collision_interrupt_system(
|
||||
.get(&fighter.manifest)
|
||||
.expect("Fighter manifest should be valid"),
|
||||
);
|
||||
|
||||
if hitstop.remaining_frames > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(new_state) = state.check_collision_interrupt(&mut state_context) {
|
||||
let mut new_state = new_state;
|
||||
let old_state_name = state.name();
|
||||
let new_state_name = new_state.name();
|
||||
state.on_exit(&mut state_context);
|
||||
new_state.on_enter(&mut state_context);
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert((new_state, StateNameDebug(new_state_name)));
|
||||
info!("State {} -> {}", old_state_name, new_state_name);
|
||||
transition_state(&mut state, new_state, &mut state_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user