Add waveland snapping #95

Merged
EIREXE merged 2 commits from waveland_snap into master 2026-10-14 06:27:51 +00:00
7 changed files with 19 additions and 35 deletions
Showing only changes of commit 4294544944 - Show all commits
+1
View File
@@ -129,6 +129,7 @@ pub fn collision_interrupt(
) -> Option<FighterState> { ) -> Option<FighterState> {
if ctx.velocity.y <= FGi32::ZERO { if ctx.velocity.y <= FGi32::ZERO {
if let Some(ledge_result) = ctx.air_collide_with_ledges() { if let Some(ledge_result) = ctx.air_collide_with_ledges() {
ctx.apply_desired_ecb();
return Some(FighterState::LedgeGrab(LedgeGrabState::new(ledge_result.ledge))); return Some(FighterState::LedgeGrab(LedgeGrabState::new(ledge_result.ledge)));
} }
} }
+1 -1
View File
@@ -56,7 +56,7 @@ impl FighterStateImpl for AirDodgeState {
let point_in_segment = segment.segment.closest_point(bottom_point); let point_in_segment = segment.segment.closest_point(bottom_point);
let dist_to_segment = point_in_segment.distance(bottom_point); let dist_to_segment = point_in_segment.distance(bottom_point);
if dist_to_segment <= state_context.waveland_snap_distance { if dist_to_segment <= state_context.waveland_snap_distance {
state_context.ecb.bottom = point_in_segment; state_context.ecb.bottom = point_in_segment - state_context.translation.0;
state_context.ecb.rebase_onto_bottom(); state_context.ecb.rebase_onto_bottom();
state_context.surface.land(StageLineID { polygon: poly_idx, segment: segment_idx }, state_context.ledge_info); state_context.surface.land(StageLineID { polygon: poly_idx, segment: segment_idx }, state_context.ledge_info);
return Some(FighterState::Land(LandingState::create(LandingType::Special))); return Some(FighterState::Land(LandingState::create(LandingType::Special)));
+2 -1
View File
@@ -43,8 +43,9 @@ impl FighterStateImpl for AngelState {
fn check_collision_interrupt( fn check_collision_interrupt(
&mut self, &mut self,
_state_context: &mut super::CollisionContext, state_context: &mut super::CollisionContext,
) -> Option<super::FighterState> { ) -> Option<super::FighterState> {
state_context.apply_desired_ecb();
None None
} }
+2 -5
View File
@@ -211,7 +211,6 @@ pub fn collide_with_stage_grounded(
crate::fighter::surface::EcbLockPolicy::StartOnDetach, crate::fighter::surface::EcbLockPolicy::StartOnDetach,
state_context.leave_ground_ecb_lock_duration, state_context.leave_ground_ecb_lock_duration,
); );
state_context.ecb_advanced = true;
return GroundedMotionResult::InAir; return GroundedMotionResult::InAir;
}; };
match resolve_grounded_position( match resolve_grounded_position(
@@ -238,7 +237,6 @@ pub fn collide_with_stage_grounded(
crate::fighter::surface::EcbLockPolicy::StartOnDetach, crate::fighter::surface::EcbLockPolicy::StartOnDetach,
state_context.leave_ground_ecb_lock_duration, state_context.leave_ground_ecb_lock_duration,
); );
state_context.ecb_advanced = true;
return GroundedMotionResult::InAir; return GroundedMotionResult::InAir;
} }
} }
@@ -247,7 +245,6 @@ pub fn collide_with_stage_grounded(
*state_context.ecb = current_ecb; *state_context.ecb = current_ecb;
state_context.translation.0 = current_position; state_context.translation.0 = current_position;
state_context.surface.land(line_id, state_context.ledge_info); state_context.surface.land(line_id, state_context.ledge_info);
state_context.ecb_advanced = true;
GroundedMotionResult::InGround GroundedMotionResult::InGround
} }
@@ -419,7 +416,7 @@ mod tests {
origin: FGVec2::ZERO, origin: FGVec2::ZERO,
end: FGVec2::ZERO, end: FGVec2::ZERO,
}, },
ecb_advanced: false, waveland_snap_distance: FGi32::ZERO,
}; };
assert!(matches!( assert!(matches!(
@@ -484,7 +481,7 @@ mod tests {
origin: FGVec2::ZERO, origin: FGVec2::ZERO,
end: FGVec2::ZERO, end: FGVec2::ZERO,
}, },
ecb_advanced: false, waveland_snap_distance: FGi32::ZERO,
}; };
assert!(matches!( assert!(matches!(
+2 -1
View File
@@ -144,8 +144,9 @@ impl FighterStateImpl for LedgeActionState {
fn check_collision_interrupt( fn check_collision_interrupt(
&mut self, &mut self,
_state_context: &mut CollisionContext, state_context: &mut CollisionContext,
) -> Option<FighterState> { ) -> Option<FighterState> {
state_context.apply_desired_ecb();
None None
} }
+2 -1
View File
@@ -102,8 +102,9 @@ impl FighterStateImpl for LedgeWaitState {
fn check_collision_interrupt( fn check_collision_interrupt(
&mut self, &mut self,
_state_context: &mut CollisionContext, state_context: &mut CollisionContext,
) -> Option<FighterState> { ) -> Option<FighterState> {
state_context.apply_desired_ecb();
None None
} }
+8 -25
View File
@@ -120,8 +120,9 @@ pub trait FighterStateImpl: Sized {
fn check_collision_interrupt( fn check_collision_interrupt(
&mut self, &mut self,
_state_context: &mut CollisionContext, state_context: &mut CollisionContext,
) -> Option<FighterState> { ) -> Option<FighterState> {
state_context.apply_desired_ecb();
None None
} }
@@ -261,7 +262,6 @@ pub struct CollisionContext<'a> {
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 waveland_snap_distance: FGi32,
pub(crate) ecb_advanced: bool,
} }
impl CollisionContext<'_> { impl CollisionContext<'_> {
@@ -269,7 +269,6 @@ impl CollisionContext<'_> {
/// substep resolver can later replace this with fractional steps. /// substep resolver can later replace this with fractional steps.
pub fn apply_desired_ecb(&mut self) { pub fn apply_desired_ecb(&mut self) {
ecb::interpolate_ecb(self.ecb, *self.desired_ecb, FGi32::ONE); ecb::interpolate_ecb(self.ecb, *self.desired_ecb, FGi32::ONE);
self.ecb_advanced = true;
} }
pub fn air_collide_with_stage( pub fn air_collide_with_stage(
@@ -279,7 +278,7 @@ impl CollisionContext<'_> {
let ignored_polys = self.surface.ignored_polygon_ids(); let ignored_polys = self.surface.ignored_polygon_ids();
let ignore_soft_platforms = can_ignore_soft_platforms let ignore_soft_platforms = can_ignore_soft_platforms
&& self.input.get_last_frame().movement.y <= -self.downward_stick_threshold; && self.input.get_last_frame().movement.y <= -self.downward_stick_threshold;
let result = crate::fighter::collision::air_collide_with_stage_substepped( crate::fighter::collision::air_collide_with_stage_substepped(
self.prev_translation, self.prev_translation,
self.translation, self.translation,
self.velocity, self.velocity,
@@ -288,15 +287,7 @@ impl CollisionContext<'_> {
self.stage_collision, self.stage_collision,
&ignored_polys, &ignored_polys,
ignore_soft_platforms, ignore_soft_platforms,
); )
self.ecb_advanced = true;
result
}
fn finish_ecb_frame(&mut self) {
if !self.ecb_advanced {
self.apply_desired_ecb();
}
} }
pub fn air_collide_with_ledges( pub fn air_collide_with_ledges(
&self, &self,
@@ -503,7 +494,6 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
.leave_ground_ecb_frame_lock_duration, .leave_ground_ecb_frame_lock_duration,
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,
waveland_snap_distance: game_settings.fighter_common.waveland_snap_distance, waveland_snap_distance: game_settings.fighter_common.waveland_snap_distance,
} }
} }
@@ -799,13 +789,9 @@ pub fn state_collision_interrupt_system(
continue; continue;
} }
let new_state = { let new_state = state.check_collision_interrupt(
let mut collision_context = &mut frame.collision_context(&stage_collision, manifest, &game_settings),
frame.collision_context(&stage_collision, manifest, &game_settings); );
let new_state = state.check_collision_interrupt(&mut collision_context);
collision_context.finish_ecb_frame();
new_state
};
if let Some(new_state) = new_state { if let Some(new_state) = new_state {
transition_state( transition_state(
&mut state, &mut state,
@@ -874,7 +860,7 @@ mod tests {
}; };
let previous_ecb = FighterPreviousECB::from(ecb); let previous_ecb = FighterPreviousECB::from(ecb);
let mut presentation_snap = FighterPresentationSnap::default(); let mut presentation_snap = FighterPresentationSnap::default();
let mut context = CollisionContext { let context = CollisionContext {
translation: &mut translation, translation: &mut translation,
prev_translation: &previous_translation, prev_translation: &previous_translation,
input: &input, input: &input,
@@ -892,7 +878,6 @@ mod tests {
end: FGVec2::lit("1", "-1"), end: FGVec2::lit("1", "-1"),
}, },
downward_stick_threshold: FGi32::ONE, downward_stick_threshold: FGi32::ONE,
ecb_advanced: false,
waveland_snap_distance: FGi32::ZERO, waveland_snap_distance: FGi32::ZERO,
}; };
@@ -902,7 +887,5 @@ mod tests {
context.air_collide_with_ledges().map(|result| result.ledge), context.air_collide_with_ledges().map(|result| result.ledge),
Some(ledge_id) Some(ledge_id)
); );
context.finish_ecb_frame();
assert_eq!(context.ecb.top, FGVec2::lit("0", "2"));
} }
} }