2024-08-23 11:22:09 +00:00
|
|
|
use core::fmt;
|
|
|
|
use std::any::TypeId;
|
|
|
|
use std::{num::ParseIntError, str::FromStr};
|
|
|
|
|
|
|
|
use anyhow::Result;
|
2025-02-26 07:39:19 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
use crate::{ComponentDebug, ComponentNotFoundError, EntityComponent, MultiMut, TypeMap};
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub const COMPONENT_SEPARATOR: char = '§';
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
#[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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub struct EntityObject<S> {
|
2024-08-23 11:22:09 +00:00
|
|
|
#[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
|
2025-02-26 07:39:19 +00:00
|
|
|
pub(crate) components: TypeMap<S>,
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
component_names: Vec<String>,
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
unsafe impl<S> Send for EntityObject<S> {}
|
|
|
|
unsafe impl<S> Sync for EntityObject<S> {}
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
impl<S> EntityObject<S> {
|
2024-08-23 11:22:09 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn multi_mut(&mut self) -> MultiMut<'_, S> {
|
2024-08-23 11:22:09 +00:00
|
|
|
self.components.multi_mut()
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn insert_component<T: EntityComponent<S> + ComponentDebug>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&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,
|
2025-02-26 07:39:19 +00:00
|
|
|
component: Box<dyn EntityComponent<S>>,
|
|
|
|
) -> Option<Box<dyn EntityComponent<S>>>
|
|
|
|
where
|
|
|
|
S: 'static,
|
|
|
|
{
|
2024-08-23 11:22:09 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn remove_component<T: EntityComponent<S>>(&mut self) -> Option<T> {
|
2024-08-23 11:22:09 +00:00
|
|
|
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,
|
2025-02-26 07:39:19 +00:00
|
|
|
) -> Option<Box<dyn EntityComponent<S>>>
|
|
|
|
where
|
|
|
|
S: 'static,
|
|
|
|
{
|
2024-08-23 11:22:09 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn get_component<T: EntityComponent<S> + ComponentDebug>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&self,
|
|
|
|
) -> std::result::Result<&T, ComponentNotFoundError> {
|
|
|
|
self.components.get()
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn get_component_mut<T: EntityComponent<S> + ComponentDebug>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&mut self,
|
|
|
|
) -> std::result::Result<&mut T, ComponentNotFoundError> {
|
|
|
|
self.components.get_mut()
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn contains_component<T: EntityComponent<S>>(&self) -> bool {
|
2024-08-23 11:22:09 +00:00
|
|
|
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 }
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub(crate) fn clone_without_components(&self, id: u32) -> EntityObject<S> {
|
2024-08-23 11:22:09 +00:00
|
|
|
EntityObject {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
debug_name: self.debug_name.clone(),
|
|
|
|
|
|
|
|
// gltf file name
|
|
|
|
gltf_file: self.gltf_file.clone(),
|
|
|
|
|
|
|
|
activation_state: Default::default(),
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
entity_id: id,
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
// component map
|
2025-02-26 07:39:19 +00:00
|
|
|
components: TypeMap::default(),
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
component_names: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub fn clone_component_from<T: EntityComponent<S> + ComponentDebug + Clone>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&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,
|
|
|
|
}
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
impl<S> PartialEq<EntityObject<S>> for Entity {
|
|
|
|
fn eq(&self, entity: &EntityObject<S>) -> bool {
|
2024-08-23 11:22:09 +00:00
|
|
|
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()? })
|
|
|
|
}
|
|
|
|
}
|