azalea_physics/collision/
entity_collisions.rs

1use azalea_core::aabb::AABB;
2use azalea_entity::{
3    LocalEntity, Physics,
4    metadata::{AbstractBoat, Shulker},
5};
6use azalea_world::Instance;
7use bevy_ecs::{
8    entity::Entity,
9    query::{Or, With, Without},
10    system::Query,
11};
12use tracing::error;
13
14use super::VoxelShape;
15
16/// This query matches on entities that we can collide with. That is, boats and
17/// shulkers.
18///
19/// If you want to use this in a more complex query, use
20/// [`CollidableEntityFilter`] as a filter instead.
21pub type CollidableEntityQuery<'world, 'state> = Query<'world, 'state, (), CollidableEntityFilter>;
22/// This filter matches on entities that we can collide with (boats and
23/// shulkers).
24///
25/// Use [`CollidableEntityQuery`] if you want an empty query that matches with
26/// this.
27pub type CollidableEntityFilter = Or<(With<AbstractBoat>, With<Shulker>)>;
28
29pub type PhysicsQuery<'world, 'state, 'a> =
30    Query<'world, 'state, &'a Physics, Without<LocalEntity>>;
31
32pub fn get_entity_collisions(
33    world: &Instance,
34    aabb: &AABB,
35    source_entity: Option<Entity>,
36    physics_query: &PhysicsQuery,
37    collidable_entity_query: &CollidableEntityQuery,
38) -> Vec<VoxelShape> {
39    if aabb.size() < 1.0E-7 {
40        return vec![];
41    }
42
43    let collision_predicate = |entity| collidable_entity_query.get(entity).is_ok();
44
45    let collidable_entities = get_entities(
46        world,
47        source_entity,
48        &aabb.inflate_all(1.0E-7),
49        &collision_predicate,
50        physics_query,
51    );
52
53    collidable_entities
54        .into_iter()
55        .map(|(_entity, aabb)| VoxelShape::from(aabb))
56        .collect()
57}
58
59/// Return all entities that are colliding with the given bounding box and match
60/// the given predicate.
61///
62/// `source_entity` is the entity that the bounding box belongs to, and won't be
63/// one of the returned entities.
64pub fn get_entities(
65    world: &Instance,
66    source_entity: Option<Entity>,
67    aabb: &AABB,
68    predicate: &dyn Fn(Entity) -> bool,
69    physics_query: &PhysicsQuery,
70) -> Vec<(Entity, AABB)> {
71    let mut matches = Vec::new();
72
73    super::world_collisions::for_entities_in_chunks_colliding_with(
74        world,
75        aabb,
76        |_chunk_pos, entities_in_chunk| {
77            // now check if the entity itself collides
78            for &candidate in entities_in_chunk {
79                if Some(candidate) != source_entity && predicate(candidate) {
80                    let Ok(physics) = physics_query.get(candidate) else {
81                        error!(
82                            "Entity {candidate} (found from for_entities_in_chunks_colliding_with) is missing required components."
83                        );
84                        continue;
85                    };
86
87                    let candidate_aabb = physics.bounding_box;
88                    if aabb.intersects_aabb(&candidate_aabb) {
89                        matches.push((candidate, physics.bounding_box));
90                    }
91                }
92            }
93        },
94    );
95
96    matches
97}