azalea/nearest_entity.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
use azalea_entity::Position;
use azalea_world::{InstanceName, MinecraftEntityId};
use bevy_ecs::{
prelude::Entity,
query::{QueryFilter, With},
system::{Query, SystemParam},
};
/// This system parameter can be used as a shorthand for quickly finding an
/// entity, (or several) close to a given position.
///
/// This system parameter allows for additional filtering of entities based off
/// of ECS marker components, such as `With<>`, `Without<>`, or `Added<>`, etc.
/// All functions used by this system parameter instance will respect the
/// applied filter.
///
/// ```
/// use azalea::chat::SendChatEvent;
/// use azalea::nearest_entity::EntityFinder;
/// use azalea_entity::metadata::{Player, AbstractMonster};
/// use azalea_entity::LocalEntity;
/// use bevy_ecs::system::Query;
/// use bevy_ecs::prelude::{Entity, EventWriter};
/// use bevy_ecs::query::With;
///
/// /// All bots near aggressive mobs will scream in chat.
/// pub fn bots_near_aggressive_mobs(
/// bots: Query<Entity, (With<LocalEntity>, With<Player>)>,
/// entity_finder: EntityFinder<With<AbstractMonster>>,
/// mut chat_events: EventWriter<SendChatEvent>,
/// ) {
/// for bot_id in bots.iter() {
/// let Some(nearest) = entity_finder.nearest_to_entity(bot_id, 16.0) else {
/// continue;
/// };
///
/// chat_events.send(SendChatEvent {
/// entity: bot_id,
/// content: String::from("Ahhh!"),
/// });
/// }
/// }
/// ```
#[derive(SystemParam)]
pub struct EntityFinder<'w, 's, F = ()>
where
F: QueryFilter + 'static,
{
all_entities:
Query<'w, 's, (&'static Position, &'static InstanceName), With<MinecraftEntityId>>,
filtered_entities: Query<
'w,
's,
(Entity, &'static InstanceName, &'static Position),
(With<MinecraftEntityId>, F),
>,
}
impl<'w, 's, 'a, F> EntityFinder<'w, 's, F>
where
F: QueryFilter + 'static,
{
/// Gets the nearest entity to the given position and world instance name.
/// This method will return `None` if there are no entities within range. If
/// multiple entities are within range, only the closest one is returned.
pub fn nearest_to_position(
&'a self,
position: &Position,
instance_name: &InstanceName,
max_distance: f64,
) -> Option<Entity> {
let mut nearest_entity = None;
let mut min_distance = max_distance;
for (target_entity, e_instance, e_pos) in self.filtered_entities.iter() {
if e_instance != instance_name {
continue;
}
let target_distance = position.distance_to(e_pos);
if target_distance < min_distance {
nearest_entity = Some(target_entity);
min_distance = target_distance;
}
}
nearest_entity
}
/// Gets the nearest entity to the given entity. This method will return
/// `None` if there are no entities within range. If multiple entities are
/// within range, only the closest one is returned.
pub fn nearest_to_entity(&'a self, entity: Entity, max_distance: f64) -> Option<Entity> {
let Ok((position, instance_name)) = self.all_entities.get(entity) else {
return None;
};
let mut nearest_entity = None;
let mut min_distance = max_distance;
for (target_entity, e_instance, e_pos) in self.filtered_entities.iter() {
if entity == target_entity {
continue;
};
if e_instance != instance_name {
continue;
}
let target_distance = position.distance_to(e_pos);
if target_distance < min_distance {
nearest_entity = Some(target_entity);
min_distance = target_distance;
}
}
nearest_entity
}
/// This function get an iterator over all nearby entities to the given
/// position within the given maximum distance. The entities in this
/// iterator are not returned in any specific order.
///
/// This function returns the Entity ID of nearby entities and their
/// distance away.
pub fn nearby_entities_to_position(
&'a self,
position: &'a Position,
instance_name: &'a InstanceName,
max_distance: f64,
) -> impl Iterator<Item = (Entity, f64)> + 'a {
self.filtered_entities
.iter()
.filter_map(move |(target_entity, e_instance, e_pos)| {
if e_instance != instance_name {
return None;
}
let distance = position.distance_to(e_pos);
if distance < max_distance {
Some((target_entity, distance))
} else {
None
}
})
}
/// This function get an iterator over all nearby entities to the given
/// entity within the given maximum distance. The entities in this iterator
/// are not returned in any specific order.
///
/// This function returns the Entity ID of nearby entities and their
/// distance away.
pub fn nearby_entities_to_entity(
&'a self,
entity: Entity,
max_distance: f64,
) -> impl Iterator<Item = (Entity, f64)> + 'a {
let position;
let instance_name;
if let Ok((pos, instance)) = self.all_entities.get(entity) {
position = *pos;
instance_name = Some(instance);
} else {
position = Position::default();
instance_name = None;
};
self.filtered_entities
.iter()
.filter_map(move |(target_entity, e_instance, e_pos)| {
if entity == target_entity {
return None;
}
if Some(e_instance) != instance_name {
return None;
}
let distance = position.distance_to(e_pos);
if distance < max_distance {
Some((target_entity, distance))
} else {
None
}
})
}
}