summaryrefslogblamecommitdiffstats
path: root/include/ecs.hpp
blob: ab5e2b243f2122536087cef524696794ca9aef0c (plain) (tree)










































































































































                                                                                        
#ifndef ECS_HPP
#define ECS_HPP 1

#include "entitycomponents.hpp"
#include "util.hpp"
#include <bitset>
#include <typeindex>
#include <queue>
#include <cassert>
#include <ranges>
#include <span>
#include <iostream>

namespace ecs {

struct ComponentContainer {
    std::vector<component_variant> packed{};
    std::vector<int> sparse{};
    std::queue<unsigned> free{};

    void resize(size_t n) { sparse.resize(n, -1); }

    template<component_type T>
    void insert(unsigned id, const T &c) {
        assert(sparse.size() > id);
        if(free.empty()) {
            sparse[id] = (int)packed.size();
            packed.push_back(c);
        }else {
            sparse[id] = (int)free.front();
            packed[sparse[id]] = c;
            free.pop();
        }
    }

    void remove(unsigned id) {
        assert(sparse.size() > id);
        if(sparse[id] > -1) {
            free.push((unsigned)sparse[id]);
        }
        sparse[id] = -1;
    }

    template<component_type T>
    T &reduce(unsigned id) {
        assert(sparse.size() > id);
        assert(sparse[id] > -1);
        return std::get<T>(packed[sparse[id]]);
    }
};

class EntityMan;
struct Entity {
    EntityMan *man;
    component_sig sig{};
    unsigned id;

    Entity(const Entity &e) = default;
    Entity(EntityMan *Man, unsigned Id) :
        man(Man), sig(0), id(Id) {}


    template<component_type T>
    constexpr bool contains() { return sig.test(get_index<T, component_variant>()); }

    template<component_type T>
    T &get();

    template<component_type T>
    Entity &addComponent(const T &component);
};

class EntityMan {
    std::array<ComponentContainer, std::variant_size_v<component_variant>> m_components;
    std::vector<Entity> m_entities;
    std::queue<unsigned> m_free;
public:
    Entity &operator[](std::size_t i) { return m_entities[i]; }

    Entity newEntity() {
        std::size_t newid = 0;
        if(!m_free.empty()) {
            newid = m_free.front();
            m_free.pop();
        }else{
            newid = m_entities.size();
            m_entities.emplace_back(this, newid);
            for(auto &container : m_components) container.resize(m_entities.size());
        }
        return m_entities[newid];
    }

    void deleteEntity(Entity &e) {
        for(auto &container : m_components) container.remove(e.id);
        e.sig.reset();
        m_free.push(e.id);
    }

    template<component_type T>
    T &get(const Entity &e) {
        constexpr unsigned cid = get_index<T, component_variant>();
        return m_components[cid].reduce<T>(e.id);
    }

    template<component_type T>
    auto getWith() {
        constexpr unsigned cid = get_index<T, component_variant>();
        auto e_hc = [](Entity const e) { return e.sig[cid]; };
        return std::ranges::views::filter(m_entities, e_hc);
    }

    std::span<Entity> all() {
        return std::span<Entity>(m_entities.begin(), m_entities.end());
    }

    std::size_t size() { return m_entities.size(); }

    template<component_type T>
    Entity &addComponent(Entity &entity, const T& component) {
        constexpr unsigned cid = get_index<T, component_variant>();
        m_components[cid].insert(entity.id, component);
        m_entities[entity.id].sig[cid] = 1;
        return entity;
    }
};

template<component_type T>
T &Entity::get() {
    return man->get<T>(*this);
}

template<component_type T>
Entity &Entity::addComponent(const T& component) {
    return man->addComponent(*this, component);
}

}

#endif