diff --git a/src/pull_object.rs b/src/pull_object.rs index 3af016e..d06b691 100644 --- a/src/pull_object.rs +++ b/src/pull_object.rs @@ -1,10 +1,13 @@ use crate::prelude::*; mod candidate; +mod find_in_cone; + +use self::{candidate::*, find_in_cone::*}; pub(super) fn plugin(app: &mut App) { app.add_event::() - .observe(on_pull_object.pipe(pull_object)); + .observe(on_pull_object.pipe(find_object)); } #[derive(Debug, Event)] @@ -14,8 +17,8 @@ fn on_pull_object(trigger: Trigger) -> Entity { trigger.entity() } -/// Inspired by [`CWeaponPhysCannon::FindObjectInCone`](https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/game/server/hl2/weapon_physcannon.cpp#L2690) -fn pull_object( +/// Inspired by [`CWeaponPhysCannon::FindObject`](https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/hl2/weapon_physcannon.cpp#L2497) +fn find_object( In(actor_entity): In, spatial_query: SpatialQuery, q_actor: Query<(&GlobalTransform, &AvianPickupActor)>, @@ -25,60 +28,33 @@ fn pull_object( let (origin, config) = q_actor.get(actor_entity).unwrap(); let origin = origin.compute_transform(); + let candidate = get_object_candidate(&spatial_query, origin, &config); - // TODO: [`CWeaponPhysCannon::FindObject`](https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/hl2/weapon_physcannon.cpp#L2497) - let candidate = candidate::get_object_candidate(&spatial_query, origin, &config); - - let mut nearest_dist = config.trace_length + 1.0; - let box_collider = Cuboid::from_size(Vec3::splat(2.0 * nearest_dist)).into(); - - let colliders = spatial_query.shape_intersections( - &box_collider, - origin.translation, - origin.rotation, - config.spatial_query_filter.clone(), - ); - let mut nearest_entity = None; - - for collider in colliders { - let rigid_body_entity = q_collider - .get(collider) - .expect("`shape_intersections` returned something without a `Collider`") - .get(); - let (&rigid_body, object_transform) = q_rigid_body - .get(rigid_body_entity) - .expect("Failed to get `RigidBody` for entity"); - if rigid_body != RigidBody::Dynamic { - continue; - } - let object_translation = object_transform.translation(); - - // Closer than other objects - let los = object_translation - origin.translation; - let (los, dist) = Dir3::new_and_length(los).expect("Failed to normalize line of sight"); - if dist >= nearest_dist { - continue; - } - - // Cull to the cone - let max_dot = config.cone; - if los.dot(origin.forward().into()) <= max_dot { - continue; - } - - // Make sure it isn't occluded! - if let Some(hit) = spatial_query.cast_ray( - origin.translation, - los, - dist, - true, - config.spatial_query_filter.clone(), - ) { - if hit.entity == rigid_body_entity { - nearest_dist = dist; - nearest_entity.replace(rigid_body_entity); - } + let reaction = if let Some(candidate) = candidate { + if candidate.toi_fraction < 0.25 { + ObjectReaction::Hold + } else { + ObjectReaction::Pull } + } else { + ObjectReaction::None + }; + + if reaction == ObjectReaction::None || true { + let object = find_object_in_cone( + In(actor_entity), + spatial_query, + q_actor, + q_collider, + q_rigid_body, + ); + info!("Found object: {object:?}"); } - info!("Nearest entity: {:?}", nearest_entity) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +enum ObjectReaction { + None, + Pull, + Hold, } diff --git a/src/pull_object/find_in_cone.rs b/src/pull_object/find_in_cone.rs new file mode 100644 index 0000000..1d71696 --- /dev/null +++ b/src/pull_object/find_in_cone.rs @@ -0,0 +1,67 @@ +use crate::prelude::*; + +/// Inspired by [`CWeaponPhysCannon::FindObjectInCone`](https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/game/server/hl2/weapon_physcannon.cpp#L2690) +pub(super) fn find_object_in_cone( + In(actor_entity): In, + spatial_query: SpatialQuery, + q_actor: Query<(&GlobalTransform, &AvianPickupActor)>, + q_collider: Query<&ColliderParent>, + q_rigid_body: Query<(&RigidBody, &GlobalTransform)>, +) -> Option { + let (origin, config) = q_actor.get(actor_entity).unwrap(); + + let origin = origin.compute_transform(); + + let mut nearest_dist = config.trace_length + 1.0; + let box_collider = Cuboid::from_size(Vec3::splat(2.0 * nearest_dist)).into(); + + let colliders = spatial_query.shape_intersections( + &box_collider, + origin.translation, + origin.rotation, + config.spatial_query_filter.clone(), + ); + let mut nearest_entity = None; + + for collider in colliders { + let rigid_body_entity = q_collider + .get(collider) + .expect("`shape_intersections` returned something without a `Collider`") + .get(); + let (&rigid_body, object_transform) = q_rigid_body + .get(rigid_body_entity) + .expect("Failed to get `RigidBody` for entity"); + if rigid_body != RigidBody::Dynamic { + continue; + } + let object_translation = object_transform.translation(); + + // Closer than other objects + let los = object_translation - origin.translation; + let (los, dist) = Dir3::new_and_length(los).expect("Failed to normalize line of sight"); + if dist >= nearest_dist { + continue; + } + + // Cull to the cone + let max_dot = config.cone; + if los.dot(origin.forward().into()) <= max_dot { + continue; + } + + // Make sure it isn't occluded! + if let Some(hit) = spatial_query.cast_ray( + origin.translation, + los, + dist, + true, + config.spatial_query_filter.clone(), + ) { + if hit.entity == rigid_body_entity { + nearest_dist = dist; + nearest_entity.replace(rigid_body_entity); + } + } + } + nearest_entity +}