231 lines
7.4 KiB
Rust
231 lines
7.4 KiB
Rust
//! Resolving an integrated fighter position against the stage.
|
|
|
|
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, StagePolyType};
|
|
/*
|
|
pub fn collide_fighter_with_scene(
|
|
fighters: Query<(&FighterECB, &mut FighterVelocity, &mut FighterTranslation)>,
|
|
stage_planes: Query<&StagePoly>,
|
|
mut gizmos: Gizmos,
|
|
) {
|
|
for (ecb, mut velocity, mut translation) in fighters {
|
|
for plane in stage_planes {
|
|
let ray_from = translation.0;
|
|
let ray_to = translation.0 + Vec2::new(0.0, -ecb.vertical_half);
|
|
|
|
let intersection_result = plane.intersect_ray(Segment2d::new(ray_from, ray_to));
|
|
|
|
if let Some(result) = intersection_result {
|
|
gizmos.sphere(
|
|
Isometry3d::from(Vec3::new(result.position.x, result.position.y, 0.0)),
|
|
0.1f32,
|
|
HOT_PINK,
|
|
);
|
|
velocity.0 = plane.project(velocity.0);
|
|
translation.0 = result.position + Vec2::new(0.0, ecb.vertical_half);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
#[derive(Clone)]
|
|
pub struct AirCollisionWithStageResult {
|
|
pub line_id: StageLineID,
|
|
pub hit_position: FGVec2,
|
|
pub hit_normal: FGVec2,
|
|
}
|
|
|
|
pub fn air_collide_with_stage(
|
|
prev_translation: &FighterPreviousTranslation,
|
|
translation: &FighterTranslation,
|
|
prev_ecb: &FighterPreviousECB,
|
|
ecb: &FighterECB,
|
|
stage_collision: &StageCollision,
|
|
ignored_polys: &[usize],
|
|
can_ignore_soft_platforms: bool,
|
|
) -> Option<AirCollisionWithStageResult> {
|
|
let ray_segment = FGSegment2d::new(
|
|
prev_translation.0 + prev_ecb.get_bottom_point(),
|
|
translation.0 + ecb.get_bottom_point(),
|
|
);
|
|
|
|
stage_collision
|
|
.stage_polys
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(poly_idx, poly)| {
|
|
!ignored_polys.contains(poly_idx)
|
|
&& !(can_ignore_soft_platforms && matches!(poly.poly_type, StagePolyType::Platform))
|
|
})
|
|
.filter_map(|(poly_idx, poly)| {
|
|
poly.intersect_ray(ray_segment).map(|intersect_result| {
|
|
(
|
|
AirCollisionWithStageResult {
|
|
line_id: {
|
|
StageLineID {
|
|
polygon: poly_idx,
|
|
segment: intersect_result.segment_idx,
|
|
}
|
|
},
|
|
hit_position: intersect_result.position,
|
|
hit_normal: intersect_result.normal,
|
|
},
|
|
intersect_result
|
|
.position
|
|
.distance_squared(ray_segment.point2()),
|
|
)
|
|
})
|
|
})
|
|
.max_by(|(_, a), (_, b)| a.cmp(b))
|
|
.map(|(result, _)| result)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::fighter::ecb::{FighterECB, FighterPreviousECB};
|
|
use crate::stage::line::{StagePoly, StagePolyLineSegmentType, StagePolyType};
|
|
|
|
fn horizontal_platform(y: &str) -> StagePoly {
|
|
StagePoly::build(
|
|
StagePolyType::Platform,
|
|
&[
|
|
(FGVec2::lit("-2", y), StagePolyLineSegmentType::Floor),
|
|
(FGVec2::lit("2", y), StagePolyLineSegmentType::Floor),
|
|
],
|
|
)
|
|
}
|
|
|
|
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 {
|
|
stage_polys: vec![horizontal_platform("0"), horizontal_platform("1")],
|
|
};
|
|
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,
|
|
&[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());
|
|
|
|
assert_eq!(
|
|
air_collide_with_stage(
|
|
&previous_translation,
|
|
&translation,
|
|
&previous_ecb,
|
|
&ecb,
|
|
&stage_collision,
|
|
&[],
|
|
false,
|
|
)
|
|
.expect("air dodges still collide with soft platforms")
|
|
.hit_position,
|
|
FGVec2::lit("0", "0")
|
|
);
|
|
}
|
|
|
|
#[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"));
|
|
}
|
|
}
|