Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04f6f2386f | ||
|
|
84e07a80aa |
@@ -88,6 +88,8 @@ pub enum AnimKind {
|
||||
CrouchIdle,
|
||||
CrouchExit,
|
||||
|
||||
PlatformPass,
|
||||
|
||||
FlinchGround,
|
||||
FlinchAir,
|
||||
|
||||
|
||||
+63
-29
@@ -1,25 +1,30 @@
|
||||
use super::{
|
||||
CollisionContext, EnterContext, FighterStateImpl, InterruptContext, UpdateContext, ground,
|
||||
};
|
||||
use crate::fighter::FighterSurfaceState;
|
||||
use crate::fighter::animation::AnimKind;
|
||||
use crate::fighter::state::air::AirborneStateCommon;
|
||||
use crate::fighter::state::ground::{
|
||||
grounded_movement_common_interrupts, grounded_movement_standstill_common_interrupts,
|
||||
};
|
||||
use crate::fighter::state::platform_pass::PlatformPassState;
|
||||
use crate::fighter::state::{FighterState, dash, ground_attack, turn, walk};
|
||||
use crate::fighter::state::ground::{grounded_movement_common_interrupts, grounded_movement_standstill_common_interrupts};
|
||||
use crate::input::FighterCommands;
|
||||
use crate::stage::line::StagePolyType;
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum CrouchSubState {
|
||||
Entering,
|
||||
Entering { pass_queued: bool },
|
||||
Idle,
|
||||
Exiting
|
||||
Exiting,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct CrouchState {
|
||||
sub_state: CrouchSubState
|
||||
sub_state: CrouchSubState,
|
||||
}
|
||||
|
||||
pub fn crouch_idle_interrupts(
|
||||
state_context: &InterruptContext,
|
||||
) -> Option<FighterState> {
|
||||
pub fn crouch_idle_interrupts(state_context: &InterruptContext) -> Option<FighterState> {
|
||||
if let Some(state) = ground_attack::check_interrupt(state_context) {
|
||||
Some(state)
|
||||
} else if let Some(state) = dash::check_input(state_context) {
|
||||
@@ -33,9 +38,7 @@ pub fn crouch_idle_interrupts(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn crouch_entering_interrupts(
|
||||
state_context: &InterruptContext,
|
||||
) -> Option<FighterState> {
|
||||
pub fn crouch_entering_interrupts(state_context: &InterruptContext) -> Option<FighterState> {
|
||||
if let Some(state) = ground_attack::check_interrupt(state_context) {
|
||||
Some(state)
|
||||
} else {
|
||||
@@ -51,54 +54,85 @@ impl FighterStateImpl for CrouchState {
|
||||
}
|
||||
|
||||
match self.sub_state {
|
||||
CrouchSubState::Entering => {
|
||||
CrouchSubState::Entering { pass_queued } => {
|
||||
// pass
|
||||
if let FighterSurfaceState::Grounded { line_id } = state_context.surface {
|
||||
if let Some(stage_poly) = state_context
|
||||
.stage_collision
|
||||
.stage_polys
|
||||
.get(line_id.polygon)
|
||||
{
|
||||
let is_inputting_crouch = -state_context.game_settings.input_common.run_stick_threshold > state_context.input.get_last_frame().movement.y;
|
||||
let is_on_a_soft_platform = matches!(stage_poly.poly_type, StagePolyType::Platform);
|
||||
if pass_queued && is_inputting_crouch && is_on_a_soft_platform
|
||||
{
|
||||
return Some(FighterState::PlatformPass(PlatformPassState::new(
|
||||
AirborneStateCommon::default(),
|
||||
line_id.polygon,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// While entering the crouch animation we do not allow movement inputs, but we do allow attacks and jumps
|
||||
if state_context.is_current_animation_finished() {
|
||||
return Some(FighterState::Crouch(
|
||||
CrouchState {
|
||||
return Some(FighterState::Crouch(CrouchState {
|
||||
sub_state: CrouchSubState::Idle,
|
||||
}
|
||||
));
|
||||
}));
|
||||
} else {
|
||||
return crouch_entering_interrupts(state_context);
|
||||
}
|
||||
},
|
||||
CrouchSubState::Idle => {
|
||||
if state_context.input.get_last_frame().movement.y > -state_context.game_settings.input_common.run_stick_threshold {
|
||||
return Some(FighterState::Crouch (
|
||||
CrouchState {
|
||||
sub_state: CrouchSubState::Exiting,
|
||||
}
|
||||
));
|
||||
CrouchSubState::Idle => {
|
||||
if state_context.input.get_last_frame().movement.y
|
||||
> -state_context.game_settings.input_common.run_stick_threshold
|
||||
{
|
||||
return Some(FighterState::Crouch(CrouchState {
|
||||
sub_state: CrouchSubState::Exiting,
|
||||
}));
|
||||
} else {
|
||||
return crouch_idle_interrupts(state_context);
|
||||
}
|
||||
},
|
||||
}
|
||||
CrouchSubState::Exiting => {
|
||||
if state_context.is_current_animation_finished() {
|
||||
return grounded_movement_standstill_common_interrupts(state_context);
|
||||
} else {
|
||||
return crouch_idle_interrupts(state_context);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
||||
match self.sub_state {
|
||||
CrouchSubState::Entering => {
|
||||
CrouchSubState::Entering { .. } => {
|
||||
state_context.play_animation(AnimKind::CrouchEnter, false);
|
||||
},
|
||||
}
|
||||
CrouchSubState::Idle => {
|
||||
state_context.play_animation(AnimKind::CrouchIdle, true);
|
||||
},
|
||||
}
|
||||
CrouchSubState::Exiting => {
|
||||
state_context.play_animation(AnimKind::CrouchExit, false);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, state_context: &mut UpdateContext) {
|
||||
if let CrouchSubState::Entering {
|
||||
pass_queued,
|
||||
} = &mut self.sub_state
|
||||
{
|
||||
if state_context
|
||||
.input
|
||||
.has_command(FighterCommands::SmashMoveDown)
|
||||
{
|
||||
state_context
|
||||
.input
|
||||
.clear_command(FighterCommands::SmashMoveDown);
|
||||
*pass_queued = true;
|
||||
}
|
||||
}
|
||||
ground::update_friction_motion(state_context);
|
||||
}
|
||||
|
||||
@@ -119,7 +153,7 @@ pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
|
||||
|
||||
if input_frame.movement.y <= -state_context.game_settings.input_common.run_stick_threshold {
|
||||
return Some(FighterState::Crouch(CrouchState {
|
||||
sub_state: CrouchSubState::Entering
|
||||
sub_state: CrouchSubState::Entering { pass_queued: false },
|
||||
}));
|
||||
}
|
||||
None
|
||||
|
||||
@@ -42,6 +42,7 @@ pub mod turn;
|
||||
pub mod wait;
|
||||
pub mod walk;
|
||||
pub mod crouch;
|
||||
pub mod platform_pass;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum SurfacePolicy {
|
||||
@@ -127,7 +128,9 @@ pub struct InterruptContext<'a> {
|
||||
pub facing_direction: &'a mut FighterFacingDirection,
|
||||
pub game_settings: &'a GameSettings,
|
||||
pub fighter_attributes: &'a FighterAttributes,
|
||||
pub surface: &'a FighterSurfaceState,
|
||||
pub animation: AnimationStatus,
|
||||
pub stage_collision: &'a StageCollision,
|
||||
}
|
||||
|
||||
impl InterruptContext<'_> {
|
||||
@@ -148,8 +151,10 @@ pub struct EnterContext<'a> {
|
||||
pub stale_move_queue: &'a mut FighterStaleMoveQueue,
|
||||
pub facing_direction: &'a mut FighterFacingDirection,
|
||||
pub animation_frame: &'a mut FighterAnimationFrame,
|
||||
pub surface: &'a mut FighterSurfaceState,
|
||||
pub fighter_attributes: &'a FighterAttributes,
|
||||
pub attack_script_assets: &'a FighterAttackScriptAssets,
|
||||
pub game_settings: &'a GameSettings,
|
||||
}
|
||||
|
||||
impl EnterContext<'_> {
|
||||
@@ -160,6 +165,7 @@ impl EnterContext<'_> {
|
||||
|
||||
pub struct ExitContext<'a> {
|
||||
pub hitboxes: &'a mut FighterHitboxes,
|
||||
pub surface: &'a mut FighterSurfaceState,
|
||||
}
|
||||
|
||||
pub struct UpdateContext<'a> {
|
||||
@@ -214,6 +220,7 @@ impl CollisionContext<'_> {
|
||||
fighter_states! {
|
||||
Wait => wait::WaitState,
|
||||
Walk => walk::WalkState,
|
||||
PlatformPass => platform_pass::PlatformPassState,
|
||||
Crouch => crouch::CrouchState,
|
||||
Dash => dash::DashState,
|
||||
Run => run::RunState,
|
||||
@@ -268,6 +275,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
||||
animations: &'a FighterAnimations,
|
||||
baked_animations: &'a Assets<BakedFighterAnimations>,
|
||||
attributes: &'a FighterAttributes,
|
||||
stage_collision: &'a StageCollision,
|
||||
) -> InterruptContext<'a> {
|
||||
let animation = self.animation_status(animations, baked_animations);
|
||||
InterruptContext {
|
||||
@@ -276,6 +284,8 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
||||
game_settings,
|
||||
fighter_attributes: attributes,
|
||||
animation,
|
||||
surface: &self.surface,
|
||||
stage_collision,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +293,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
||||
&'a mut self,
|
||||
attributes: &'a FighterAttributes,
|
||||
attack_script_assets: &'a FighterAttackScriptAssets,
|
||||
game_settings: &'a GameSettings,
|
||||
) -> EnterContext<'a> {
|
||||
EnterContext {
|
||||
input: &mut self.input,
|
||||
@@ -292,14 +303,17 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
||||
stale_move_queue: &mut self.stale_move_queue,
|
||||
facing_direction: &mut self.facing_direction,
|
||||
animation_frame: &mut self.animation_frame,
|
||||
surface: &mut self.surface,
|
||||
fighter_attributes: attributes,
|
||||
attack_script_assets,
|
||||
game_settings
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_context(&mut self) -> ExitContext<'_> {
|
||||
ExitContext {
|
||||
hitboxes: &mut self.hitboxes,
|
||||
surface: &mut self.surface,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,7 +421,7 @@ fn transition_state(
|
||||
game_settings.fighter_common.leave_ground_ecb_frame_lock_duration,
|
||||
);
|
||||
}
|
||||
next.on_enter(&mut frame.enter_context(attributes, attack_script_assets));
|
||||
next.on_enter(&mut frame.enter_context(attributes, attack_script_assets, game_settings));
|
||||
*current = next;
|
||||
info!("{} -> {}", old_name, new_name);
|
||||
}
|
||||
@@ -417,6 +431,7 @@ pub fn state_interrupt_system(
|
||||
game_settings: Res<GameSettings>,
|
||||
baked_animations: Res<Assets<BakedFighterAnimations>>,
|
||||
fighter_manifests: Res<Assets<FighterManifest>>,
|
||||
stage_collision: Res<StageCollision>,
|
||||
) {
|
||||
for fighter in &mut query {
|
||||
let StatefulFighterQueryItem {
|
||||
@@ -490,6 +505,7 @@ pub fn state_interrupt_system(
|
||||
animations,
|
||||
&baked_animations,
|
||||
&manifest.attributes,
|
||||
&stage_collision
|
||||
))
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use super::{CollisionContext, EnterContext, FighterStateImpl, InterruptContext, UpdateContext};
|
||||
use crate::fighter::animation::AnimKind;
|
||||
use crate::fighter::state::air::AirborneStateCommon;
|
||||
use crate::fighter::state::{ExitContext, FighterState, air, jump};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Default)]
|
||||
pub struct PlatformPassState {
|
||||
airborne_state_common: AirborneStateCommon,
|
||||
ignored_polygon: usize,
|
||||
}
|
||||
|
||||
impl PlatformPassState {
|
||||
pub fn new(airborne_state_common: AirborneStateCommon, ignored_polygon: usize) -> Self {
|
||||
Self {
|
||||
airborne_state_common,
|
||||
ignored_polygon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FighterStateImpl for PlatformPassState {
|
||||
const NAME: &'static str = "PlatformPass";
|
||||
|
||||
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
|
||||
jump::check_input_double_jump(state_context)
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
||||
state_context.surface.ignore_polygon(self.ignored_polygon, state_context.game_settings.fighter_common.platform_pass_platform_ignore_duration_frames);
|
||||
state_context.velocity.y = state_context.game_settings.fighter_common.platform_pass_initial_downwards_velocity;
|
||||
state_context.play_animation(AnimKind::PlatformPass, false);
|
||||
}
|
||||
|
||||
fn on_exit(&mut self, state_context: &mut ExitContext) {
|
||||
state_context.surface.clear_polygon_ignore(self.ignored_polygon);
|
||||
}
|
||||
|
||||
fn update(&mut self, state_context: &mut UpdateContext) {
|
||||
air::update_standard_motion(state_context, &mut self.airborne_state_common);
|
||||
}
|
||||
|
||||
fn check_collision_interrupt(
|
||||
&mut self,
|
||||
state_context: &mut CollisionContext,
|
||||
) -> Option<FighterState> {
|
||||
air::collision_interrupt(
|
||||
state_context,
|
||||
crate::fighter::state::land::LandingType::Normal,
|
||||
)
|
||||
}
|
||||
|
||||
fn is_visually_grounded<'a>(
|
||||
&'a self,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn surface_policy(&self) -> super::SurfacePolicy {
|
||||
super::SurfacePolicy::Airborne(crate::fighter::surface::EcbLockPolicy::StartOnDetach)
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,14 @@ impl FighterSurfaceState {
|
||||
*self = Self::Grounded { line_id };
|
||||
}
|
||||
|
||||
pub fn clear_polygon_ignore(&mut self, polygon: usize) {
|
||||
let Self::Airborne { ignored_polys, .. } = self else {
|
||||
return;
|
||||
};
|
||||
|
||||
ignored_polys.retain(|element| element.polygon != polygon);
|
||||
}
|
||||
|
||||
pub fn ignore_polygon(&mut self, polygon: usize, duration_frames: u32) {
|
||||
let Self::Airborne { ignored_polys, .. } = self else {
|
||||
return;
|
||||
|
||||
@@ -39,7 +39,9 @@ pub struct FighterSettingsCommon {
|
||||
|
||||
/// Maximum angle changed caused by directional influence
|
||||
pub di_max_angle: FGi32,
|
||||
pub leave_ground_ecb_frame_lock_duration: u32
|
||||
pub leave_ground_ecb_frame_lock_duration: u32,
|
||||
pub platform_pass_platform_ignore_duration_frames: u32,
|
||||
pub platform_pass_initial_downwards_velocity: FGi32,
|
||||
}
|
||||
#[derive(Reflect, Serialize, Deserialize, Clone)]
|
||||
#[reflect(opaque)]
|
||||
|
||||
@@ -233,6 +233,8 @@ pub fn postprocess_input(
|
||||
input.clear_command(FighterCommands::SmashMoveUp);
|
||||
input.clear_command(FighterCommands::SmashMoveDown);
|
||||
|
||||
input.frames_in_y_flick_deadzone = 0xFF;
|
||||
|
||||
let command_to_set = if input.current_frame.movement.y > 0.0 {
|
||||
FighterCommands::SmashMoveUp
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ pub struct StageLineID {
|
||||
|
||||
#[derive(Reflect, Clone, Default)]
|
||||
pub struct StagePoly {
|
||||
poly_type: StagePolyType,
|
||||
pub poly_type: StagePolyType,
|
||||
pub segments: Vec<StagePolyLineSegment>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user