Integrate basic attack script debug.
This commit is contained in:
@@ -149,4 +149,8 @@ pub enum AnimKind {
|
||||
AirDodge,
|
||||
Fall,
|
||||
Turn,
|
||||
|
||||
// Attacks
|
||||
|
||||
AttackJab1,
|
||||
}
|
||||
|
||||
+27
-4
@@ -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)]
|
||||
@@ -78,4 +90,15 @@ pub struct AttackHitbox {
|
||||
pub knockback_growth: FGi32,
|
||||
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>>
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -10,4 +10,5 @@ pub struct FighterInputFrame {
|
||||
pub directional_attack: FGVec2,
|
||||
pub jump: bool,
|
||||
pub shield: bool,
|
||||
pub attack: bool,
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ pub enum InputMapAction {
|
||||
MovementYDir(i32),
|
||||
Jump,
|
||||
Shield,
|
||||
Attack,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -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
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
+14
-1
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user