use std::cell::RefCell;
use std::collections::HashMap;
use std::panic;
use std::rc::Rc;
use crossterm::event::KeyCode;
use uuid::Uuid;
use crate::engine_v2::collision::Collider;
use crate::engine_v2::coords::Coords;
use crate::engine_v2::entity::frame::Frame;
use crate::engine_v2::entity::movement::Movement;
use crate::engine_v2::entity::sprite::Sprite;
use crate::engine_v2::size::Size;
pub type ObjectRef = Rc<RefCell<Object>>;
type ObjectAction = Box<dyn Fn(ObjectRef)>;
pub struct Object {
id: Uuid,
tdid: u64,
tdname: String,
movement: Movement,
sprites: Vec<Sprite>,
active_sprite: usize,
coords: Coords,
coords_history: Vec<Coords>,
visible: bool,
pub input_actions: HashMap<KeyCode, ObjectAction>,
collider: Collider,
}
impl Object {
pub fn new(
tdid: u64,
tdname: String,
sprites: Vec<Sprite>,
collider: Option<Collider>,
) -> ObjectRef {
let id = Uuid::new_v4();
let col: Collider = match collider {
Some(c) => c,
None => {
let size = sprites[0].size();
Collider::new(Coords::new(0, 0, 0), size, true)
}
};
Rc::new(RefCell::new(Self {
id,
tdid,
tdname,
coords: Coords::new(0, 0, 0),
coords_history: vec![],
movement: Movement::new_none(),
sprites,
active_sprite: 0,
visible: true,
input_actions: HashMap::new(),
collider: col,
}))
}
pub fn id(&self) -> Uuid {
self.id
}
pub fn tdid(&self) -> u64 {
self.tdid
}
pub fn tdname(&self) -> String {
self.tdname.clone()
}
pub fn coords(&self) -> Coords {
self.coords
}
pub fn set_coords(&mut self, coords: Coords) {
self.coords = coords;
}
pub fn sprite(&mut self) -> &Sprite {
&self.sprites[self.active_sprite]
}
pub fn activate_sprite(&mut self, tick_id: usize) {
self.sprites[self.active_sprite].set_activate(true);
self.sprites[self.active_sprite].reset(tick_id);
}
pub fn deactivate_sprite(&mut self) {
self.sprites[self.active_sprite].set_activate(false);
}
pub fn set_frame_id(&mut self, frame_id: usize) {
self.sprites[self.active_sprite].set_frame_id(frame_id);
}
pub fn current_frame(&self) -> &Frame {
self.sprites[self.active_sprite].current_frame()
}
pub fn size(&self) -> Size {
let frame = self.current_frame();
Size::new(frame.get_width(), frame.get_height())
}
pub fn update(&mut self, tick_id: usize, terminal_size: Size) {
let sprite = &mut self.sprites[self.active_sprite];
if self.movement.is_active() {
self.movement.advance(tick_id, terminal_size, sprite.size());
}
self.coords_history.push(self.coords);
let tdid = self.tdid; let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
self.movement.get_coordinate(tick_id)
}));
self.coords = match result {
Ok(coords) => coords,
Err(_) => {
panic!("Can not find coords for tdid: {}", tdid);
}
};
let mut moved_x = true;
let mut moved_y = true;
if !self.coords_history.is_empty() {
moved_x = self.coords.x() != self.coords_history[self.coords_history.len() - 1].x();
moved_y = self.coords.y() != self.coords_history[self.coords_history.len() - 1].y();
}
if sprite.is_active() {
sprite.advance(
tick_id,
moved_x,
moved_y,
self.movement.is_active(),
self.movement.is_done(),
);
}
}
pub fn compute_predefined_path(&mut self, terminal_size: Size) {
self.movement
.compute_predefined_path(terminal_size, self.size());
}
pub fn predefined_path(&self) -> Vec<Coords> {
self.movement.predefined_path()
}
pub fn set_movement(&mut self, movement: Movement) {
self.movement = movement;
}
pub fn movement(&mut self) -> &mut Movement {
&mut self.movement
}
pub fn is_movement_active(&self) -> bool {
self.movement.is_active()
}
pub fn deactivate_movement(&mut self) {
self.movement.deactivate();
}
pub fn activate_movement(&mut self) {
self.movement.activate();
}
pub fn reset_animation(&mut self, start_tick_id: usize) {
self.sprites[0].reset(start_tick_id);
}
pub fn collider(&self) -> &Collider {
&self.collider
}
pub fn visible(&self) -> bool {
self.visible
}
pub fn set_visible(&mut self, value: bool) {
self.visible = value;
}
pub fn on_key(object: &ObjectRef, key: KeyCode, action: impl Fn(ObjectRef) + 'static) {
object
.borrow_mut()
.input_actions
.insert(key, Box::new(action));
}
}