227 lines
7.7 KiB
Rust
227 lines
7.7 KiB
Rust
|
|
use crate::{
|
|
fighter::{
|
|
animation::AnimKind, state::{
|
|
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext, UpdateContext, air::{self, AirborneStateCommon}, ground::{self}, ground_attack,
|
|
},
|
|
}, input::{DigitalAction, FighterCommands},
|
|
};
|
|
#[derive(Debug, Hash, Clone, Copy)]
|
|
pub enum JumpType {
|
|
ShortHop,
|
|
LongJump,
|
|
DoubleJump,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Hash)]
|
|
pub enum JumpStateInitiator {
|
|
Tap,
|
|
Button,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Hash)]
|
|
pub struct JumpState {
|
|
pub jump_type: JumpType,
|
|
pub airborne_state_common: AirborneStateCommon,
|
|
pub initiator: JumpStateInitiator,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Hash)]
|
|
pub struct JumpSquatState {
|
|
pub duration_counter: u32,
|
|
pub initiator: JumpStateInitiator,
|
|
}
|
|
|
|
impl JumpSquatState {
|
|
pub fn create(initiator: JumpStateInitiator) -> Self {
|
|
Self {
|
|
duration_counter: 0,
|
|
initiator,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FighterStateImpl for JumpSquatState {
|
|
const NAME: &'static str = "JumpSquat";
|
|
|
|
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
|
|
if let Some(state) = ground_attack::check_interrupt(state_context) {
|
|
return Some(state);
|
|
}
|
|
// Special handling for wavedash from jump squat
|
|
if state_context.input.has_command(FighterCommands::Shield) {
|
|
Some(ground::perform_wavedash(state_context.input, state_context.game_settings))
|
|
} else if self.duration_counter >= state_context.fighter_attributes.jumpsquat_duration {
|
|
let tap_jump_held = matches!(self.initiator, JumpStateInitiator::Tap)
|
|
&& state_context.input.get_last_frame().movement.y
|
|
> state_context
|
|
.game_settings
|
|
.input_common
|
|
.smash_input_axis_threshold;
|
|
let jump_type = if tap_jump_held
|
|
|| state_context
|
|
.input
|
|
.get_last_frame()
|
|
.is_held(DigitalAction::Jump)
|
|
{
|
|
JumpType::LongJump
|
|
} else {
|
|
JumpType::ShortHop
|
|
};
|
|
Some(FighterState::Jump(JumpState {
|
|
jump_type,
|
|
airborne_state_common: AirborneStateCommon::default(),
|
|
initiator: self.initiator.clone(),
|
|
}))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, state_context: &mut UpdateContext) {
|
|
self.duration_counter += 1;
|
|
ground::update_friction_motion(state_context);
|
|
}
|
|
|
|
fn check_collision_interrupt(
|
|
&mut self,
|
|
state_context: &mut CollisionContext,
|
|
) -> Option<FighterState> {
|
|
ground::collision_interrupt(state_context, false)
|
|
}
|
|
|
|
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
|
state_context.play_animation(AnimKind::JumpSquat, false);
|
|
state_context.input.enable_hold_all(10);
|
|
state_context.input.clear_command(FighterCommands::Jump);
|
|
state_context.input.clear_command(FighterCommands::TapJump);
|
|
}
|
|
|
|
fn on_exit(&mut self, state_context: &mut crate::fighter::state::ExitContext) {
|
|
state_context.input.disable_hold_all();
|
|
}
|
|
|
|
fn is_visually_grounded<'a>(&'a self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl FighterStateImpl for JumpState {
|
|
const NAME: &'static str = "Jump";
|
|
fn check_interrupt(&self, state_context: &mut InterruptContext) -> Option<FighterState> {
|
|
air::air_interrupts_common(state_context, &self.airborne_state_common)
|
|
}
|
|
|
|
fn update(&mut self, state_context: &mut UpdateContext) {
|
|
air::update_standard_motion(state_context, &mut self.airborne_state_common);
|
|
}
|
|
|
|
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
|
if let JumpType::DoubleJump = self.jump_type {
|
|
state_context.input.clear_command(FighterCommands::Jump);
|
|
}
|
|
let movement_stick_x = state_context.input.get_last_frame().movement.x;
|
|
match self.jump_type {
|
|
JumpType::ShortHop | JumpType::LongJump => {
|
|
if movement_stick_x.is_zero()
|
|
|| movement_stick_x.signum() == state_context.facing_direction.to_sign()
|
|
{
|
|
state_context.play_animation(AnimKind::JumpForward, false);
|
|
} else {
|
|
state_context.play_animation(AnimKind::JumpBack, false);
|
|
}
|
|
}
|
|
JumpType::DoubleJump => {
|
|
if movement_stick_x.is_zero()
|
|
|| movement_stick_x.signum() == state_context.facing_direction.to_sign()
|
|
{
|
|
state_context.play_animation(AnimKind::DoubleJumpForward, false);
|
|
} else {
|
|
state_context.play_animation(AnimKind::DoubleJumpBack, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
let vertical_vel = match self.jump_type {
|
|
JumpType::ShortHop => state_context.fighter_attributes.short_hop_vertical_velocity,
|
|
JumpType::LongJump => state_context.fighter_attributes.full_jump_vertical_velocity,
|
|
JumpType::DoubleJump => {
|
|
state_context.fighter_attributes.full_jump_vertical_velocity
|
|
* state_context.fighter_attributes.air_jump_multiplier
|
|
}
|
|
};
|
|
|
|
match self.jump_type {
|
|
JumpType::ShortHop | JumpType::LongJump => {
|
|
state_context.velocity.x +=
|
|
state_context.fighter_attributes.jump_horizontal_velocity * movement_stick_x;
|
|
}
|
|
JumpType::DoubleJump => {
|
|
state_context.velocity.x = state_context
|
|
.fighter_attributes
|
|
.air_jump_horizontal_velocity
|
|
* movement_stick_x;
|
|
}
|
|
};
|
|
|
|
state_context.velocity.y = vertical_vel;
|
|
}
|
|
|
|
fn check_collision_interrupt(
|
|
&mut self,
|
|
state_context: &mut CollisionContext,
|
|
) -> Option<FighterState> {
|
|
air::collision_interrupt(
|
|
state_context,
|
|
crate::fighter::state::land::LandingType::Normal,
|
|
true,
|
|
)
|
|
}
|
|
|
|
fn is_visually_grounded<'a>(&'a self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn surface_policy(&self) -> super::SurfacePolicy {
|
|
use crate::fighter::surface::EcbLockPolicy;
|
|
match self.jump_type {
|
|
JumpType::DoubleJump => super::SurfacePolicy::Airborne(EcbLockPolicy::Restart),
|
|
JumpType::ShortHop | JumpType::LongJump => {
|
|
super::SurfacePolicy::Airborne(EcbLockPolicy::StartOnDetach)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
|
|
if state_context.input.has_command(FighterCommands::Jump) {
|
|
Some(FighterState::JumpSquat(JumpSquatState::create(
|
|
JumpStateInitiator::Button,
|
|
)))
|
|
} else if state_context.input.has_command(FighterCommands::TapJump) {
|
|
Some(FighterState::JumpSquat(JumpSquatState::create(
|
|
JumpStateInitiator::Tap,
|
|
)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn check_input_double_jump(state_context: &InterruptContext) -> Option<FighterState> {
|
|
if state_context.input.has_command(FighterCommands::Jump) {
|
|
Some(FighterState::Jump(JumpState {
|
|
jump_type: JumpType::DoubleJump,
|
|
airborne_state_common: AirborneStateCommon::default(),
|
|
initiator: JumpStateInitiator::Button,
|
|
}))
|
|
} else if state_context.input.has_command(FighterCommands::TapJump) {
|
|
Some(FighterState::Jump(JumpState {
|
|
jump_type: JumpType::DoubleJump,
|
|
airborne_state_common: AirborneStateCommon::default(),
|
|
initiator: JumpStateInitiator::Tap,
|
|
}))
|
|
} else {
|
|
None
|
|
}
|
|
}
|