Allow airborne fighters to pass through platforms while holding down (#65)
CI / build-and-test (push) Successful in 1m50s
CI / production-build (push) Canceled after 18s

## Summary
- Ignore soft-platform collision while an airborne fighter holds down.
- Preserve existing momentum and collision with solid floors.
- Add regression coverage for soft and solid platform behavior.

Reviewed-on: #65
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #65.
This commit is contained in:
2026-09-26 12:07:38 +00:00
committed by EIREXE
parent 615872f29c
commit a58b5eeec7
3 changed files with 99 additions and 3 deletions
+86 -2
View File
@@ -4,7 +4,7 @@ use crate::fighter::ecb::FighterPreviousECB;
use crate::fighter::{FighterECB, FighterPreviousTranslation, FighterTranslation};
use crate::math::segment::FGSegment2d;
use crate::math::vec::FGVec2;
use crate::stage::line::{StageCollision, StageLineID};
use crate::stage::line::{StageCollision, StageLineID, StagePolyType};
/*
pub fn collide_fighter_with_scene(
fighters: Query<(&FighterECB, &mut FighterVelocity, &mut FighterTranslation)>,
@@ -45,6 +45,7 @@ pub fn air_collide_with_stage(
ecb: &FighterECB,
stage_collision: &StageCollision,
ignored_polys: &[usize],
ignore_soft_platforms: bool,
) -> Option<AirCollisionWithStageResult> {
let ray_segment = FGSegment2d::new(
prev_translation.0 + prev_ecb.get_bottom_point(),
@@ -55,7 +56,10 @@ pub fn air_collide_with_stage(
.stage_polys
.iter()
.enumerate()
.filter(|(poly_idx, _)| !ignored_polys.contains(poly_idx))
.filter(|(poly_idx, poly)| {
!ignored_polys.contains(poly_idx)
&& !(ignore_soft_platforms && matches!(poly.poly_type, StagePolyType::Platform))
})
.filter_map(|(poly_idx, poly)| {
poly.intersect_ray(ray_segment).map(|intersect_result| {
(
@@ -95,6 +99,18 @@ mod tests {
)
}
fn solid_floor() -> StagePoly {
StagePoly::build(
StagePolyType::Closed,
&[
(FGVec2::lit("-2", "0"), StagePolyLineSegmentType::Floor),
(FGVec2::lit("2", "0"), StagePolyLineSegmentType::Floor),
(FGVec2::lit("2", "-2"), StagePolyLineSegmentType::Wall),
(FGVec2::lit("-2", "-2"), StagePolyLineSegmentType::Wall),
],
)
}
#[test]
fn airborne_collision_skips_ignored_polygons() {
let stage_collision = StageCollision {
@@ -122,10 +138,78 @@ mod tests {
&ecb,
&stage_collision,
&[1],
false,
)
.expect("the non-ignored platform should still be collidable");
assert_eq!(collision.line_id.polygon, 0);
assert_eq!(collision.hit_position, FGVec2::lit("0", "0"));
}
#[test]
fn airborne_collision_skips_soft_platforms_while_passing_down() {
let stage_collision = StageCollision {
stage_polys: vec![horizontal_platform("0")],
};
let previous_translation = FighterPreviousTranslation(FGVec2::lit("0", "2"));
let translation = FighterTranslation(FGVec2::lit("0", "-1"));
let previous_ecb = FighterPreviousECB {
bottom: FGVec2::ZERO,
top: FGVec2::ZERO,
left: FGVec2::ZERO,
right: FGVec2::ZERO,
};
let ecb = FighterECB {
bottom: FGVec2::ZERO,
top: FGVec2::ZERO,
left: FGVec2::ZERO,
right: FGVec2::ZERO,
};
assert!(air_collide_with_stage(
&previous_translation,
&translation,
&previous_ecb,
&ecb,
&stage_collision,
&[],
true,
)
.is_none());
}
#[test]
fn airborne_collision_still_hits_solid_floors_while_passing_down() {
let stage_collision = StageCollision {
stage_polys: vec![solid_floor()],
};
let previous_translation = FighterPreviousTranslation(FGVec2::lit("0", "2"));
let translation = FighterTranslation(FGVec2::lit("0", "-1"));
let previous_ecb = FighterPreviousECB {
bottom: FGVec2::ZERO,
top: FGVec2::ZERO,
left: FGVec2::ZERO,
right: FGVec2::ZERO,
};
let ecb = FighterECB {
bottom: FGVec2::ZERO,
top: FGVec2::ZERO,
left: FGVec2::ZERO,
right: FGVec2::ZERO,
};
let collision = air_collide_with_stage(
&previous_translation,
&translation,
&previous_ecb,
&ecb,
&stage_collision,
&[],
true,
)
.expect("solid floors remain collidable while passing down");
assert_eq!(collision.line_id.polygon, 0);
assert_eq!(collision.hit_position, FGVec2::lit("0", "0"));
}
}