mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
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>
This commit is contained in:
parent
a81c4c060b
commit
47a280212a
3 changed files with 263 additions and 0 deletions
73
azalea/examples/nearest_entity.rs
Normal file
73
azalea/examples/nearest_entity.rs
Normal file
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
189
azalea/src/nearest_entity.rs
Normal file
189
azalea/src/nearest_entity.rs
Normal file
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue