use crate::fighter::state::ground::GroundedStateCommon; use crate::fighter::state::land::{LandingState, LandingType}; use crate::fighter::state::{CollisionContext, FighterState}; use crate::fighter::*; pub fn integrate_gravity(velocity: &mut FighterVelocity, attributes: &FighterAttributes) { velocity.y -= attributes.gravity; velocity.y = velocity.y.max(-attributes.terminal_velocity); } pub fn apply_air_drift( velocity: &mut FighterVelocity, attribs: &FighterAttributes, input: &FighterInput, ) { if input.get_last_frame().movement.x.is_zero() { if velocity.x.abs() < attribs.air_friction { velocity.x = FGi32::ZERO; } else { let velocity_sign = velocity.x.signum(); velocity.x += (-velocity_sign) * attribs.air_friction; } } else { let target_velocity = input.get_last_frame().movement.x * attribs.air_drift_max; let mut acceleration = input.get_last_frame().movement.x.signum() * attribs.air_acceleration_base + input.get_last_frame().movement.x * attribs.air_acceleration_stick; // Acceleration opposes velocity if acceleration * velocity.x < 0 { // do nothing } else { let predicted_vel = velocity.x + acceleration; let will_go_past = predicted_vel.abs() > target_velocity.abs(); if velocity.x.abs() < target_velocity.abs() && will_go_past { // Prevent overshoot acceleration = target_velocity - velocity.x } else if velocity.x.abs() > target_velocity.abs() { let diff = (velocity.x - target_velocity).abs(); acceleration = velocity.x.signum() * (-(attribs.air_friction).min(diff)); } } velocity.x += acceleration; } } pub fn apply_air_motion( velocity: &FighterVelocity, translation: &mut FighterTranslation, prev_translation: &mut FighterPreviousTranslation, ) { // Vertical velocity should be 0 on the ground prev_translation.0 = translation.0; translation.0 += velocity.0; } pub fn update_standard_motion(ctx: &mut crate::fighter::state::UpdateContext) { integrate_gravity(ctx.velocity, ctx.fighter_attributes); apply_air_drift(ctx.velocity, ctx.fighter_attributes, ctx.input); apply_air_motion(ctx.velocity, ctx.translation, ctx.prev_translation); } pub fn collision_interrupt( ctx: &mut CollisionContext, landing_type: LandingType, ) -> Option { let res = ctx.air_collide_with_stage()?; ctx.translation.0 = res.hit_position - ctx.ecb.get_bottom_point(); let grounded_common = GroundedStateCommon { current_line_id: res.line_id, }; Some(FighterState::Land(LandingState::create( grounded_common, landing_type, ))) } #[cfg(test)] mod tests { use super::*; use crate::{ input::{FighterInput, FighterInputFrame}, math::vec::FGVec2, }; fn test_attributes() -> FighterAttributes { FighterAttributes { base_walk_accel: FGi32::ZERO, stick_walk_accel: FGi32::ZERO, max_walk_vel: FGi32::ZERO, dash_acceleration_duration: 0, dash_duration: 0, dash_initial_velocity: FGi32::ZERO, base_dash_accel: FGi32::ZERO, stick_dash_accel: FGi32::ZERO, max_dash_vel: FGi32::ZERO, jumpsquat_duration: 0, short_hop_vertical_velocity: FGi32::ZERO, full_jump_vertical_velocity: FGi32::ZERO, jump_horizontal_velocity: FGi32::ZERO, air_jump_multiplier: FGi32::ZERO, air_jump_horizontal_velocity: FGi32::ZERO, landing_iasa: 0, landing_duration: 0, air_acceleration_base: FGi32::ZERO, air_acceleration_stick: FGi32::ZERO, max_air_horizontal_velocity: FGi32::lit("1.2"), air_drift_max: FGi32::lit("1.2"), ground_friction: FGi32::ZERO, terminal_velocity: FGi32::ZERO, gravity: FGi32::ZERO, weight: FGi32::lit("1"), air_friction: FGi32::lit("0.001"), air_dodge_duration: 0, air_dodge_velocity: FGi32::ZERO, air_dodge_decay: FGi32::ZERO, } } #[test] fn zero_air_acceleration_is_symmetric() { let attributes = test_attributes(); let mut right_input = FighterInput::default(); right_input.push_input_frame(FighterInputFrame { movement: FGVec2::new(FGi32::ONE, FGi32::ZERO), ..default() }); let mut left_input = FighterInput::default(); left_input.push_input_frame(FighterInputFrame { movement: FGVec2::new(-FGi32::ONE, FGi32::ZERO), ..default() }); let mut right_velocity = FighterVelocity(FGVec2::ZERO); let mut left_velocity = FighterVelocity(FGVec2::ZERO); apply_air_drift(&mut right_velocity, &attributes, &right_input); apply_air_drift(&mut left_velocity, &attributes, &left_input); assert_eq!(right_velocity.x, FGi32::ZERO); assert_eq!(left_velocity.x, FGi32::ZERO); } }