Integrate basic attack script debug.

This commit is contained in:
2026-08-30 14:45:26 +02:00
parent af0e567332
commit 1d5a07324d
19 changed files with 353 additions and 43 deletions
+4
View File
@@ -149,4 +149,8 @@ pub enum AnimKind {
AirDodge,
Fall,
Turn,
// Attacks
AttackJab1,
}
+27 -4
View File
@@ -1,7 +1,9 @@
use bevy::reflect::Reflect;
use std::collections::HashMap;
use bevy::{asset::Handle, ecs::component::Component, reflect::Reflect};
use serde::{Deserialize, Serialize};
use crate::math::{int::FGi32, vec3::FGVec3};
use crate::{fighter::animation::AnimKind, math::{int::FGi32, vec3::FGVec3}, scripting::FighterAttackScript};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FighterDamage(FGi32);
@@ -59,9 +61,19 @@ impl AttackAngle {
}
}
#[derive(Serialize, Deserialize, Reflect, PartialEq, Eq, Hash, Clone)]
#[derive(Serialize, Deserialize, Reflect, PartialEq, Eq, Hash, Clone, Debug)]
pub enum AttackKind {
NAir
NAir,
Jab
}
impl AttackKind {
pub fn get_animation(&self) -> AnimKind {
match self {
AttackKind::NAir => AnimKind::AttackJab1,
AttackKind::Jab => AnimKind::AttackJab1,
}
}
}
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
@@ -79,3 +91,14 @@ pub struct AttackHitbox {
pub start_frame: u32,
pub end_frame: u32
}
pub fn update_active_hitbox_list(script: &FighterAttackScript, list: &mut Vec<usize>, frame: u32) {
*list = script.hitboxes.iter().enumerate().filter(|(i, hb)| {
hb.start_frame <= frame && hb.end_frame > frame
}).map(|(i, _)| i).collect()
}
#[derive(Component)]
pub struct FighterAttackScriptAssets {
pub scripts: HashMap<AttackKind, Handle<FighterAttackScript>>
}
+120 -2
View File
@@ -20,7 +20,10 @@ use serde::{Deserialize, Serialize};
use crate::{
fighter::{animation::AnimKind, manifest::FighterManifest},
math::int::{FGWide, FGi32},
math::{
int::{FGWide, FGi32},
vec3::FGVec3,
},
};
pub const BAKED_ANIMATION_FPS: u32 = 60;
@@ -33,6 +36,14 @@ pub struct FixedMat4 {
}
impl FixedMat4 {
pub const IDENTITY: Self = Self {
cols: [
[FGi32::ONE, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
]
};
fn from_mat4(matrix: Mat4) -> io::Result<Self> {
let values = matrix.to_cols_array();
let mut cols = [[FGi32::ZERO; 4]; 4];
@@ -47,7 +58,7 @@ impl FixedMat4 {
Ok(Self { cols })
}
fn mul(self, rhs: Self) -> Self {
pub fn mul(self, rhs: Self) -> Self {
let mut cols = [[FGi32::ZERO; 4]; 4];
for column in 0..4 {
for row in 0..4 {
@@ -61,6 +72,67 @@ impl FixedMat4 {
}
Self { cols }
}
pub fn transform_point(self, point: FGVec3) -> FGVec3 {
let input = [point.x, point.y, point.z, FGi32::ONE];
let mut output = [FGi32::ZERO; 3];
for row in 0..3 {
let mut value = FGWide::ZERO;
for (column, input) in input.into_iter().enumerate() {
value += FGWide::from_num(self.cols[column][row]) * FGWide::from_num(input);
}
output[row] = FGi32::from_num(value);
}
FGVec3 {
x: output[0],
y: output[1],
z: output[2],
}
}
pub fn translate(&mut self, translation: FGVec3) {
self.cols[3][0] = translation.x;
self.cols[3][1] = translation.y;
self.cols[3][2] = translation.z;
}
pub fn rotate_y(self, quarter_turns: u8) -> Self {
let rotation = match quarter_turns % 4 {
0 => Self {
cols: [
[FGi32::ONE, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
],
},
1 => Self {
cols: [
[FGi32::ZERO, FGi32::ZERO, FGi32::NEG_ONE, FGi32::ZERO],
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::ONE, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
],
},
2 => Self {
cols: [
[FGi32::NEG_ONE, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::NEG_ONE, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
],
},
_ => Self {
cols: [
[FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO],
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::NEG_ONE, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE],
],
},
};
self.mul(rotation)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -525,4 +597,50 @@ mod tests {
baked.bone_names.len()
);
}
#[test]
fn fixed_matrix_transforms_points() {
let transform = FixedMat4 {
cols: [
[FGi32::lit("2"), FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::lit("3"), FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::lit("4"), FGi32::ZERO],
[
FGi32::lit("10"),
FGi32::lit("-5"),
FGi32::lit("1.5"),
FGi32::ONE,
],
],
};
assert_eq!(
transform.transform_point(FGVec3::lit("1.25", "-2", "0.5")),
FGVec3::lit("12.5", "-11", "3.5")
);
}
#[test]
fn fixed_matrix_rotates_points_around_y() {
let transform = FixedMat4 {
cols: [
[FGi32::ONE, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ONE, FGi32::ZERO, FGi32::ZERO],
[FGi32::ZERO, FGi32::ZERO, FGi32::ONE, FGi32::ZERO],
[
FGi32::lit("10"),
FGi32::lit("2"),
FGi32::lit("-3"),
FGi32::ONE,
],
],
};
assert_eq!(
transform
.rotate_y(5)
.transform_point(FGVec3::lit("1", "0", "0")),
FGVec3::lit("10", "2", "-4")
);
}
}
+41 -3
View File
@@ -10,10 +10,11 @@ use crate::fighter::ecb::FighterPreviousECB;
use crate::fighter::state::StateNameDebug;
use crate::fighter::visual::FighterAnimations;
use crate::fighter::{
FighterECB, FighterPreviousTranslation, FighterTranslation,
FighterVelocity,
FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
};
use crate::input::FighterInput;
use crate::math::vec3::FGVec3;
use crate::scripting::FighterAttackScript;
pub fn debug_draw_ecb(
ecbs: Query<(
@@ -60,14 +61,51 @@ pub fn animation_debug(
.get(&anims.baked)
.expect("Baked anims should be in");
for bone in &baked.bone_names {
let out = anims.sample_bone(&baked_anims, *frame, bone);
if bone != "Bone_30" {
continue;
}
let out = anims.sample_bone(&baked_anims, frame, bone);
if let Some(out) = out {
let pos = global_transform.transform_point(Vec3::new(
out.cols[3][0].to_num(),
out.cols[3][1].to_num(),
out.cols[3][2].to_num(),
));
//gizmos.text(pos, bone, 1.0, Vec2::new(0.0, 0.0), bevy::color::palettes::css::BLACK);
gizmos.cross(pos, 0.5, bevy::color::palettes::css::RED);
gizmos.axes(*global_transform, 1.0);
}
}
}
}
pub fn attack_debug(
query: Query<(&FighterHitboxes, &FighterAnimationFrame, &FighterAnimations, &FighterTranslation, &FighterFacingDirection)>,
baked_anims: Res<Assets<BakedFighterAnimations>>,
attack_scripts: Res<Assets<FighterAttackScript>>,
mut gizmos: Gizmos,
) {
for (hitboxes, frame, animations, translation, facing_direction) in query {
if let Some(script) = &hitboxes.attack_script {
let script = attack_scripts.get(script).expect("Script ref should be valid");
let player_trf = translation.get_3d_transform(facing_direction);
for i in hitboxes.active_hitboxes.iter() {
let offset = script.hitboxes[*i].offset;
let radius = script.hitboxes[*i].radius;
let bone = &script.hitboxes[*i].bone;
let trf = animations.sample_bone(&baked_anims, frame, bone);
if let Some(trf) = trf {
let trf = player_trf.mul(trf);
let start = player_trf.transform_point(FGVec3::lit("0.0", "1.0", "0.0"));
let end = player_trf.transform_point(FGVec3::lit("0.0", "1.0", "1.0"));
let pos = trf.transform_point(offset);
let pos_draw = Vec3::new(pos.x.to_num(), pos.y.to_num(), pos.z.to_num());
gizmos.sphere(pos_draw, radius.to_num(), bevy::color::palettes::css::RED);
gizmos.arrow(start.to_vec3(), end.to_vec3(),bevy::color::palettes::css::RED);
}
}
}
}
+17 -11
View File
@@ -29,16 +29,8 @@ pub use motion::{FighterPreviousTranslation, FighterTranslation, FighterVelocity
use crate::{
fighter::{
animation::animation_init,
manifest::FighterManifest,
state::{FighterState, fall::FallState},
visual::FighterAnimations,
},
input::FighterInput,
math::{int::FGi32, vec::FGVec2},
player::Player,
schedule::GameplaySet,
stage::line::StageCollision,
animation::animation_init, attack::FighterAttackScriptAssets, manifest::FighterManifest, state::{FighterState, fall::FallState}, visual::FighterAnimations,
}, input::FighterInput, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, stage::line::StageCollision,
};
use state::state_interrupt_system;
@@ -158,20 +150,29 @@ impl Plugin for FighterPlugin {
.add_systems(FixedPostUpdate, debug::debug_draw_ecb)
.add_systems(EguiPrimaryContextPass, debug::fighter_debug)
.add_systems(FixedPostUpdate, debug::animation_debug)
.add_systems(FixedPostUpdate, debug::attack_debug)
.add_systems(EguiPrimaryContextPass, debug::update_config)
.rollback_component_with_clone::<FighterState>()
.rollback_component_with_clone::<FighterHitboxes>()
.checksum_component_with_hash::<FighterState>()
.checksum_component_with_hash::<FighterFacingDirection>()
.register_type::<FighterFacingDirection>();
}
}
#[derive(Component, Clone)]
pub struct FighterHitboxes {
pub attack_script: Option<Handle<FighterAttackScript>>,
pub active_hitboxes: Vec<usize>
}
pub fn spawn_fighter(
commands: &mut Commands,
player_handle: usize,
spawn_position: FGVec2,
manifest: Handle<FighterManifest>,
animations: FighterAnimations,
attack_scripts: FighterAttackScriptAssets,
visual_root: WorldAssetRoot,
) {
commands.spawn((
@@ -183,14 +184,19 @@ pub fn spawn_fighter(
manifest: manifest,
},
FighterFacingDirection::Right,
FighterECB {
(FighterECB {
vertical_half: FGi32::lit("5.0"),
horizontal_half: FGi32::lit("2.5"),
},
FighterHitboxes {
attack_script: None,
active_hitboxes: vec![]
}),
FighterVelocity::default(),
FighterTranslation(spawn_position),
visual_root,
animations,
attack_scripts,
FighterState::Fall(FallState),
animation::FighterAnimationFrame::new(animation::AnimKind::Wait, true),
FighterVisual,
+13 -1
View File
@@ -2,8 +2,9 @@
//! this module is what turns that into a position.
use bevy::prelude::*;
use fixed::types::I16F16;
use crate::math::vec::FGVec2;
use crate::{fighter::{FighterFacingDirection, baked_animation::FixedMat4}, math::{int::FGi32, vec::FGVec2, vec3::FGVec3}};
#[derive(Component, Clone, Copy)]
pub struct Grounded;
@@ -18,6 +19,17 @@ pub struct FighterPreviousTranslation(pub FGVec2);
#[require(FighterPreviousTranslation)]
pub struct FighterTranslation(pub FGVec2);
impl FighterTranslation {
pub fn get_3d_transform(&self, facing_dir: &FighterFacingDirection) -> FixedMat4 {
let mut o = match facing_dir {
FighterFacingDirection::Left => FixedMat4::IDENTITY.rotate_y(3),
FighterFacingDirection::Right => FixedMat4::IDENTITY.rotate_y(1),
};
o.translate(FGVec3::new(self.0.x, self.0.y, FGi32::ZERO));
o
}
}
pub fn apply_air_motion(
fighters: Query<(
&FighterVelocity,
+4 -2
View File
@@ -2,7 +2,7 @@
//! ECS types so it stays cheap to reason about and to unit-test — it has to be
//! bit-for-bit deterministic for rollback.
use crate::fighter::state::{FighterState, FighterStateContext, dash, turn, wait, walk};
use crate::fighter::state::{FighterState, FighterStateContext, dash, ground_attack, turn, wait, walk};
use crate::fighter::{
FighterAttributes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
};
@@ -151,7 +151,9 @@ pub fn grounded_movement_standstill_common_interrupts(
state_context: &FighterStateContext,
ground_common: &GroundedStateCommon,
) -> Option<FighterState> {
if let Some(state) = dash::check_input(state_context, &ground_common) {
if let Some(state) = ground_attack::check_interrupt(state_context, &ground_common) {
Some(state)
} else if let Some(state) = dash::check_input(state_context, &ground_common) {
Some(state)
} else if let Some(state) = turn::check_input(state_context, &ground_common) {
Some(state)
+48
View File
@@ -0,0 +1,48 @@
use crate::fighter::{attack::{AttackKind, update_active_hitbox_list}, state::{FighterState, FighterStateImpl, ground::GroundedStateCommon, wait::WaitState}};
use bevy::prelude::*;
#[derive(Clone, Debug, Hash)]
pub struct GroundAttackState {
pub attack_kind: AttackKind,
pub grounded_common: GroundedStateCommon,
}
impl FighterStateImpl for GroundAttackState {
const NAME: &'static str = "GroundAttack";
fn check_interrupt(&self, state_context: &mut super::FighterStateContext) -> Option<FighterState> {
if state_context.is_current_animation_finished() {
Some(FighterState::Wait(WaitState {
grounded_common: self.grounded_common.clone()
}))
} else {
None
}
}
fn update(&mut self, state_context: &mut super::FighterStateContext) {
let script = state_context.attack_scripts.get(&state_context.attack_script_assets.scripts[&self.attack_kind]);
if let Some(script) = script {
let animation_frame = state_context.get_current_animation_frame();
update_active_hitbox_list(script, &mut state_context.hitboxes.active_hitboxes, animation_frame);
}
}
fn on_enter(&mut self, state_context: &mut super::FighterStateContext) {
state_context.play_animation(self.attack_kind.get_animation(), false);
state_context.hitboxes.attack_script = Some(state_context.attack_script_assets.scripts[&self.attack_kind].clone());
state_context.hitboxes.active_hitboxes.clear();
}
}
pub fn check_interrupt(state_context: &super::FighterStateContext, grounded_common: &GroundedStateCommon) -> Option<FighterState> {
if state_context.input.has_command(crate::input::FighterCommands::Attack) {
Some(FighterState::GroundAttack(GroundAttackState {
attack_kind: AttackKind::Jab,
grounded_common: grounded_common.clone()
}))
} else {
None
}
}
+29 -4
View File
@@ -6,16 +6,18 @@ use bevy::ecs::query::QueryData;
use bevy::prelude::*;
use crate::fighter::animation::{AnimKind, FighterAnimationFrame, FighterAnimationPlayerLink};
use crate::fighter::attack::FighterAttackScriptAssets;
use crate::fighter::baked_animation::{BakedFighterAnimations, FixedMat4};
use crate::fighter::ecb::FighterPreviousECB;
use crate::fighter::manifest::FighterManifest;
use crate::fighter::state::ground_attack::GroundAttackState;
use crate::fighter::visual::FighterAnimations;
use crate::fighter::{
Fighter, FighterECB, FighterFacingDirection, FighterPreviousTranslation,
FighterTranslation, FighterVelocity,
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
};
use crate::game_settings::GameSettings;
use crate::input::FighterInput;
use crate::scripting::FighterAttackScript;
use crate::stage::line::StageCollision;
pub mod air;
@@ -29,6 +31,7 @@ pub mod run;
pub mod turn;
pub mod wait;
pub mod walk;
pub mod ground_attack;
macro_rules! fighter_states {
($($variant:ident => $ty:ty),* $(,)?) => {
@@ -80,6 +83,7 @@ pub struct FighterStateContext<'a> {
pub translation: &'a mut FighterTranslation,
pub prev_translation: &'a mut FighterPreviousTranslation,
pub input: &'a mut FighterInput,
pub hitboxes: &'a mut FighterHitboxes,
pub velocity: &'a mut FighterVelocity,
pub ecb: &'a mut FighterECB,
pub prev_ecb: &'a mut FighterPreviousECB,
@@ -91,7 +95,9 @@ pub struct FighterStateContext<'a> {
pub animation_transitions: &'a mut AnimationTransitions,
pub animation_frame: &'a mut FighterAnimationFrame,
pub baked_animations: &'a Assets<BakedFighterAnimations>,
pub attack_scripts: &'a Assets<FighterAttackScript>,
pub fighter_manifest: &'a FighterManifest,
pub attack_script_assets: &'a FighterAttackScriptAssets,
}
impl FighterStateContext<'_> {
@@ -124,7 +130,7 @@ impl FighterStateContext<'_> {
pub fn sample_bone(&self, bone: &str) -> Option<FixedMat4> {
self.animations
.sample_bone(self.baked_animations, *self.animation_frame, bone)
.sample_bone(self.baked_animations, self.animation_frame, bone)
}
}
@@ -138,7 +144,8 @@ fighter_states! {
Jump => jump::JumpState,
Land => land::LandingState,
AirDodge => air_dodge::AirDodgeState,
Turn => turn::TurnState
Turn => turn::TurnState,
GroundAttack => ground_attack::GroundAttackState,
}
#[derive(QueryData)]
@@ -147,6 +154,7 @@ pub struct FighterFrameQuery {
pub translation: &'static mut FighterTranslation,
pub prev_translation: &'static mut FighterPreviousTranslation,
pub input: &'static mut FighterInput,
pub hitboxes: &'static mut FighterHitboxes,
pub velocity: &'static mut FighterVelocity,
pub ecb: &'static mut FighterECB,
pub prev_ecb: &'static mut FighterPreviousECB,
@@ -164,12 +172,15 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
animation_player: &'a mut AnimationPlayer,
animation_transitions: &'a mut AnimationTransitions,
baked_animations: &'a Assets<BakedFighterAnimations>,
attack_scripts: &'a Assets<FighterAttackScript>,
attack_script_assets: &'a FighterAttackScriptAssets,
manifest: &'a FighterManifest,
) -> FighterStateContext<'a> {
FighterStateContext {
translation: &mut self.translation,
prev_translation: &mut self.prev_translation,
input: &mut self.input,
hitboxes: &mut self.hitboxes,
velocity: &mut self.velocity,
ecb: &mut self.ecb,
prev_ecb: &mut self.prev_ecb,
@@ -181,6 +192,8 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
animation_transitions,
animation_frame: &mut self.animation_frame,
baked_animations,
attack_scripts,
attack_script_assets,
fighter_manifest: manifest,
}
}
@@ -195,6 +208,7 @@ pub struct StatefulFighterQuery {
pub animations: &'static FighterAnimations,
pub animation_player_link: &'static FighterAnimationPlayerLink,
pub fighter: &'static Fighter,
pub attack_script_assets: &'static FighterAttackScriptAssets,
}
#[derive(Component)]
@@ -212,6 +226,7 @@ pub fn state_update_system(
stage_collision: Res<StageCollision>,
game_settings: Res<GameSettings>,
baked_animations: Res<Assets<BakedFighterAnimations>>,
attack_scripts: Res<Assets<FighterAttackScript>>,
manifests: Res<Assets<FighterManifest>>,
) {
for fighter in &mut query {
@@ -235,6 +250,8 @@ pub fn state_update_system(
&mut animation_player,
&mut animation_transitions,
&baked_animations,
&attack_scripts,
&fighter.attack_script_assets,
&manifests
.get(&(fighter.fighter.manifest))
.expect("Fighter manifest should be valid"),
@@ -250,6 +267,7 @@ pub fn state_interrupt_system(
game_settings: Res<GameSettings>,
baked_animations: Res<Assets<BakedFighterAnimations>>,
fighter_manifests: Res<Assets<FighterManifest>>,
attack_scripts: Res<Assets<FighterAttackScript>>,
mut commands: Commands,
) {
for fighter in &mut query {
@@ -260,6 +278,7 @@ pub fn state_interrupt_system(
animations,
animation_player_link,
fighter,
attack_script_assets
} = fighter;
let Ok((mut animation_player, mut animation_transitions)) =
animation_players.get_mut(animation_player_link.player())
@@ -273,6 +292,8 @@ pub fn state_interrupt_system(
&mut animation_player,
&mut animation_transitions,
&baked_animations,
&attack_scripts,
attack_script_assets,
fighter_manifests
.get(&fighter.manifest)
.expect("Fighter manifest should be valid"),
@@ -297,6 +318,7 @@ pub fn state_collision_interrupt_system(
game_settings: Res<GameSettings>,
baked_animations: Res<Assets<BakedFighterAnimations>>,
fighter_manifests: Res<Assets<FighterManifest>>,
attack_scripts: Res<Assets<FighterAttackScript>>,
mut commands: Commands,
) {
for fighter in &mut query {
@@ -307,6 +329,7 @@ pub fn state_collision_interrupt_system(
animations,
animation_player_link,
fighter,
attack_script_assets,
} = fighter;
let Ok((mut animation_player, mut animation_transitions)) =
animation_players.get_mut(animation_player_link.player())
@@ -320,6 +343,8 @@ pub fn state_collision_interrupt_system(
&mut animation_player,
&mut animation_transitions,
&baked_animations,
&attack_scripts,
&attack_script_assets,
fighter_manifests
.get(&fighter.manifest)
.expect("Fighter manifest should be valid"),
+1 -1
View File
@@ -16,7 +16,7 @@ impl FighterAnimations {
pub fn sample_bone(
&self,
baked_assets: &Assets<BakedFighterAnimations>,
frame: FighterAnimationFrame,
frame: &FighterAnimationFrame,
bone: &str,
) -> Option<crate::fighter::baked_animation::FixedMat4> {
let baked = baked_assets.get(&self.baked)?;
+11
View File
@@ -20,6 +20,7 @@ pub enum FighterCommands {
SmashMoveRight,
Jump,
Shield,
Attack,
}
/// Frames of life remaining for each buffered command. Indexed by
@@ -30,6 +31,7 @@ pub struct FighterCommandLifetimes {
smash_move_right: u8,
jump: u8,
shield: u8,
attack: u8,
}
impl Index<FighterCommands> for FighterCommandLifetimes {
@@ -41,6 +43,7 @@ impl Index<FighterCommands> for FighterCommandLifetimes {
FighterCommands::SmashMoveRight => &self.smash_move_right,
FighterCommands::Jump => &self.jump,
FighterCommands::Shield => &self.shield,
FighterCommands::Attack => &self.attack,
}
}
}
@@ -52,6 +55,7 @@ impl IndexMut<FighterCommands> for FighterCommandLifetimes {
FighterCommands::SmashMoveRight => &mut self.smash_move_right,
FighterCommands::Jump => &mut self.jump,
FighterCommands::Shield => &mut self.shield,
FighterCommands::Attack => &mut self.attack,
}
}
}
@@ -148,6 +152,13 @@ pub fn postprocess_input(
);
}
if !input.prev_frame.attack && input.current_frame.attack {
input.set_lifetime(
FighterCommands::Attack,
game_settings.input_common.input_buffer_size,
);
}
if x_abs >= axis_threshold && input.frames_in_smash_move_deadzone <= frame_threshold {
input.clear_command(FighterCommands::SmashMoveLeft);
input.clear_command(FighterCommands::SmashMoveRight);
+1 -1
View File
@@ -1,6 +1,6 @@
use bevy_egui::prelude::*;
use crate::input::FighterInput;
use crate::{fighter::{FighterHitboxes, animation::FighterAnimationFrame, visual::FighterAnimations}, input::FighterInput};
use bevy::prelude::*;
pub fn input_debug(mut contexts: EguiContexts, query: Query<&FighterInput>) -> Result {
+1
View File
@@ -10,4 +10,5 @@ pub struct FighterInputFrame {
pub directional_attack: FGVec2,
pub jump: bool,
pub shield: bool,
pub attack: bool,
}
+1
View File
@@ -17,6 +17,7 @@ pub fn sample_gamepad(pad: &Gamepad, input_map: &BaseInputMap) -> FighterInputFr
match gamepad_element.action {
InputMapAction::Jump => input_frame.jump |= pressed,
InputMapAction::Shield => input_frame.shield |= pressed,
InputMapAction::Attack => input_frame.attack |= pressed,
// Digital buttons driving movement isn't used by the
// default bindings (the stick covers that), but stay
// consistent with the keyboard backend if one is added.
+1
View File
@@ -32,6 +32,7 @@ pub fn sample_keyboard(key: &ButtonInput<KeyCode>, input_map: &BaseInputMap) ->
}
InputMapAction::Jump => input_frame.jump = action_strength != FGi32::ZERO,
InputMapAction::Shield => input_frame.shield = action_strength != FGi32::ZERO,
InputMapAction::Attack => input_frame.attack = action_strength != FGi32::ZERO,
}
}
+1
View File
@@ -16,6 +16,7 @@ pub enum InputMapAction {
MovementYDir(i32),
Jump,
Shield,
Attack,
}
#[derive(Debug)]
+10
View File
@@ -67,6 +67,11 @@ impl Plugin for FighterInputPlugin {
key: KeyCode::ShiftLeft,
action: InputMapAction::Shield,
},
// attack
KeyboardInputMapElement {
key: KeyCode::KeyJ,
action: InputMapAction::Attack,
},
],
gamepad: vec![
// left stick
@@ -92,6 +97,11 @@ impl Plugin for FighterInputPlugin {
binding: GamepadBinding::Button(GamepadButton::RightTrigger),
action: InputMapAction::Shield,
},
// attack
GamepadInputMapElement {
binding: GamepadBinding::Button(GamepadButton::East),
action: InputMapAction::Attack,
},
],
})
.add_plugins(gamecube::GamecubeAdapterPlugin)
+9 -13
View File
@@ -8,7 +8,7 @@ use bevy_ggrs::prelude::Session;
use crate::{
AppState, args::Args, fighter::{
FighterId, animation::AnimKind, attack::AttackKind, baked_animation::BakedFighterAnimations, manifest::{FighterManifest, FighterManifestRegistry}, spawn_fighter, visual::FighterAnimations,
FighterId, animation::AnimKind, attack::{AttackKind, FighterAttackScriptAssets}, baked_animation::BakedFighterAnimations, manifest::{FighterManifest, FighterManifestRegistry}, spawn_fighter, visual::FighterAnimations,
}, math::{int::FGi32, vec::FGVec2}, netcode::{GGRSCfg, session::create_session}, scripting::FighterAttackScript, stage::{
self,
manifest::{StageId, StageManifest, StageManifestRegistry},
@@ -163,7 +163,6 @@ pub fn prepare_match(
}
let mut prepared = Vec::with_capacity(assets.fighters.len());
let mut shared_animations = HashMap::<FighterId, FighterAnimations>::new();
for character in &assets.fighters {
let manifest_handle = registry
@@ -189,15 +188,6 @@ pub fn prepare_match(
return;
};
if let Some(animations) = shared_animations.get(&character.fighter) {
prepared.push((
scene,
animations.clone(),
manifest_handle,
));
continue;
}
let mut graph = AnimationGraph::new();
let mut clips = HashMap::<AnimKind, AnimationNodeIndex>::new();
@@ -235,10 +225,15 @@ pub fn prepare_match(
clips,
baked: character.baked_animations.clone(),
};
shared_animations.insert(character.fighter, animations.clone());
let attack_scripts = FighterAttackScriptAssets {
scripts: character.attack_scripts.clone()
};
prepared.push((
scene,
animations,
attack_scripts,
manifest_handle,
));
}
@@ -272,7 +267,7 @@ pub fn prepare_match(
for (
index,
(player, (scene, animations, manifest_handle)),
(player, (scene, animations, attack_scripts, manifest_handle)),
) in request.players.iter().zip(prepared).enumerate()
{
let spawn_x = (index as i32 * 2 + 1 - request.players.len() as i32) * 5;
@@ -282,6 +277,7 @@ pub fn prepare_match(
FGVec2::new(FGi32::from_num(spawn_x), FGi32::lit("12.5")),
manifest_handle.clone(),
animations,
attack_scripts,
WorldAssetRoot(scene),
);
}
+13
View File
@@ -12,6 +12,15 @@ pub struct FGVec3 {
}
impl FGVec3 {
pub fn new(x: FGi32, y: FGi32, z: FGi32) -> Self {
Self {
x,
y,
z
}
}
pub fn lit(x: &str, y: &str, z: &str) -> Self {
Self {
x: FGi32::lit(x),
@@ -19,4 +28,8 @@ impl FGVec3 {
z: FGi32::lit(z),
}
}
pub fn to_vec3(&self) -> Vec3 {
Vec3::new(self.x.to_num(), self.y.to_num(), self.z.to_num())
}
}