use core::fmt; use std::any::TypeId; use std::{num::ParseIntError, str::FromStr}; use anyhow::Result; use serde::{Deserialize, Serialize}; use crate::{ComponentDebug, ComponentNotFoundError, EntityComponent, MultiMut, TypeMap}; pub const COMPONENT_SEPARATOR: char = 'ยง'; #[derive(Default)] pub struct ActivationState { activated: bool, } impl ActivationState { pub fn is_active(&self) -> bool { self.activated } pub(crate) fn apply_change(&mut self) { self.activated = !self.activated; } } pub struct EntityObject<S> { #[cfg(debug_assertions)] pub debug_name: Option<String>, // gltf file name pub(crate) gltf_file: Option<String>, // activation state of Entity pub(crate) activation_state: ActivationState, // program local ID pub entity_id: u32, // component map pub(crate) components: TypeMap<S>, #[cfg(debug_assertions)] component_names: Vec<String>, } unsafe impl<S> Send for EntityObject<S> {} unsafe impl<S> Sync for EntityObject<S> {} impl<S> EntityObject<S> { pub(crate) fn new(id: u32) -> Self { Self { #[cfg(debug_assertions)] debug_name: None, gltf_file: None, activation_state: ActivationState::default(), entity_id: id, components: TypeMap::default(), #[cfg(debug_assertions)] component_names: Vec::new(), } } pub fn gltf_file(&self) -> Option<&String> { self.gltf_file.as_ref() } pub fn multi_mut(&mut self) -> MultiMut<'_, S> { self.components.multi_mut() } pub fn insert_component<T: EntityComponent<S> + ComponentDebug>( &mut self, component: T, ) -> Option<T> { assert!( !self.activation_state.is_active(), "inserting components while the entity is activated is not allowed" ); #[cfg(debug_assertions)] { let name = component.name().to_string(); if !self.component_names.contains(&name) { self.component_names.push(name); } } self.components.insert(component) } pub(crate) fn insert_component_by_id( &mut self, type_id: TypeId, component: Box<dyn EntityComponent<S>>, ) -> Option<Box<dyn EntityComponent<S>>> where S: 'static, { assert!( !self.activation_state.is_active(), "inserting components while the entity is activated is not allowed" ); #[cfg(debug_assertions)] { let name = component.name().to_string(); if !self.component_names.contains(&name) { self.component_names.push(name); } } self.components.insert_type(type_id, component) } pub fn remove_component<T: EntityComponent<S>>(&mut self) -> Option<T> { assert!( !self.activation_state.is_active(), "removing components while the entity is activated is not allowed" ); let t: Option<T> = self.components.remove(); #[cfg(debug_assertions)] if let Some(t) = &t { if let Some(index) = self .component_names .iter() .position(|name| name == t.name()) { self.component_names.remove(index); } } t } pub(crate) fn remove_component_by_id( &mut self, type_id: TypeId, ) -> Option<Box<dyn EntityComponent<S>>> where S: 'static, { assert!( !self.activation_state.is_active(), "removing components while the entity is activated is not allowed" ); let t = self.components.remove_by_type_id(&type_id); #[cfg(debug_assertions)] if let Some(t) = &t { if let Some(index) = self .component_names .iter() .position(|name| name == t.name()) { self.component_names.remove(index); } } t } #[cfg(debug_assertions)] pub fn component_names(&self) -> &[String] { &self.component_names } pub fn get_component<T: EntityComponent<S> + ComponentDebug>( &self, ) -> std::result::Result<&T, ComponentNotFoundError> { self.components.get() } pub fn get_component_mut<T: EntityComponent<S> + ComponentDebug>( &mut self, ) -> std::result::Result<&mut T, ComponentNotFoundError> { self.components.get_mut() } pub fn contains_component<T: EntityComponent<S>>(&self) -> bool { self.components.contains::<T>() } pub fn is_activated(&self) -> bool { self.activation_state.is_active() } pub fn as_entity(&self) -> Entity { Entity { id: self.entity_id } } pub(crate) fn clone_without_components(&self, id: u32) -> EntityObject<S> { EntityObject { #[cfg(debug_assertions)] debug_name: self.debug_name.clone(), // gltf file name gltf_file: self.gltf_file.clone(), activation_state: Default::default(), entity_id: id, // component map components: TypeMap::default(), #[cfg(debug_assertions)] component_names: Vec::new(), } } pub fn clone_component_from<T: EntityComponent<S> + ComponentDebug + Clone>( &mut self, other: &Self, ) -> Result<()> { let component = other.get_component::<T>()?; self.insert_component(component.clone()); Ok(()) } } #[allow(clippy::derive_hash_xor_eq)] #[derive(Debug, Clone, Copy, Hash, Eq, Serialize, Deserialize)] pub struct Entity { pub(crate) id: u32, } impl<S> PartialEq<EntityObject<S>> for Entity { fn eq(&self, entity: &EntityObject<S>) -> bool { self.id == entity.entity_id } } impl PartialEq<Entity> for Entity { fn eq(&self, entity: &Entity) -> bool { self.id == entity.id } } impl fmt::Display for Entity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.id) } } impl FromStr for Entity { type Err = ParseIntError; fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { Ok(Self { id: s.parse()? }) } }