Add waveland snapping #95

Merged
EIREXE merged 2 commits from waveland_snap into master 2026-10-14 06:27:51 +00:00
5 changed files with 44 additions and 17 deletions
Showing only changes of commit 5be69cab52 - Show all commits
+24 -11
View File
@@ -1,17 +1,9 @@
use crate::{ use crate::{
fighter::{ fighter::{
FighterInvulnerabilityState, FighterInvulnerabilityState, animation::AnimKind, state::{
animation::AnimKind, CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext, UpdateContext, air::{self, AirborneStateCommon}, fall::FallState, land::{LandingState, LandingType},
state::{
CollisionContext, EnterContext, FighterState, FighterStateImpl, InterruptContext,
UpdateContext,
air::{self, AirborneStateCommon},
fall::FallState,
land::LandingType,
}, },
}, }, input, math::vec::FGVec2, stage::line::{StageLineID, StagePolyLineSegmentType},
input,
math::vec::FGVec2,
}; };
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
@@ -52,6 +44,27 @@ impl FighterStateImpl for AirDodgeState {
&mut self, &mut self,
state_context: &mut CollisionContext, state_context: &mut CollisionContext,
) -> Option<FighterState> { ) -> Option<FighterState> {
let can_wavesnap = self.direction.y.is_zero() || self.direction.y.is_negative();
if can_wavesnap {
for (poly_idx, poly) in state_context.stage_collision.stage_polys.iter().enumerate() {
for (segment_idx, segment) in poly.segments.iter().enumerate() {
if !matches!(segment.segment_type, StagePolyLineSegmentType::Floor) {
continue;
}
let bottom_point = state_context.translation.0 + state_context.ecb.get_bottom_point();
let point_in_segment = segment.segment.closest_point(bottom_point);
let dist_to_segment = point_in_segment.distance(bottom_point);
if dist_to_segment <= state_context.waveland_snap_distance {
state_context.ecb.bottom = point_in_segment;
state_context.ecb.rebase_onto_bottom();
state_context.surface.land(StageLineID { polygon: poly_idx, segment: segment_idx }, state_context.ledge_info);
return Some(FighterState::Land(LandingState::create(LandingType::Special)));
}
}
}
}
air::collision_interrupt(state_context, LandingType::Special, false) air::collision_interrupt(state_context, LandingType::Special, false)
} }
+3
View File
@@ -260,6 +260,7 @@ pub struct CollisionContext<'a> {
pub ledge_info: &'a mut FighterLedgeInformation, pub ledge_info: &'a mut FighterLedgeInformation,
pub fighter_ledge_collision_box: FGRect, pub fighter_ledge_collision_box: FGRect,
pub downward_stick_threshold: FGi32, pub downward_stick_threshold: FGi32,
pub waveland_snap_distance: FGi32,
pub(crate) ecb_advanced: bool, pub(crate) ecb_advanced: bool,
} }
@@ -503,6 +504,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
downward_stick_threshold: game_settings.input_common.run_stick_threshold, downward_stick_threshold: game_settings.input_common.run_stick_threshold,
fighter_ledge_collision_box, fighter_ledge_collision_box,
ecb_advanced: false, ecb_advanced: false,
waveland_snap_distance: game_settings.fighter_common.waveland_snap_distance,
} }
} }
} }
@@ -891,6 +893,7 @@ mod tests {
}, },
downward_stick_threshold: FGi32::ONE, downward_stick_threshold: FGi32::ONE,
ecb_advanced: false, ecb_advanced: false,
waveland_snap_distance: FGi32::ZERO,
}; };
assert!(context.air_collide_with_ledges().is_none()); assert!(context.air_collide_with_ledges().is_none());
+2
View File
@@ -66,6 +66,8 @@ pub struct FighterSettingsCommon {
pub sakurai_angle_high_knockback_angle: FGi32, pub sakurai_angle_high_knockback_angle: FGi32,
/// past this threshold, the knockback is considered high, so it will use [sakurai_angle_high_knockback_angle] /// past this threshold, the knockback is considered high, so it will use [sakurai_angle_high_knockback_angle]
pub sakurai_angle_knockback_threshold: FGi32, pub sakurai_angle_knockback_threshold: FGi32,
pub waveland_snap_distance: FGi32,
} }
#[derive(Reflect, Serialize, Deserialize, Clone)] #[derive(Reflect, Serialize, Deserialize, Clone)]
#[reflect(opaque)] #[reflect(opaque)]
-1
View File
@@ -717,7 +717,6 @@ pub fn postprocess_input(
FighterAttackCommandType::Neutral FighterAttackCommandType::Neutral
}; };
dbg!(input.movement_flick.y, attack_type);
input.set_lifetime(FighterCommands::Attack, tap); input.set_lifetime(FighterCommands::Attack, tap);
input.set_attack_command_type(attack_type); input.set_attack_command_type(attack_type);
} }
+15 -5
View File
@@ -1,15 +1,15 @@
use bevy::{color::palettes::css::RED, prelude::*}; use bevy::{color::palettes::css::RED, prelude::*};
use crate::{ use crate::{
match_loading::MatchConfig, math::{int::FGi32, vec3::FGVec3}, stage::{ game_settings::GameSettings, match_loading::MatchConfig, math::{int::FGi32, vec3::FGVec3}, stage::{
line::{StageCollision, StageLedgeSide}, line::{StageCollision, StageLedgeSide, StagePolyLineSegmentType}, manifest::{StageManifest, StageManifestRegistry},
manifest::{StageManifest, StageManifestRegistry},
}, },
}; };
pub fn debug_draw_scene(mut gizmos: Gizmos, stage_collision: Res<StageCollision>) { pub fn debug_draw_scene(mut gizmos: Gizmos, stage_collision: Res<StageCollision>, game_settings: Res<GameSettings>) {
for poly in stage_collision.stage_polys.iter() { for poly in stage_collision.stage_polys.iter() {
for segment in &poly.segments { for (segment, next_segment) in poly.segments.iter().zip(poly.segments.iter().cycle().skip(1)) {
let wavedash_normal = ((next_segment.normal.to_vec2() + segment.normal.to_vec2()) * 0.5).normalize();
let from = segment.segment.point1().to_vec2(); let from = segment.segment.point1().to_vec2();
let to = segment.segment.point2().to_vec2(); let to = segment.segment.point2().to_vec2();
let from_3d = Vec3::new(from.x, from.y, 0.0); let from_3d = Vec3::new(from.x, from.y, 0.0);
@@ -18,6 +18,16 @@ pub fn debug_draw_scene(mut gizmos: Gizmos, stage_collision: Res<StageCollision>
gizmos.line(from_3d, end_3d, RED); gizmos.line(from_3d, end_3d, RED);
let midpoint = (from_3d + end_3d) * 0.5; let midpoint = (from_3d + end_3d) * 0.5;
gizmos.arrow(midpoint, midpoint + normal_3d, RED); gizmos.arrow(midpoint, midpoint + normal_3d, RED);
if matches!(segment.segment_type, StagePolyLineSegmentType::Floor) {
let radius = game_settings.fighter_common.waveland_snap_distance.to_num::<f32>();
let a = segment.segment.point1().to_vec2();
let a = Vec3::new(a.x, a.y, 0.0);
let b = segment.segment.point2().to_vec2();
let b = Vec3::new(b.x, b.y, 0.0);
let offset = Vec3::new(wavedash_normal.x, wavedash_normal.y, 0.0) * radius;
gizmos.line(a + offset, b + offset, bevy::color::palettes::css::HOT_PINK);
gizmos.line(a - offset, b - offset, bevy::color::palettes::css::HOT_PINK);
}
} }
} }