summaryrefslogtreecommitdiffstats
path: root/include/ecs.hpp
blob: ab5e2b243f2122536087cef524696794ca9aef0c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#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