use crate::{
prelude::*,
verb::{Pulling, SetVerb, Verb},
};
mod can_pull;
mod find_in_cone;
mod find_in_trace;
use self::{can_pull::*, find_in_cone::*, find_in_trace::*};
pub(super) fn plugin(app: &mut App) {
app.add_systems(PhysicsSchedule, find_object.in_set(HandleVerbSystem::Pull))
.add_systems(
PhysicsSchedule,
flush_pulling_state.in_set(AvianPickupSystem::ResetIdle),
);
}
fn find_object(
mut commands: Commands,
spatial_query: SpatialQuery,
mut q_actor: Query<
(
Entity,
&GlobalTransform,
&AvianPickupActor,
&mut AvianPickupActorState,
&mut Cooldown,
),
With<Pulling>,
>,
q_collider_parent: Query<&ColliderOf>,
mut q_rigid_body: Query<(
&RigidBody,
&ComputedMass,
Forces,
&GlobalTransform,
Has<HeldProp>,
)>,
q_position: Query<&GlobalTransform>,
) {
for (actor, actor_transform, config, mut state, mut cooldown) in q_actor.iter_mut() {
let actor_transform = actor_transform.compute_transform();
let prop = find_prop_in_trace(
&spatial_query,
actor_transform,
config,
&q_rigid_body.transmute_lens().query(),
&q_collider_parent,
)
.or_else(|| {
find_prop_in_cone(
&spatial_query,
actor_transform,
config,
&q_position,
&q_rigid_body.transmute_lens().query(),
&q_collider_parent,
)
});
let Some(prop) = prop else {
continue;
};
let Ok((_, &mass, mut forces, prop_position, is_already_being_held)) =
q_rigid_body.get_mut(prop.entity)
else {
continue;
};
if is_already_being_held || !can_pull(mass, config) {
continue;
}
let can_hold = prop.toi <= config.hold.distance_to_allow_holding;
if can_hold {
cooldown.hold();
commands
.entity(actor)
.queue(SetVerb::new(Verb::Hold(prop.entity)));
} else {
let direction =
(actor_transform.translation - prop_position.translation()).normalize_or_zero();
let mass_adjustment = adjust_impulse_for_mass(mass);
let pull_impulse = direction * config.pull.impulse * mass_adjustment;
cooldown.pull();
forces.apply_linear_impulse(pull_impulse);
if !matches!(state.as_ref(), AvianPickupActorState::Pulling(..)) {
*state = AvianPickupActorState::Pulling(prop.entity);
}
commands.entity(actor).queue(SetVerb::new(None));
}
}
}
fn adjust_impulse_for_mass(mass: ComputedMass) -> f32 {
if mass.value() < 50.0 {
(mass.value() + 0.5) * (1.0 / 50.0)
} else {
1.0
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
struct Prop {
pub entity: Entity,
pub toi: f32,
}
fn flush_pulling_state(mut q_state: Query<(Mut<AvianPickupActorState>, Has<Pulling>, &Cooldown)>) {
for (mut state, has_pulling, cooldown) in q_state.iter_mut() {
if matches!(state.as_ref(), AvianPickupActorState::Pulling(..))
&& !has_pulling
&& cooldown.is_finished(AvianPickupAction::Pull)
{
*state = AvianPickupActorState::Idle;
}
}
}