use anyhow::Result; use engine::prelude::*; use cgmath::Vector2; pub enum CollisionEventType { Block, Ignore, } pub type HitCallback = dyn FnMut(&mut World, Entity, Entity) -> Result + Send + Sync; // HitBox is basically a cylinder pub struct HitBox { pub radius: f32, height: f32, hit_event: Option>, } impl HitBox { pub fn from_bb(bounding_box: &BoundingBox) -> Self { Self { radius: Self::calculate_radius(bounding_box), height: bounding_box.max[2] - bounding_box.min[2], hit_event: None, } } pub fn new(radius: f32, height: f32) -> Self { Self { radius, height, hit_event: None, } } pub fn forced(bounding_box: &BoundingBox, radius: f32, height: f32) -> Self { Self { radius: if radius == 0.0 { Self::calculate_radius(bounding_box) } else { radius }, height: if height == 0.0 { bounding_box.max[2] - bounding_box.min[2] } else { height }, hit_event: None, } } pub fn calculate_radius(bounding_box: &BoundingBox) -> f32 { // seek the furthest point let mut radius = 0.0; let mut tmp = bounding_box.min[0].abs(); if tmp > radius { radius = tmp; } tmp = bounding_box.min[1].abs(); if tmp > radius { radius = tmp; } tmp = bounding_box.max[0].abs(); if tmp > radius { radius = tmp; } tmp = bounding_box.max[0].abs(); if tmp > radius { radius = tmp; } radius } pub fn set_event(&mut self, f: F) where F: FnMut(&mut World, Entity, Entity) -> Result + Send + Sync + 'static, { self.hit_event = Some(Box::new(f)); } pub fn set_radius(&mut self, radius: f32) { self.radius = radius; } pub fn set_height(&mut self, height: f32) { self.height = height; } pub fn height(&self) -> f32 { self.height } pub fn radius(&self) -> f32 { self.radius } pub fn check_collision( &self, my_pos: Vector2, other_radius: f32, other_pos: Vector2, ) -> bool { let d = other_pos - my_pos; let radii = other_radius + self.radius; ((d.x * d.x) + (d.y * d.y)) <= (radii * radii) } pub fn collision( &mut self, world: &mut World, me: Entity, collider: Entity, ) -> Result { match &mut self.hit_event { Some(hit_event) => hit_event(world, me, collider), None => Ok(CollisionEventType::Ignore), } } } impl EntityComponent for HitBox { fn name(&self) -> &str { Self::debug_name() } } impl ComponentDebug for HitBox { fn debug_name() -> &'static str { "HitBox" } } impl Clone for HitBox { fn clone(&self) -> Self { Self { radius: self.radius.clone(), height: self.height.clone(), hit_event: None, } } }