diff options
| author | TheDudeFromCI <thedudefromci@gmail.com> | 2023-08-21 23:46:54 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-22 01:46:54 -0500 |
| commit | 47a280212ab2e3b8c1afb34cad30488a2ea1071f (patch) | |
| tree | 83a8c1fdc3deead30bdb252b2ec41d95d09bb549 | |
| parent | a81c4c060b9a32d1dccf451158750fac52349acc (diff) | |
| download | azalea-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.rs | 73 | ||||
| -rw-r--r-- | azalea/src/lib.rs | 1 | ||||
| -rw-r--r-- | azalea/src/nearest_entity.rs | 189 |
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 + } + }) + } +} |
