use crate::engine_v2::coords::Coords;
use crate::engine_v2::engine::Engine;
use crate::engine_v2::entity::object::ObjectRef;
use crate::engine_v2::size::Size;
use std::rc::Rc;
#[derive(Debug, Clone, Copy)]
pub struct Collider {
offset: Coords,
size: Size,
is_active: bool,
}
impl Collider {
pub fn new(offset: Coords, size: Size, is_active: bool) -> Self {
Self {
offset,
size,
is_active,
}
}
pub fn is_active(&self) -> bool {
self.is_active
}
pub fn set_active(&mut self, enabled: bool) {
self.is_active = enabled;
}
pub fn size(&self) -> Size {
self.size
}
pub fn is_null(&self) -> bool {
self.size.height() == 0 && self.size.width() == 0
}
pub fn min(&self, object_pos: Coords) -> Coords {
object_pos + self.offset
}
pub fn max(&self, object_pos: Coords) -> Coords {
object_pos
+ self.offset
+ Coords::new(self.size.width() as i32, self.size.height() as i32, 0)
}
}
#[derive(Clone)]
pub enum ScreenEdge {
Bottom,
Top,
Left,
Right,
TopWithObjectTopSide,
TopWithObjectBottomSide,
LeftWithObjectLeftSide,
LeftWithObjectRightSide,
BottomWithObjectBottomSide,
BottomWithObjectTopSide,
RightWithObjectRightSide,
RightWithObjectLeftSide,
}
type SpriteCollisionCallback = Box<dyn FnMut(&ObjectRef, &ObjectRef, usize, &mut Engine)>;
type EdgeCollisionCallback = Box<dyn FnMut(&ObjectRef, usize, &mut Engine)>;
type PointCollisionCallback = Box<dyn FnMut(&ObjectRef, &Coords, usize, &mut Engine)>;
type LineCollisionCallback = Box<dyn FnMut(&ObjectRef, i32, &LineOrientation, usize, &mut Engine)>;
#[derive(Debug)]
pub enum LineOrientation {
Horizontal,
Vertical,
}
pub enum Collision {
Object {
a: ObjectRef,
b: ObjectRef,
counter: usize,
callback: SpriteCollisionCallback,
},
Edge {
a: ObjectRef,
b: ScreenEdge,
counter: usize,
callback: EdgeCollisionCallback,
},
Point {
a: ObjectRef,
c: Coords,
counter: usize,
callback: PointCollisionCallback,
},
Line {
a: ObjectRef,
c: i32,
o: LineOrientation,
counter: usize,
callback: LineCollisionCallback,
},
}
impl Collision {
pub fn new_object(
a: ObjectRef,
b: ObjectRef,
callback: impl FnMut(&ObjectRef, &ObjectRef, usize, &mut Engine) + 'static,
) -> Self {
Collision::Object {
a,
b,
counter: 0,
callback: Box::new(callback),
}
}
pub fn new_edge(
a: ObjectRef,
b: ScreenEdge,
callback: impl FnMut(&ObjectRef, usize, &mut Engine) + 'static,
) -> Self {
Collision::Edge {
a,
b,
counter: 0,
callback: Box::new(callback),
}
}
pub fn new_point(
a: ObjectRef,
c: Coords,
callback: impl FnMut(&ObjectRef, &Coords, usize, &mut Engine) + 'static,
) -> Self {
Collision::Point {
a,
c,
counter: 0,
callback: Box::new(callback),
}
}
pub fn new_line(
a: ObjectRef,
c: i32,
o: LineOrientation,
callback: impl FnMut(&ObjectRef, i32, &LineOrientation, usize, &mut Engine) + 'static,
) -> Self {
Collision::Line {
a,
c,
o,
counter: 0,
callback: Box::new(callback),
}
}
pub fn counter(&self) -> usize {
match self {
Collision::Object { counter, .. } => *counter,
Collision::Edge { counter, .. } => *counter,
Collision::Point { counter, .. } => *counter,
Collision::Line { counter, .. } => *counter,
}
}
pub fn is_colliding(&self, terminal_size: Size) -> bool {
match self {
Collision::Object { a, b, .. } => {
if Rc::ptr_eq(a, b) {
panic!("Same object twice used in the collision handler");
}
if !(a.borrow_mut().collider().is_active() && b.borrow_mut().collider().is_active())
{
return false;
}
let (a_coords, a_collider) = {
let a = a.borrow_mut();
(a.coords(), *a.collider())
};
let (b_coords, b_collider) = {
let b = b.borrow_mut();
(b.coords(), *b.collider())
};
let a_min = a_collider.min(a_coords);
let b_min = b_collider.min(b_coords);
let a_max = a_collider.max(a_coords);
let b_max = b_collider.max(b_coords);
a_min.x() <= b_max.x()
&& a_max.x() > b_min.x()
&& a_min.y() < b_max.y()
&& a_max.y() > b_min.y()
}
Collision::Edge { a, b, .. } => {
if !a.borrow_mut().collider().is_active() {
return false;
}
let (a_coords, a_collider) = {
let a = a.borrow_mut();
(a.coords(), *a.collider())
};
let a_min = a_collider.min(a_coords);
let a_max = a_collider.max(a_coords);
let terminal_height = terminal_size.height() as i32;
let terminal_width = terminal_size.width() as i32;
match b {
ScreenEdge::Bottom => a_min.y() <= 0 && a_max.y() >= 0,
ScreenEdge::BottomWithObjectBottomSide => a_min.y() == 0,
ScreenEdge::BottomWithObjectTopSide => a_max.y() == 0,
ScreenEdge::Left => a_min.x() <= 0 && a_max.x() >= 0,
ScreenEdge::LeftWithObjectLeftSide => a_min.x() == 0,
ScreenEdge::LeftWithObjectRightSide => a_max.x() == 0,
ScreenEdge::Top => a_min.y() <= terminal_height && a_max.y() >= terminal_height,
ScreenEdge::TopWithObjectTopSide => a_max.y() == terminal_height,
ScreenEdge::TopWithObjectBottomSide => a_min.y() == terminal_height,
ScreenEdge::Right => a_min.x() <= terminal_width && a_max.x() >= terminal_width,
ScreenEdge::RightWithObjectRightSide => a_max.x() == terminal_width,
ScreenEdge::RightWithObjectLeftSide => a_min.x() == terminal_width,
}
}
Collision::Point { a, c, .. } => {
if !a.borrow_mut().collider().is_active() {
return false;
}
let (a_coords, a_collider) = {
let a = a.borrow_mut();
(a.coords(), *a.collider())
};
let a_min = a_collider.min(a_coords);
let a_max = a_collider.max(a_coords);
c.x() >= a_min.x() && c.x() <= a_max.x() && c.y() >= a_min.y() && c.y() <= a_max.y()
}
Collision::Line { a, c, o, .. } => {
if !a.borrow_mut().collider().is_active() {
return false;
}
let (a_coords, a_collider) = {
let a = a.borrow_mut();
(a.coords(), *a.collider())
};
let a_min = a_collider.min(a_coords);
let a_max = a_collider.max(a_coords);
let toto = match o {
LineOrientation::Horizontal => a_min.y() <= *c && a_max.y() >= *c,
LineOrientation::Vertical => a_min.x() <= *c && a_max.x() >= *c,
};
if false {
panic!(
"GGGGG min {:#?} - max {:#?} - {}- {:#?}",
a_min, a_max, c, o
);
}
toto
}
}
}
pub fn trigger(&mut self, engine: &mut Engine) {
match self {
Collision::Object {
a,
b,
counter,
callback,
} => {
*counter += 1;
(callback)(a, b, *counter, engine);
}
Collision::Edge {
a,
counter,
callback,
..
} => {
*counter += 1;
(callback)(a, *counter, engine);
}
Collision::Point {
a,
c,
counter,
callback,
..
} => {
*counter += 1;
(callback)(a, c, *counter, engine);
}
Collision::Line {
a,
c,
o,
counter,
callback,
..
} => {
*counter += 1;
(callback)(a, *c, o, *counter, engine);
}
}
}
}