bbecs 0.3.0

ECS library for Brooks Builds projects made live on Twitch at https://twitch.tv/brookzerker.
Documentation
pub mod bitmap;
mod entity_data;

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use entity_data::EntityData;
use eyre::Result;
use ggez::audio::SoundData;
use ggez::event::KeyCode;
use ggez::graphics::{Color, Mesh, Text, TextFragment};

use crate::components::{CastComponents, ComponentData};
use crate::data_types::point::Point;
use crate::resources::resource::Resource;
use crate::resources::resources_data::ResourcesData;

use self::bitmap::BitMap;
use self::entity_data::EntityDataTraits;

macro_rules! impl_world_trait {
    ($new_type:ty, $arm:ident) => {
        impl WorldMethods<$new_type> for World {
            fn with_component(&mut self, name: &str, data: $new_type) -> Result<&mut Self> {
                self.entity_data.insert(name, data)?;
                self.bitmap.insert(name)?;
                Ok(self)
            }

            fn add_resource(&mut self, name: String, data: $new_type) {
                self.resources.insert(name, Resource::$arm(data));
            }
        }
    };
}

const TO_BE_DELETED: &str = "to be deleted";
pub const ENTITY_ID: &str = "entity id";

pub type DataWrapper<T> = Rc<RefCell<T>>;

pub trait WorldMethods<T> {
    fn with_component(&mut self, name: &str, data: T) -> Result<&mut Self>;
    fn add_resource(&mut self, name: String, data: T);
}

pub struct World {
    pub entity_data: EntityData,
    resources: ResourcesData,
    is_empty: bool,
    next_entity_id: u32,
    bitmap: BitMap,
}

impl World {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn register<S: ToString>(&mut self, name: S) -> Result<()> {
        self.entity_data.register(name.to_string())?;
        self.bitmap.register(name.to_string());
        Ok(())
    }

    pub fn spawn_entity(&mut self) -> Result<&mut Self> {
        self.entity_data.insert(TO_BE_DELETED, false)?;
        self.entity_data.insert(ENTITY_ID, self.next_entity_id)?;
        self.bitmap.spawn_entity();
        self.bitmap.insert(TO_BE_DELETED)?;
        self.bitmap.insert(ENTITY_ID)?;
        self.is_empty = false;
        self.next_entity_id += 1;

        Ok(self)
    }

    pub fn query(&self, names: Vec<&str>) -> Result<HashMap<String, Vec<&ComponentData>>> {
        let bitmap_query = self.bitmap.query(names.clone())?;
        self.entity_data.query(bitmap_query)
    }

    pub fn get_resource<S: Into<String>>(&self, name: S) -> Result<&Rc<RefCell<Resource>>> {
        self.resources.get(&name.into())
    }

    pub fn update(&mut self) -> Result<()> {
        let query_results = self.query(vec![TO_BE_DELETED])?;
        let to_be_deleted_query = query_results.get(TO_BE_DELETED).unwrap();
        let mut bitmap_indexes_to_delete = vec![];

        to_be_deleted_query
            .iter()
            .enumerate()
            .for_each(|(index, to_be_deleted)| {
                let to_be_deleted: &Rc<RefCell<bool>> = to_be_deleted.cast().unwrap();
                let to_be_deleted = to_be_deleted.borrow();
                if *to_be_deleted {
                    bitmap_indexes_to_delete.push(index);
                }
            });

        let component_indexes_to_delete = self
            .bitmap
            .calculate_component_indexes_to_delete(&bitmap_indexes_to_delete)?;

        self.bitmap
            .delete_entities_by_index(bitmap_indexes_to_delete)?;

        self.entity_data
            .delete_entities_by_index(component_indexes_to_delete)?;
        Ok(())
    }

    pub fn delete_by_id(&self, id: u32) -> Result<()> {
        let query_results = self.query(vec![TO_BE_DELETED, ENTITY_ID])?;
        let query_to_be_deleted = query_results.get(TO_BE_DELETED).unwrap();
        let query_ids = query_results.get(ENTITY_ID).unwrap();

        for (index, component_id) in query_ids.iter().enumerate() {
            let wrapped_component_id: &Rc<RefCell<u32>> = component_id.cast()?;
            let component_id = wrapped_component_id.borrow();

            if *component_id == id {
                let wrapped_to_be_deleted: &Rc<RefCell<bool>> =
                    query_to_be_deleted[index].cast()?;
                let mut to_be_deleted = wrapped_to_be_deleted.borrow_mut();
                *to_be_deleted = true;
            }
        }
        Ok(())
    }
}

impl Default for World {
    fn default() -> Self {
        let mut entity_data = EntityData::new();
        let mut bitmap = BitMap::new();

        entity_data.register(TO_BE_DELETED.into()).unwrap();
        bitmap.register(TO_BE_DELETED.into());
        entity_data.register(ENTITY_ID.into()).unwrap();
        bitmap.register(ENTITY_ID.into());

        Self {
            entity_data,
            resources: ResourcesData::new(),
            is_empty: true,
            next_entity_id: 0,
            bitmap,
        }
    }
}

impl_world_trait!(Color, Color);
impl_world_trait!(Mesh, Mesh);
impl_world_trait!(Point, Point);
impl_world_trait!(u32, U32);
impl_world_trait!(f32, F32);
impl_world_trait!(usize, Usize);
impl_world_trait!(bool, Bool);
impl_world_trait!(KeyCode, GgezKeyCode);
impl_world_trait!(String, Marker);
impl_world_trait!(Text, GgezText);
impl_world_trait!(SoundData, GgezSound);
impl_world_trait!(TextFragment, GgezTextFragment);