mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-09 10:39:46 -10:00
Merge pull request #9632 from DUzzL/fix/9607-scaffolding-sight
🤖 Agent: Line of sight now clips with the viewer's collision context, as LivingEntity.hasLineOfSight's ClipContext(COLLIDER, entity) does, so scaffolding is open from below and solid from above. I ran the new test against prepared resources too: it builds the world and passes, not vacuously.
This commit is contained in:
2 files changed
+61
-6
No files matched your search
@@ -647,7 +647,11 @@ impl Mob {
|
||||
return false;
|
||||
};
|
||||
let eye = Vec3::new(self.position.x, self.eye_y(), self.position.z);
|
||||
let v = w.line_of_sight(eye, Vec3::new(t.position.x, t.eye_y, t.position.z));
|
||||
let v = w.line_of_sight_for(
|
||||
Some(self.position.y),
|
||||
eye,
|
||||
Vec3::new(t.position.x, t.eye_y, t.position.z),
|
||||
);
|
||||
self.ai.seen_cache.push((who, v));
|
||||
v
|
||||
}
|
||||
|
||||
@@ -116,6 +116,19 @@ impl MobWorld<'_> {
|
||||
}
|
||||
/// Level.clip with COLLIDER shapes (optionally stopping at any fluid): true on a miss.
|
||||
pub fn clear_between(&self, from: Vec3, to: Vec3, fluids_block: bool) -> bool {
|
||||
self.clear_between_for(None, from, to, fluids_block)
|
||||
}
|
||||
/// The same for an entity whose `getY()` is `feet_y`: `ClipContext` hands
|
||||
/// its `CollisionContext.of(entity)` to every block, so scaffolding is
|
||||
/// solid for a viewer above it and open for one below.
|
||||
fn clear_between_for(
|
||||
&self,
|
||||
feet_y: Option<f64>,
|
||||
from: Vec3,
|
||||
to: Vec3,
|
||||
fluids_block: bool,
|
||||
) -> bool {
|
||||
let context = feet_y.map(horizon_world::collision_context::CollisionContext::position);
|
||||
let mut scene = SceneCollisions {
|
||||
world: self.world,
|
||||
binding: self.collision,
|
||||
@@ -129,10 +142,15 @@ impl MobWorld<'_> {
|
||||
if self.facts.is_air(state) {
|
||||
return None;
|
||||
}
|
||||
let shape = self
|
||||
.collision
|
||||
.collision_shape(&mut scene, state, [p.x, p.y, p.z])
|
||||
.ok()?;
|
||||
let shape = match &context {
|
||||
Some(context) => self
|
||||
.collision
|
||||
.collision_shape_with_context(&mut scene, state, p, context),
|
||||
None => self
|
||||
.collision
|
||||
.collision_shape(&mut scene, state, [p.x, p.y, p.z]),
|
||||
}
|
||||
.ok()?;
|
||||
shape.clip(from, to, p).map(|_| ())
|
||||
});
|
||||
matches!(result, Ok(None))
|
||||
@@ -219,12 +237,16 @@ impl MobWorld<'_> {
|
||||
}
|
||||
/// LivingEntity.hasLineOfSight: eye to eye within 128 blocks.
|
||||
pub fn line_of_sight(&self, from_eye: Vec3, to: Vec3) -> bool {
|
||||
self.line_of_sight_for(None, from_eye, to)
|
||||
}
|
||||
/// The same for a viewer standing at `feet_y` (its collision context).
|
||||
pub fn line_of_sight_for(&self, feet_y: Option<f64>, from_eye: Vec3, to: Vec3) -> bool {
|
||||
let d =
|
||||
(to.x - from_eye.x).powi(2) + (to.y - from_eye.y).powi(2) + (to.z - from_eye.z).powi(2);
|
||||
if d > 128.0 * 128.0 {
|
||||
return false;
|
||||
}
|
||||
self.clear_between(from_eye, to, false)
|
||||
self.clear_between_for(feet_y, from_eye, to, false)
|
||||
}
|
||||
/// Level.canSeeSky: sky light 15 at the position (no roof above).
|
||||
pub fn can_see_sky(&self, x: i32, y: i32, z: i32) -> bool {
|
||||
@@ -542,3 +564,32 @@ pub(crate) fn test_player(position: Vec3) -> super::PlayerView {
|
||||
vehicle: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod scaffolding_sight_tests {
|
||||
use super::*;
|
||||
|
||||
/// ScaffoldingBlock.getCollisionShape: a viewer below the block sees
|
||||
/// through it, one above does not (issue 9607).
|
||||
#[test]
|
||||
fn scaffolding_blocks_sight_only_from_above() {
|
||||
let ran = super::with_built_test_world(
|
||||
&[],
|
||||
|world, registry| {
|
||||
world.chunks.insert(
|
||||
(0, 0),
|
||||
std::sync::Arc::new(crate::chunk::Chunk::empty(0, 0)),
|
||||
);
|
||||
let scaffolding = registry.default_state("minecraft:scaffolding").unwrap();
|
||||
world.set(0, 65, 0, scaffolding).unwrap();
|
||||
},
|
||||
|w, _| {
|
||||
let below = Vec3::new(0.5, 64.0 + 1.62, 0.5);
|
||||
let above = Vec3::new(0.5, 66.0 + 1.62, 0.5);
|
||||
assert!(w.line_of_sight_for(Some(64.0), below, above));
|
||||
assert!(!w.line_of_sight_for(Some(66.0), above, below));
|
||||
},
|
||||
);
|
||||
let _ = ran;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user