use std::collections::{HashMap, HashSet};
use hecs::{Component, Entity};
use crate::core::components::{color::Color, material::Material, maths::{
collider::{Collider, ColliderDebug, ColliderMask, Collision},
hierarchy::Parent,
transform::Transform,
}, shapes::polygon::Polygon};
use crate::core::resources::global_storage::GlobalStorage;
use crate::core::resources::inputs::types::{Input, KeyCode};
use crate::core::world::{GameData, World};
use crate::rendering::Renderable2D;
pub(crate) fn collider_cleaner_system(data: &mut GameData) {
for (_, c) in data.query_mut::<&mut Collider>() {
c.clear_collisions();
}
}
pub(crate) fn compute_collisions_system(data: &mut GameData) {
let mut res: HashMap<Entity, Vec<Collision>> = HashMap::default();
{
let colliders: Vec<(Entity, Transform, Collider)> = {
let mut res = Vec::new();
for (e, (t, c)) in data.query::<(&Transform, &Collider)>().iter() {
res.push((e, *t, c.clone()));
}
res
};
let mut colliders_by_mask: HashMap<
ColliderMask,
Vec<(Entity, &Transform, &Collider)>,
> = HashMap::default();
colliders.iter().for_each(|(e, t, c)| {
colliders_by_mask.entry(c.mask().clone()).or_default().push((*e, t, c))
});
let mut cpt = 0;
colliders.iter().for_each(|(entity, transform, collider)| {
collider.filters().iter().filter(|mask| colliders_by_mask.contains_key(mask)).for_each(
|mask| {
colliders_by_mask
.get(mask)
.expect("Impossible to find a collider's entry")
.iter()
.map(|(_e, t, c)| {
cpt += 1;
(_e, t, c, collider.collides_with(transform, c, t))
})
.filter(|(_e, _t, _c, collision_area)| collision_area.is_some())
.for_each(|(_e, t, c, collision_area)| {
res.entry(*entity).or_default().push(Collision {
mask: c.mask().clone(),
entity: *entity,
coordinates: *t.global_translation(),
collision_area: collision_area.expect("Filtered Option is still KO"),
});
})
},
);
});
}
res.drain().for_each(|(e, mut collisions)| {
data.entry_mut::<&mut Collider>(e)
.expect("Collisions on unreachable collider")
.add_collisions(&mut collisions);
});
}
pub(crate) fn debug_colliders_system(data: &mut GameData) {
let global_debug_activated = handle_global_debug_colliders(data);
let mut collider_debug = fetch_collider_debug_entities(data);
let mut debug_lines_to_add = Vec::new();
let mut debug_lines_to_remove = Vec::new();
for (entity, (_, collider))
in data.query_mut::<(&Transform, &mut Collider)>() {
if (collider.debug_lines() || global_debug_activated) && !collider_debug.0.contains(&entity) {
let color = match collider.mask() {
ColliderMask::None => Color::new_rgb(255, 255, 255),
ColliderMask::Character => Color::new_rgb(255, 0, 0),
ColliderMask::Bullet => Color::new_rgb(255, 0, 0),
ColliderMask::Death => Color::new_rgb(255, 0, 0),
ColliderMask::Landscape => Color::new_rgb(255, 255, 0),
ColliderMask::Custom(_) => Color::new_rgb(0, 0, 255),
ColliderMask::Item => Color::new_rgb(0, 255, 255),
};
let offset = collider.offset();
let polygon_collider =
Polygon::new(collider.collider_coordinates(0.,0.)).pivot(collider.get_pivot());
debug_lines_to_add.push((
Parent(entity),
ColliderDebug,
Transform::from_xyz(offset.x(), offset.y(), 99),
polygon_collider,
Material::Color(color),
));
} else if !collider.debug_lines() && !global_debug_activated && collider_debug.0.contains(&entity) {
debug_lines_to_remove.push(entity);
}
}
debug_lines_to_add.drain(0..).for_each(|components| {
data.push(components);
});
debug_lines_to_remove.drain(0..).for_each(|e| {
let _r = data.remove(collider_debug.1.remove(&e).expect(""));
});
}
pub(crate) fn collider_pivot_propagation_system<T: Component + Renderable2D>(data: &mut GameData) {
for (_, (renderable, collider)) in data.query_mut::<(&T, &mut Collider)>(){
collider.set_parent_pivot(renderable.get_pivot());
}
}
fn handle_global_debug_colliders(game_data: &mut GameData) -> bool {
let shortcut_event = game_data.inputs().shortcut_pressed_event(&vec![Input::Key(KeyCode::LShift), Input::Key(KeyCode::D)]);
let mut resources = game_data.get_resource_mut::<GlobalStorage>().expect("Missing Global Storage resource");
let mut current_val = **resources.flags.get("debug_colliders").get_or_insert(&false);
if shortcut_event {
current_val = !current_val;
resources.flags.insert("debug_colliders".to_string(), current_val);
}
current_val
}
fn fetch_collider_debug_entities(data: &mut GameData) -> (HashSet<Entity>, HashMap<Entity, Entity>) {
let mut parents = HashSet::new();
let mut debug_line: HashMap<Entity, Entity> = HashMap::new();
for (e, (_, parent)) in data.query::<(&ColliderDebug, &Parent)>().iter() {
parents.insert(parent.0);
debug_line.insert(parent.0, e);
}
(parents, debug_line)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::components::maths::{
collider::{Collider, ColliderMask, ColliderType, Collision},
transform::Transform,
};
use crate::core::components::maths::collider::CollisionArea;
use crate::core::resources::inputs::inputs_controller::InputsController;
use crate::core::world::GameData;
#[test]
fn clear_collision_system_test() {
let mut world = GameData::default();
let mut t = Transform::default();
t.append_x(1.0);
let e = world.push((
1,
t,
Collider::new(ColliderMask::Bullet, vec![], ColliderType::Square(5)),
));
let entry = world.entry_mut::<&mut Collider>(e).unwrap();
entry.add_collisions(&mut vec![Collision {
mask: ColliderMask::Character,
entity: e,
coordinates: Default::default(),
collision_area: CollisionArea { coordinates: vec![]},
}]);
assert_eq!(1, entry.collisions().len());
collider_cleaner_system(&mut world);
assert_eq!(0, world.entry::<&Collider>(e).unwrap().get().unwrap().collisions().len());
}
#[test]
fn compute_collision_system_test() {
let mut world = GameData::default();
let mut t = Transform::default();
t.append_x(1.0);
let mut t2 = Transform::default();
t2.append_x(2.0);
let e = world.push((
1,
t,
Collider::new(ColliderMask::Bullet, vec![], ColliderType::Square(5)),
));
let e2 = world.push((
2,
t2,
Collider::new(
ColliderMask::Landscape,
vec![ColliderMask::Bullet],
ColliderType::Square(5),
),
));
compute_collisions_system(&mut world);
assert_eq!(0, world.entry::<&Collider>(e).unwrap().get().unwrap().collisions().len());
assert_eq!(1, world.entry::<&Collider>(e2).unwrap().get().unwrap().collisions().len());
}
#[test]
fn debug_colliders_system_test() {
let mut world = GameData::default();
world.insert_resource(InputsController::default());
world.insert_resource(GlobalStorage::default());
let _collider = world.push((
Transform::default(),
Collider::new(ColliderMask::None, vec![], ColliderType::Square(100)).with_debug_lines(),
));
debug_colliders_system(&mut world);
let res = world.query::<(&ColliderDebug, &Parent)>().iter().count();
assert_eq!(1, res);
}
}