use glam::{IVec2, Vec2}; use rapier2d::{ dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle}, geometry::{Collider, ColliderHandle}, }; use crate::{ config::{CELLS_TO_METRES, MASS_SCALING}, content::materials::MaterialId, sim::{ cell::cell::Cell, lib::marching_squares::Marchable, rb_manager::RbManager, sim_manager::SimCtx, }, }; pub struct EntityCells { pub size: IVec2, pub cells: Vec, } impl EntityCells { #[inline] pub fn get_cell_at_local_position(&self, pos: IVec2) -> Cell { self.cells[pos.x as usize + pos.y as usize * self.size.x as usize] } #[inline] pub fn set_cell_at_local_position(&mut self, pos: IVec2, cell: Cell) { self.cells[pos.x as usize + pos.y as usize * self.size.x as usize] = cell; } } impl Marchable for EntityCells { fn occupied(&self, pos: IVec2) -> bool { if pos.x < 0 || pos.x >= self.size.x || pos.y < 0 || pos.y >= self.size.y { false } else { self.get_cell_at_local_position(pos).material != MaterialId::Void } } fn size(&self) -> IVec2 { self.size } } pub trait EntityBehaviour { fn update( &mut self, _update_ctx: &mut EntityUpdateCtx, _ctx: &mut SimCtx, _delta_time: f32, ) -> () { } fn physics_update( &mut self, _update_ctx: &mut EntityUpdateCtx, _ctx: &mut SimCtx, _delta_time: f32, ) -> () { } } pub struct EntityDef { pub rb: Option, pub collider: Option, pub cells: Option, pub behaviour: Option>, } impl EntityDef { pub fn from_cells( position: Vec2, cells: EntityCells, behaviour: Option>, ) -> Self { let rb = RigidBodyBuilder::dynamic() .translation(position / CELLS_TO_METRES) .build(); let mass = cells .cells .iter() .fold(0, |acc, cur| acc + cur.material.def().density) as f32 * MASS_SCALING; let collider = RbManager::convex_hull_collider_from_marchable(&cells, None) .mass(mass) .build(); EntityDef { rb: Some(rb), collider: Some(collider), cells: Some(cells), behaviour, } } } pub struct EntityData { pub id: u32, pub rb_h: Option, pub collider_h: Option, pub cells: Option, } pub struct EntityUpdateResult { pub deferred_destructions: Vec, } pub struct EntityUpdateCtx<'a> { pub entity_data: &'a mut EntityData, deferred_destructions: Vec, } impl<'a> EntityUpdateCtx<'a> { pub fn from_entity_data(entity_data: &'a mut EntityData) -> Self { EntityUpdateCtx { entity_data, deferred_destructions: Vec::new(), } } pub fn deferred_destroy(&mut self, entity_id: u32) { self.deferred_destructions.push(entity_id); } pub fn to_result(self) -> EntityUpdateResult { EntityUpdateResult { deferred_destructions: self.deferred_destructions, } } } impl EntityData { pub fn transform(&self, ctx: &SimCtx) -> Option<(Vec2, (f32, f32))> { self.rb_h .map(|rb_h| { ctx.rb_manager .physics_manager .rigid_body_set .get(rb_h) .map(|rb| { ( rb.translation() * CELLS_TO_METRES, (rb.rotation().cos(), rb.rotation().sin()), ) }) }) .flatten() } } pub struct Entity { pub data: EntityData, behaviour: Option>, } impl Entity { pub fn update(&mut self, ctx: &mut SimCtx, delta_time: f32) -> Option { if let Some(behaviour) = &mut self.behaviour { let mut ectx = EntityUpdateCtx::from_entity_data(&mut self.data); behaviour.update(&mut ectx, ctx, delta_time); return Some(ectx.to_result()); } None } pub fn physics_update( &mut self, ctx: &mut SimCtx, delta_time: f32, ) -> Option { if let Some(behaviour) = &mut self.behaviour { let mut ectx = EntityUpdateCtx::from_entity_data(&mut self.data); behaviour.physics_update(&mut ectx, ctx, delta_time); return Some(ectx.to_result()); } None } pub fn new( id: u32, rb_h: Option, collider_h: Option, cells: Option, behaviour: Option>, ) -> Self { Entity { data: EntityData { id, rb_h, collider_h, cells, }, behaviour, } } }