Add affine transformations for capsules.

This commit is contained in:
2026-09-03 16:40:41 +02:00
parent 06d221936b
commit 31cc9cfa15
20 changed files with 792 additions and 121 deletions
+3 -9
View File
@@ -1,14 +1,10 @@
use crate::{
fighter::{
animation::AnimKind,
collision,
state::{
FighterState, FighterStateContext, FighterStateImpl, air,
fall::FallState,
ground::GroundedStateCommon,
land::LandingState,
FighterState, FighterStateContext, FighterStateImpl, air, fall::FallState,
ground::GroundedStateCommon, land::LandingState,
},
},
input,
@@ -51,9 +47,7 @@ impl FighterStateImpl for AirDodgeState {
let grounded_common = GroundedStateCommon {
current_line_id: res.line_id,
};
Some(FighterState::Land(LandingState {
grounded_common
}))
Some(FighterState::Land(LandingState { grounded_common }))
} else {
None
}
+1 -1
View File
@@ -1,4 +1,4 @@
use std::hash::{Hasher};
use std::hash::Hasher;
use super::{FighterStateContext, FighterStateImpl, ground, run, wait, walk};
use crate::fighter::FighterFacingDirection;
+1 -3
View File
@@ -45,9 +45,7 @@ impl FighterStateImpl for FallState {
let grounded_common = GroundedStateCommon {
current_line_id: res.line_id,
};
Some(FighterState::Land(LandingState {
grounded_common
}))
Some(FighterState::Land(LandingState { grounded_common }))
} else {
None
}
+3 -1
View File
@@ -2,7 +2,9 @@
//! 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, ground_attack, turn, wait, walk};
use crate::fighter::state::{
FighterState, FighterStateContext, dash, ground_attack, turn, wait, walk,
};
use crate::fighter::{
FighterAttributes, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
};
+36 -15
View File
@@ -1,6 +1,12 @@
use fixed::types::I16F16;
use crate::{fighter::{attack::{AttackKind, update_active_hitbox_list}, state::{FighterState, FighterStateImpl, ground::GroundedStateCommon, wait::WaitState}}, math::int::FGi32};
use crate::{
fighter::{
attack::{AttackKind, update_active_hitbox_list},
state::{FighterState, FighterStateImpl, ground::GroundedStateCommon, wait::WaitState},
},
math::int::FGi32,
};
#[derive(Clone, Debug, Hash)]
pub struct GroundAttackState {
@@ -11,10 +17,13 @@ pub struct GroundAttackState {
impl FighterStateImpl for GroundAttackState {
const NAME: &'static str = "GroundAttack";
fn check_interrupt(&self, state_context: &mut super::FighterStateContext) -> Option<FighterState> {
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()
grounded_common: self.grounded_common.clone(),
}))
} else {
None
@@ -22,23 +31,30 @@ impl FighterStateImpl for GroundAttackState {
}
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 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);
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.attack_script =
Some(state_context.attack_script_assets.scripts[&self.attack_kind].clone());
state_context.hitboxes.active_hitboxes.clear();
}
fn on_exit(&mut self, state_context: &mut super::FighterStateContext) {
state_context.hitboxes.clear();
}
fn check_collision_interrupt(
&mut self,
_state_context: &mut super::FighterStateContext,
@@ -47,9 +63,14 @@ impl FighterStateImpl for GroundAttackState {
}
}
pub fn check_interrupt(state_context: &super::FighterStateContext, grounded_common: &GroundedStateCommon) -> Option<FighterState> {
if state_context.input.has_command(crate::input::FighterCommands::Attack) {
pub fn check_interrupt(
state_context: &super::FighterStateContext,
grounded_common: &GroundedStateCommon,
) -> Option<FighterState> {
if state_context
.input
.has_command(crate::input::FighterCommands::Attack)
{
let attack_kind = if state_context.input.get_last_frame().movement.y > FGi32::ZERO {
AttackKind::UpTilt
} else {
@@ -58,9 +79,9 @@ pub fn check_interrupt(state_context: &super::FighterStateContext, grounded_comm
Some(FighterState::GroundAttack(GroundAttackState {
attack_kind,
grounded_common: grounded_common.clone()
grounded_common: grounded_common.clone(),
}))
} else {
None
}
}
}
+1 -4
View File
@@ -1,4 +1,3 @@
use bevy::prelude::*;
use crate::{
@@ -239,9 +238,7 @@ impl FighterStateImpl for JumpState {
let grounded_common = GroundedStateCommon {
current_line_id: res.line_id,
};
Some(FighterState::Land(LandingState {
grounded_common
}))
Some(FighterState::Land(LandingState { grounded_common }))
} else {
None
}
+5 -5
View File
@@ -13,14 +13,12 @@ use crate::fighter::{
#[derive(Debug, Clone, Hash)]
pub struct LandingState {
pub grounded_common: GroundedStateCommon
pub grounded_common: GroundedStateCommon,
}
impl LandingState {
pub fn create(grounded_common: GroundedStateCommon) -> Self {
Self {
grounded_common
}
Self { grounded_common }
}
}
@@ -30,7 +28,9 @@ impl FighterStateImpl for LandingState {
fn check_interrupt(&self, state_context: &mut FighterStateContext) -> Option<FighterState> {
if state_context.is_current_animation_finished() {
grounded_movement_standstill_common_interrupts(state_context, &self.grounded_common)
} else if state_context.get_current_animation_frame() >= state_context.fighter_manifest.attributes.landing_iasa {
} else if state_context.get_current_animation_frame()
>= state_context.fighter_manifest.attributes.landing_iasa
{
dash::check_input(state_context, &self.grounded_common).or_else(|| {
ground::grounded_movement_common_interrupts(state_context, &self.grounded_common)
.or_else(|| walk::check_input(state_context, &self.grounded_common))
-1
View File
@@ -1,4 +1,3 @@
use bevy::prelude::*;
use super::{FighterStateContext, FighterStateImpl, ground, wait, walk};
+1 -1
View File
@@ -2,7 +2,7 @@ use super::{FighterStateContext, FighterStateImpl};
use crate::fighter::animation::AnimKind;
use crate::fighter::state::fall::FallState;
use crate::fighter::state::ground::{self, GroundedMotionResult, GroundedStateCommon};
use crate::fighter::state::{dash, jump, wait, walk, FighterState};
use crate::fighter::state::{FighterState, dash, jump, wait, walk};
#[derive(Debug, Clone, Hash)]
pub struct TurnState {
+7 -9
View File
@@ -1,12 +1,8 @@
use super::{FighterStateContext, FighterStateImpl, ground};
use crate::fighter::animation::AnimKind;
use crate::fighter::state::fall::FallState;
use crate::fighter::state::ground::{
GroundedMotionResult, GroundedStateCommon,
};
use crate::fighter::state::FighterState;
use crate::fighter::state::fall::FallState;
use crate::fighter::state::ground::{GroundedMotionResult, GroundedStateCommon};
#[derive(Debug, Clone, Hash)]
pub struct WaitState {
@@ -16,12 +12,14 @@ pub struct WaitState {
impl FighterStateImpl for WaitState {
const NAME: &'static str = "Wait";
fn check_interrupt(&self, state_context: &mut FighterStateContext) -> Option<FighterState> {
if let Some(state) = ground::grounded_movement_common_interrupts(state_context, &self.grounded_common) {
if let Some(state) =
ground::grounded_movement_common_interrupts(state_context, &self.grounded_common)
{
Some(state)
} else if let Some(state) = ground::grounded_movement_standstill_common_interrupts(
state_context,
&self.grounded_common)
{
&self.grounded_common,
) {
// Don't transition into yourself
if let FighterState::Wait(_) = state {
None
+3 -5
View File
@@ -1,10 +1,9 @@
use super::{FighterStateContext, FighterStateImpl, dash, ground, wait};
use crate::fighter::animation::AnimKind;
use crate::fighter::state::FighterState;
use crate::fighter::state::fall::FallState;
use crate::fighter::state::turn;
use crate::fighter::state::ground::{GroundedMotionResult, GroundedStateCommon};
use crate::fighter::state::turn;
#[derive(Debug, Clone, Hash)]
pub struct WalkState {
@@ -15,9 +14,8 @@ impl FighterStateImpl for WalkState {
const NAME: &'static str = "Walk";
fn check_interrupt(&self, state_context: &mut FighterStateContext) -> Option<FighterState> {
if let Some(state) = turn::check_input(state_context, &self.grounded_common) {
return Some(state)
}
else if let Some(state) =
return Some(state);
} else if let Some(state) =
ground::grounded_movement_common_interrupts(state_context, &self.grounded_common)
{
return Some(state);