aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheDudeFromCI <thedudefromci@gmail.com>2023-08-21 23:46:54 -0700
committerGitHub <noreply@github.com>2023-08-22 01:46:54 -0500
commit47a280212ab2e3b8c1afb34cad30488a2ea1071f (patch)
tree83a8c1fdc3deead30bdb252b2ec41d95d09bb549
parenta81c4c060b9a32d1dccf451158750fac52349acc (diff)
downloadazalea-drasl-47a280212ab2e3b8c1afb34cad30488a2ea1071f.tar.xz
Created nearest_entity system param (#102)
* Created nearest_entity system param Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Added nearby item iterators. Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Export bot.rs (#101) * Removed .vscode settings (#104) Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * raycasting not raytracing * don't panic if TranslatableComponent::to_string fails * Food/saturation component support (#97) * modified for food stuff * moved food/saturation to a separate file * hunger component * simplify some logic --------- Co-authored-by: mat <git@matdoes.dev> * Created nearest_entity system param Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Added nearby item iterators. Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Applied tweaks from PR review Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> * Fixed doctests Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> --------- Signed-off-by: TheDudeFromCI <thedudefromci@gmail.com> Co-authored-by: mat <git@matdoes.dev> Co-authored-by: Luuk van Oijen <lazyluuk.channel@gmail.com>
-rw-r--r--azalea/examples/nearest_entity.rs73
-rw-r--r--azalea/src/lib.rs1
-rw-r--r--azalea/src/nearest_entity.rs189
3 files changed, 263 insertions, 0 deletions
diff --git a/azalea/examples/nearest_entity.rs b/azalea/examples/nearest_entity.rs
new file mode 100644
index 00000000..9ac6331b
--- /dev/null
+++ b/azalea/examples/nearest_entity.rs
@@ -0,0 +1,73 @@
+use azalea::nearest_entity::EntityFinder;
+use azalea::ClientBuilder;
+use azalea::{Bot, LookAtEvent};
+use azalea_client::Account;
+use azalea_entity::metadata::{ItemItem, Player};
+use azalea_entity::{EyeHeight, Local, Position};
+use bevy_app::{FixedUpdate, Plugin};
+use bevy_ecs::{
+ prelude::{Entity, EventWriter},
+ query::With,
+ system::Query,
+};
+
+#[tokio::main]
+async fn main() {
+ let account = Account::offline("bot");
+
+ ClientBuilder::new()
+ .add_plugins(LookAtStuffPlugin)
+ .start(account, "localhost")
+ .await
+ .unwrap();
+}
+
+pub struct LookAtStuffPlugin;
+impl Plugin for LookAtStuffPlugin {
+ fn build(&self, app: &mut bevy_app::App) {
+ app.add_systems(FixedUpdate, (look_at_everything, log_nearby_item_drops));
+ }
+}
+
+fn look_at_everything(
+ bots: Query<Entity, (With<Local>, With<Player>)>,
+ entities: EntityFinder,
+ entity_positions: Query<(&Position, Option<&EyeHeight>)>,
+ mut look_at_event: EventWriter<LookAtEvent>,
+) {
+ for bot_id in bots.iter() {
+ let Some(entity) = entities.nearest_to_entity(bot_id, 16.0) else {
+ continue;
+ };
+
+ let (position, eye_height) = entity_positions.get(entity).unwrap();
+
+ let mut look_target = **position;
+ if let Some(eye_height) = eye_height {
+ look_target.y += **eye_height as f64;
+ }
+
+ look_at_event.send(LookAtEvent {
+ entity: bot_id,
+ position: look_target,
+ });
+ }
+}
+
+fn log_nearby_item_drops(
+ bots: Query<Entity, With<Bot>>,
+ entities: EntityFinder<With<ItemItem>>,
+ item_drops: Query<&ItemItem>,
+) {
+ for bot_id in bots.iter() {
+ for (entity, distance) in entities.nearby_entities_to_entity(bot_id, 8.0) {
+ let item_drop = item_drops.get(entity).unwrap();
+ let kind = item_drop.kind();
+
+ println!(
+ "Bot {:?} can see an {:?} {:.1} meters away.",
+ bot_id, kind, distance
+ );
+ }
+ }
+}
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs
index 9a8dc560..52551ea3 100644
--- a/azalea/src/lib.rs
+++ b/azalea/src/lib.rs
@@ -7,6 +7,7 @@
mod auto_respawn;
mod bot;
pub mod container;
+pub mod nearest_entity;
pub mod pathfinder;
pub mod prelude;
pub mod swarm;
diff --git a/azalea/src/nearest_entity.rs b/azalea/src/nearest_entity.rs
new file mode 100644
index 00000000..b9db6adb
--- /dev/null
+++ b/azalea/src/nearest_entity.rs
@@ -0,0 +1,189 @@
+use azalea_entity::Position;
+use azalea_world::{InstanceName, MinecraftEntityId};
+use bevy_ecs::{
+ prelude::Entity,
+ query::{ReadOnlyWorldQuery, 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::Local;
+/// 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<Local>, 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: ReadOnlyWorldQuery + '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: ReadOnlyWorldQuery + '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)> + '_ {
+ 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)> + '_ {
+ 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
+ }
+ })
+ }
+}