use super::Interactable;
use crate::prelude::*;
use crate::private::Sealed;
use std::time::Duration;
#[derive(Debug)]
pub struct WorldInteractorImpl<'a, T> {
interactable: &'a dyn Interactable<T>,
id: Id,
}
impl<'a, T> WorldInteractorImpl<'a, T> {
pub fn new(interactable: &'a dyn Interactable<T>, id: Id) -> Self {
Self { interactable, id }
}
}
impl<T> Sealed for WorldInteractorImpl<'_, T> {}
impl<'a, T> WorldInteractor<T> for WorldInteractorImpl<'a, T>
where
T: AssociatedObjectData,
{
fn find_objects_in_area(&self, area: Aabb) -> Snapshot<'_, T> {
self.interactable.objects_in_area(area)
}
fn find_objects_in_polygon(&self, area: &Polygon) -> Snapshot<'_, T> {
self.interactable.objects_in_polygon(area)
}
fn find_objects_in_ray(&self, origin: Point, direction: Vector) -> Snapshot<'_, T> {
self.interactable.objects_in_ray(origin, direction)
}
fn elapsed_time_in_update(&self) -> Duration {
self.interactable.elapsed_time_in_update()
}
fn own_object(&self) -> Object<'_, T> {
self.interactable
.object(self.id)
.expect("Internal error: Own ID stored in WorldInteractorImpl was invalid")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::world_interactor::InteractableMock;
use myelin_geometry::{Point, PolygonBuilder};
fn object_description() -> ObjectDescription<()> {
ObjectBuilder::default()
.location(10.0, 10.0)
.mobility(Mobility::Immovable)
.shape(
PolygonBuilder::default()
.vertex(-5.0, -5.0)
.vertex(5.0, 5.0)
.vertex(5.0, -5.0)
.build()
.unwrap(),
)
.build()
.unwrap()
}
#[test]
fn find_objects_in_area_is_propagated() {
let object_behavior = ObjectBehaviorMock::new();
let objects = vec![Object {
id: 125,
description: object_description(),
behavior: &object_behavior,
}];
let area = Aabb {
upper_left: Point { x: 10.0, y: 10.0 },
lower_right: Point { x: 20.0, y: 0.0 },
};
let mut interactable = InteractableMock::new();
interactable
.expect_objects_in_area(|arg| arg.partial_eq(area))
.returns(objects.clone());
let world_interactor = WorldInteractorImpl::new(&interactable, 0);
let objects_in_area = world_interactor.find_objects_in_area(area);
assert_eq!(1, objects_in_area.len());
assert_eq!(objects[0].id, objects_in_area[0].id);
assert_eq!(objects[0].description, objects_in_area[0].description);
}
#[test]
fn find_objects_in_polygon_is_propagated() {
let object_behavior = ObjectBehaviorMock::new();
let objects = vec![Object {
id: 125,
description: object_description(),
behavior: &object_behavior,
}];
let area = PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(20.0, 60.0)
.vertex(150.0, 100.0)
.vertex(180.0, 0.0)
.vertex(150.0, -100.0)
.vertex(20.0, -60.0)
.build()
.unwrap();
let mut interactable = InteractableMock::new();
interactable
.expect_objects_in_polygon(|arg| arg.partial_eq_owned(area.clone()))
.returns(objects.clone());
let world_interactor = WorldInteractorImpl::new(&interactable, 0);
let objects_in_area = world_interactor.find_objects_in_polygon(&area);
assert_eq!(1, objects_in_area.len());
assert_eq!(objects[0].id, objects_in_area[0].id);
assert_eq!(objects[0].description, objects_in_area[0].description);
}
#[test]
fn find_objects_in_ray_is_propagated() {
let object_behavior = ObjectBehaviorMock::new();
let objects = vec![Object {
id: 125,
description: object_description(),
behavior: &object_behavior,
}];
let origin = Point { x: 5.0, y: 10.0 };
let direction = Vector { x: 3.0, y: -5.0 };
let mut interactable = InteractableMock::new();
interactable
.expect_objects_in_ray(
|arg| arg.partial_eq(origin),
|arg| arg.partial_eq(direction),
)
.returns(objects.clone());
let world_interactor = WorldInteractorImpl::new(&interactable, 0);
let objects_in_area = world_interactor.find_objects_in_ray(origin, direction);
assert_eq!(1, objects_in_area.len());
assert_eq!(objects[0].id, objects_in_area[0].id);
assert_eq!(objects[0].description, objects_in_area[0].description);
}
#[test]
fn object_is_propagated() {
let object_behavior = ObjectBehaviorMock::new();
let expected_object = Object {
id: 125,
description: object_description(),
behavior: &object_behavior,
};
let mut interactable = InteractableMock::new();
interactable
.expect_object(|arg| arg.partial_eq(expected_object.id))
.returns(Some(expected_object.clone()));
let world_interactor = WorldInteractorImpl::new(&interactable, expected_object.id);
let object = world_interactor.own_object();
assert_eq!(expected_object.id, object.id);
assert_eq!(expected_object.description, object.description);
}
#[test]
#[should_panic]
fn object_panics_on_internal_error() {
let object_behavior = ObjectBehaviorMock::new();
let expected_object = Object {
id: 125,
description: object_description(),
behavior: &object_behavior,
};
let mut interactable = InteractableMock::<()>::new();
interactable
.expect_object(|arg| arg.partial_eq(expected_object.id))
.returns(None);
let world_interactor = WorldInteractorImpl::new(&interactable, expected_object.id);
let _object = world_interactor.own_object();
}
}