diff options
author | Silas Bartha <silas@exvacuum.dev> | 2024-11-21 12:17:08 -0500 |
---|---|---|
committer | Silas Bartha <silas@exvacuum.dev> | 2024-11-21 12:17:08 -0500 |
commit | 57827d53713e9bdf65a0bcc34fe670cfe8b356dd (patch) | |
tree | 478ae6a9c50d3bc6f89696494884b6be7f2918d8 /src | |
parent | 85c80561669bdf7b0f491ee4d7e49f2cc8f7b81c (diff) |
Thu Nov 21 12:17:08 PM EST 2024
Diffstat (limited to 'src')
-rw-r--r-- | src/components.rs | 27 | ||||
-rw-r--r-- | src/lib.rs | 37 |
2 files changed, 52 insertions, 12 deletions
diff --git a/src/components.rs b/src/components.rs index 1be2f2c..c2b2547 100644 --- a/src/components.rs +++ b/src/components.rs @@ -6,7 +6,7 @@ use bevy::{prelude::*, utils::HashSet}; /// /// An entity with an `Interactor` component can be passed to an `InteractorFiredEvent` in order to /// start an interaction with any nearby `Interactable` entities. -#[derive(Component, Default)] +#[derive(Component, Default, Debug)] pub struct Interactor { /// All `Interactable` targets in-range of this interactor. pub targets: HashSet<Entity>, @@ -18,10 +18,19 @@ pub struct Interactor { /// /// An entity with an `Interactable` component might get passed to an `InteractionEvent` when an /// `Interactor` requests an interaction, if the interactable is in range. -#[derive(Component)] +#[derive(Component, Clone, Debug)] pub struct Interactable { + /// An optional name for this interactable + pub name: Option<String>, + /// An optional description of the action + pub description: Option<String>, + /// Predicate to check to see if interaction is possible + pub predicate: Option<fn(Entity, &mut World) -> bool>, pub(crate) exclusive: bool, pub(crate) max_distance_squared: f32, + pub(crate) possible: bool, + /// Whether this pickup is enabled + pub enabled: bool, } impl Interactable { @@ -30,17 +39,27 @@ impl Interactable { /// If exclusive, this interactable will only be interacted with if it's the closest one to the /// interactor, and the interaction will *not* be processed for any other in-range /// interactables. - pub fn new(max_distance: f32, exclusive: bool) -> Self { + pub fn new(max_distance: f32, exclusive: bool, name: Option<String>, description: Option<String>, predicate: Option<fn(Entity, &mut World) -> bool>) -> Self { Self { + name, + description, + predicate, exclusive, max_distance_squared: max_distance * max_distance, + possible: true, + enabled: true, } } + + /// Gets whether this interaction is currently possible. Set this value using predicate. + pub fn possible(&self) -> bool { + self.possible + } } impl Default for Interactable { fn default() -> Self { - Self::new(1.0, false) + Self::new(1.0, false, None, None, None) } } @@ -26,9 +26,12 @@ pub struct InteractionPlugin; impl Plugin for InteractionPlugin { fn build(&self, app: &mut App) { - app.add_systems(Update, (handle_interactor_events, update_interactor_targets)) - .add_event::<InteractorFiredEvent>() - .add_event::<InteractionEvent>(); + app.add_systems( + Update, + (handle_interactor_events, update_interactor_targets, update_interactable_predicates), + ) + .add_event::<InteractorFiredEvent>() + .add_event::<InteractionEvent>(); } } @@ -43,7 +46,7 @@ fn handle_interactor_events( if let Some(interactable_entity) = interactor.closest { let interactable = interactable_query.get(interactable_entity).unwrap(); - if interactable.exclusive { + if interactable.exclusive && interactable.possible { event_writer.send(InteractionEvent { interactor: *interactor_entity, interactable: interactable_entity, @@ -52,7 +55,7 @@ fn handle_interactor_events( } else { for interactable_entity in &interactor.targets { let interactable = interactable_query.get(*interactable_entity).unwrap(); - if !interactable.exclusive { + if !interactable.exclusive && interactable.possible { event_writer.send(InteractionEvent { interactor: *interactor_entity, interactable: *interactable_entity, @@ -73,7 +76,11 @@ fn update_interactor_targets( let interactor_transform = transform_query.get(interactor_entity).unwrap(); let mut closest_active_interactable: Option<(f32, Entity)> = None; + interactor.targets.clear(); for (interactable_entity, interactable) in interactable_query.iter_mut() { + if !interactable.enabled { + continue; + } let interactable_transform = transform_query.get(interactable_entity).unwrap(); let interactable_distance_squared = interactable_transform .translation() @@ -85,7 +92,7 @@ fn update_interactor_targets( ), ); if interactable_distance_squared < interactable.max_distance_squared - && interactable_arccosine < PI / 8.0 + && interactable_arccosine < PI / 4.0 { interactor.targets.insert(interactable_entity); if let Some((arccosine, _)) = closest_active_interactable { @@ -97,8 +104,6 @@ fn update_interactor_targets( closest_active_interactable = Some((interactable_arccosine, interactable_entity)); } - } else { - interactor.targets.remove(&interactable_entity); } } interactor.closest = if let Some((_, interactable_entity)) = closest_active_interactable { @@ -108,3 +113,19 @@ fn update_interactor_targets( } } } + +fn update_interactable_predicates(world: &mut World) { + let mut interactable_query = world.query::<(Entity, &mut Interactable)>(); + let mut interactables = vec![]; + for (interactable_entity, interactable) in interactable_query.iter(world) { + interactables.push((interactable_entity, (*interactable).clone())); + } + for (interactable_entity, interactable) in interactables.iter_mut() { + if let Some(predicate) = &interactable.predicate { + interactable.possible = predicate(*interactable_entity, world); + } + } + for ((_, mut interactable), (_, temp)) in interactable_query.iter_mut(world).zip(interactables.into_iter()) { + *interactable = temp; + } +} |