text// Internal structure
include/darkmoon/ecs/
    ├── entity_manager.hpp   ← EntityManager<CMPList, TAGList>
    ├── slotmap.hpp          ← Slotmap<T, Capacity><T> (component storage)
    └── meta_program.hpp     ← MP:: namespace (TypeList, traits, masks)

// User structure
game/src/
    ├── utils/
    │   └── types.hpp        ← user defines CMPList, TAGList, using EM = ...
    ├── components/
    │   ├── render_component.hpp
    │   └── physics_component.hpp
    └── systems/
        ├── render_system.hpp
        └── physics_system.hpp

texttypes.hpp defines CMPList, TAGList, EM = EntityManager<CMPList, TAGList>
            │
            ▼
        user owns EM m_em {}
            │
            ├── m_em.newEntity()        --> Entity&
            ├── m_em.addComponent(e)    --> T&
            ├── m_em.getComponent(e)    --> T&
            └── m_em.forEach()          --> lambda(Entity&, T&...)

types.hpp

c++#pragma once
#include <darkmoon/ecs.hpp>

// Include your component headers
#include "../components/render_component.hpp"
#include "../components/physics_component.hpp"

// List all components here (order matters for the bitmask)
using GameCMPs = MP::TypeList<
    RenderComponent,
    PhysicsComponent
>;

// Tags: empty structs used as boolean flags, no data, no SlotMap slot
struct TagPlayer {};
struct TagEnemy  {};

// List all tags here
using GameTAGs = MP::TypeList<
    TagPlayer,
    TagEnemy
>;

// Instantiate the EntityManager with your types
using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;

C++// components/physics_component.hpp
#pragma once

struct PhysicsComponent {
    int positionX {}, positionY {};
};

// components/render_component.hpp
#pragma once

struct RenderComponent {
    char character {};
    RenderComponent(const char* ch) : character(*ch) {}
};

// Adding a component to an entity
auto& e = EM.newEntity();
EM.addComponent<PhysicsComponent>(e);

// Checking a component in a system
if (e.hasComponent<PhysicsComponent>()) { /* ... */ }

// Iterating only entities with a specific component
EM.forEach<MP::TypeList<PhysicsComponent>, MP::TypeList<>>([&](Entity& e){
    // only PhysicsComponent entities
});

C++// In types.hpp
struct TagPlayer {};
struct TagEnemy  {};

using GameTAGs = MP::TypeList<TagPlayer, TagEnemy>;

// Adding a tag to an entity
auto& e = EM.newEntity();
EM.addTag<TagPlayer>(e);

// Checking a tag in a system
if (e.hasTag<TagPlayer>()) { /* ... */ }

// Iterating only entities with a specific tag
EM.forEach<MP::TypeList<>, MP::TypeList<TagPlayer>>([&](Entity& e){
    // only TagPlayer entities
});

C++// systems/render_system.hpp
#pragma once
#include "../utils/types.hpp"

struct RenderSystem {
    // Declare which components and tags this system needs
    using SYSCMPs = MP::TypeList<RenderComponent>;
    using SYSTAGs = MP::TypeList<>;

    void update(EM& em) {
        em.forEach<SYSCMPs, SYSTAGs>([&](Entity& e, RenderComponent& r) {
            // Optionally access other components on the same entity
            if (e.hasComponent<PhysicsComponent>()) {
                auto& phys = em.getComponent<PhysicsComponent>(e);
                // do something
            }
            std::cout << r.character << "\n";
        });
    }
};

// main.cpp
#include "./systems/render_system.hpp"

int main() {
    EM em {};

    Entity& e = em.newEntity();
    em.addComponent<RenderComponent>(e);

    RenderSystem rm {};

    while (true) {
        rm.update(em);
    }
}

newEntity() Entity&
destroyEntityByID(id) void
destroyEntities(deathSet) void
destroyAll() void
getEntityByID(id) Entity*
getEntities() std::span<Entity>
aliveCount() std::size_t
freeEntities() std::size_t

getID() std::size_t
hasComponent<T>() bool
hasTag<T>() bool

addComponent<T>(e, args...) T&
getComponent<T>(e) T&
destroyComponent<T>(e) void
addTag<T>(e) void
destroyTag<T>(e) void

forEach<CMPs, TAGs>(func) void
forEachAny<CMPs, TAGs>(func) void
C++// Safe pattern for destroying entities during iteration
EM::deathSet toDestroy {};

EM.forEach<SYSCMPs, SYSTAGs>([&](Entity& e, PhysicsComponent& p) {
    if (p.positionX > 100)
        toDestroy.insert(e.getID());
});

EM.destroyEntities(toDestroy); // destroy after the loop

C++// include/darkmoon/ecs/slotmap.hpp
template <
    typename T,
    std::size_t Capacity = 10,
    typename INDEXT = std::uint32_t
>
struct Slotmap;
T -
Capacity 10
INDEXT uint32_t

textm_indices [ ]  ->  indirection table. Each slot holds:
                    .id  --> position in m_data (when occupied)
                         --> next free slot index (when free, forms the freelist)
                    .gen --> generation counter; incremented on every alloc/free

m_data [ ]     ->  packed array of T. Elements 0..m_size-1 are always valid.
                   Gaps never exist; erase uses swap-and-pop to stay compact.

m_erase [ ]    ->  reverse map: m_erase[dataIdx] = slotIdx that points here.
                   Used to fix up m_indices when swap-and-pop relocates the last element.

C++using key_type = struct { index_type id; gen_type gen; };

// push_back returns a key
auto key = slotmap.push_back(MyComponent{});

// Access via key  asserts in debug if stale
MyComponent& cmp = slotmap[key];

// Safe validity check before access
if (slotmap.is_valid(key)) {
    slotmap[key].value = 42;
}

// Erase increments generation, invalidates all copies of this key
slotmap.erase(key); // returns false if key was already stale

push_back(T&&) key_type O(1)
push_back(const T&) key_type O(1)
operator[](key) T& O(1)
erase(key) bool O(1)
is_valid(key) bool O(1)
clear() void O(N)
begin() / end() T* O(1)
size() std::size_t O(1)
capacity() std::size_t O(1)

textBefore erase(key B):           After erase(key B):

m_data:  [ A | B | C ]         m_data:  [ A | C ]
m_erase: [ a | b | c ]         m_erase: [ a | c ]
                                              ↑ C moved here; m_indices[c].id updated

C++// Compile-time constant wrapper
template <typename T, T VAL>
struct constant { static constexpr T value{ VAL }; };

struct true_type  : constant<bool, true>  {};
struct false_type : constant<bool, false> {};

// Type identity helper used as base for "this struct IS type T"
template <typename T>
struct type_id { using type = T; };

// Type equality
template <typename T, typename U> struct is_same : false_type {};
template <typename T>             struct is_same<T, T> : true_type {};
template <typename T, typename U>
constexpr bool is_same_v{ is_same<T, U>::value };

C++// Base case: N == 0 --> the type is the first in the pack
template <typename T, typename... Ts>
struct nth_type<0, T, Ts...> : type_id<T> {};

// Recursive case: strip the head, decrement N
template <std::size_t N, typename T, typename... Ts>
struct nth_type<N, T, Ts...> : type_id<nth_type_t<N - 1, Ts...>> {};

// Convenience alias
template <std::size_t N, typename... Ts>
using nth_type_t = typename nth_type<N, TypeList<Ts...>>::type;

C++// Match found at head --> position 0
template <typename T, typename... Ts>
struct pos<T, T, Ts...> : constant<std::size_t, 0> {};

// No match yet --> 1 + position in the rest
template <typename T, typename U, typename... Ts>
struct pos<T, U, Ts...> : constant<std::size_t, 1 + pos_v<T, Ts...>> {};

C++template <bool Condition, typename T, typename F> struct IFT          : type_id<F> {};
template <typename T, typename F>                struct IFT<true, T, F> : type_id<T> {};

// Alias for readability
template <bool Condition, typename T, typename F>
using IFT_t = typename IFT<Condition, T, F>::type;

TypeList

C++template <typename... Ts>
struct TypeList {
    // Number of types in the list
    consteval static std::size_t size() noexcept { return sizeof...(Ts); }

    // True if T appears anywhere in the list
    template <typename T>
    consteval static bool contains() noexcept {
        return (false || ... || is_same_v<T, Ts>);
    }

    // Zero-based index of T; static_assert if not found
    template <typename T>
    consteval static std::size_t pos() noexcept {
        static_assert(contains<T>(), "Type not found");
        return pos_v<T, Ts...>;
    }
};

// Usage
using GameCMPs = MP::TypeList<RenderComponent, PhysicsComponent>;
constexpr std::size_t idx = GameCMPs::pos<PhysicsComponent>(); // --> 1
constexpr bool has = GameCMPs::contains<AIComponent>();         // --> false

C++template <typename LIST>
using smallest_type =
    IFT_t<LIST::size() <=  8, std::uint8_t,
    IFT_t<LIST::size() <= 16, std::uint16_t,
    IFT_t<LIST::size() <= 32, std::uint32_t,
    std::uint64_t>>>;

// Example: 3 components + 2 tags = 5 total --> uint8_t (fits in 8 bits)
// Example: 20 total types                  --> uint32_t

C++template <typename TLIST>
struct cmp_tag_traits {
    static_assert(TLIST::size() <= 64, "Component tag list is too large");

    // Narrowest uint that fits one bit per type
    using mask_type = smallest_type<TLIST>;

    // Number of types registered
    consteval static uint8_t size() noexcept { return TLIST::size(); }

    // Bit position of a single component or tag
    template <typename CMPTAG>
    consteval static uint8_t id() noexcept {
        static_assert(TLIST::template contains<CMPTAG>(), "Component or Tag not found");
        return TLIST::template pos<CMPTAG>();
    }

    // OR together the bits for all types in the query
    template <typename... Ts>
    consteval static mask_type mask() noexcept {
        return (0 | ... | (1LL << id<Ts>()));
    }
};

// forEach uses it like this (pseudocode):
using Traits = cmp_tag_traits<MergedList>;
constexpr auto queryMask = Traits::mask<PhysicsComponent, TagPlayer>();
// compiled down to a single integer constant zero runtime cost
if ((entity.mask & queryMask) == queryMask) { /* visit */ }

C++// replace_t: reuse the types in a TypeList with a different variadic template
// TypeList<A,B,C> --> std::tuple<A,B,C>
using SlotmapTuple = replace_t<std::tuple, GameCMPs>;

// forall_insert_template_t: wrap every type in a TypeList with another template
// TypeList<A,B,C> --> TypeList<Slotmap<A>, Slotmap<B>, Slotmap<C>>
using SlotmapList = forall_insert_template_t<Slotmap, GameCMPs>;

C++// Both const and non-const overloads are provided
// Base case (I == tuple size): do nothing
// Recursive case: call f on element I, then advance

MP::for_each_in_tuple(m_slotmaps, [](auto& slotmap) {
    slotmap.clear(); // resets every component storage in one call
});

constant<T, V> struct
true_type / false_type struct
is_same<T,U> trait
nth_type<N, Ts...> trait
pos<T, Ts...> trait
IFT<Cond, T, F> trait
TypeList<Ts...> struct
smallest_type<TL> alias
cmp_tag_traits<TL> struct
replace_t<New, TL> alias
forall_insert_template_t<W, TL> alias
for_each_in_tuple(t, f) function

TEXT// structure
src/
    ├── types.hpp
    └── main.cpp
C++// types.hpp
#include <darkmoon/ecs.hpp>

struct HealthComponent {
    int hp { 100 };
};

using GameCMPs = MP::TypeList<HealthComponent>;
using GameTAGs = MP::TypeList<>;

using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;
C++// main.cpp
#include <iostream>
#include "types.hpp"

int main (){
    EM em {};

    // Create entity
    Entity& player = em.newEntity();

    // Add component
    auto& health = em.addComponent<HealthComponent>(player);
    health.hp = 80;

    // Save ID player
    std::size_t playerID = player.getID();
    std::cout << "Player ID: " << playerID << "\n";

    // Check before accessing (good practice in generic systems)
    if(player.hasComponent<HealthComponent>()){
        auto& h = em.getComponent<HealthComponent>(player);
        h.hp -= 10;
        std::cout << "Player HP: " << h.hp << "\n";
    }

    // EntityManager statistics
    std::cout << "Living Entities: " << em.aliveCount() << "\n";

    return 0;
}
TEXT// output.txt
Player ID: 0
Player HP: 70
Living Entities: 1

TEXT// structure
src/
    ├── types.hpp
    └── main.cpp
C++// types.hpp
#include <darkmoon/ecs.hpp>

struct PositionComponent {
    float x {}, y {};
};
struct VelocityComponent {
    float dx {}, dy {};
};
struct HealthComponent {
    int hp { 100 };
};

// The order in the TypeList determines the bit in the internal bitmask.
// Do not change it once fixed without recompiling everything.

using GameCMPs = MP::TypeList<
    PositionComponent,
    VelocityComponent,
    HealthComponent
>;
using GameTAGs = MP::TypeList<>;

using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;
C++// main.cpp
#include <iostream>
#include "types.hpp"

int main (){
    EM em {};

    // Create entity and add the 3 components
    Entity& player = em.newEntity();
    auto& pPos = em.addComponent<PositionComponent>(player);
    pPos.x = 10.f; pPos.y = 5.f;
    auto& pVel = em.addComponent<VelocityComponent>(player);
    pVel.dx = 1.f; pVel.dy = 0.5f;
    auto& pHp = em.addComponent<HealthComponent>(player);
    pHp.hp = 200;

    // Enemy without velocity (static)
    Entity& enemy = em.newEntity();
    em.addComponent<PositionComponent>(enemy).x = 50.f;
    em.addComponent<HealthComponent>(enemy);

    // Pickup with only position
    Entity& pickup = em.newEntity();
    em.addComponent<PositionComponent>(pickup) = { 30.f, 20.f };

    std::cout << "Living Entities: " << em.aliveCount() << "\n";

    // Movement System
    std::cout << "-- Movement System --\n";

    em.forEach<MP::TypeList<PositionComponent, VelocityComponent>, MP::TypeList<>>(
        [](Entity& e, PositionComponent& pos, VelocityComponent& vel) {
            pos.x += vel.dx;
            pos.y += vel.dy;
            std::cout << " - Entity " << e.getID()
                      << " move to (" << pos.x << ", " << pos.y << ")\n";
        }
    );

    // Health System
    std::cout << "-- Health System --\n";

    em.forEach<MP::TypeList<HealthComponent>, MP::TypeList<>>(
        [&](Entity& e, HealthComponent& h) {
            // Optionally access other components on the same entity
            if (e.hasComponent<PositionComponent>()) {
                auto& pos = em.getComponent<PositionComponent>(e);
                std::cout << " - Entity " << e.getID()
                          << " in (" << pos.x << ", " << pos.y << ")"
                          << " HP: " << h.hp << "\n";
            }
        }
    );

    return 0;
}
TEXT// output.txt
Living Entities: 3
-- Movement System --
 - Entity 0 move to (11, 5.5)
-- Health System --
 - Entity 0 in (11, 5.5) HP: 200
 - Entity 1 in (50, 0) HP: 100

TEXT// structure
src/
    ├── types.hpp
    └── main.cpp
C++// types.hpp
#pragma once
#include <darkmoon/ecs.hpp>

struct HealthComponent {
    int hp { 100 };
};

struct TagPlayer     {};
struct TagEnemy      {};
struct TagInvincible {};

using GameCMPs = MP::TypeList<HealthComponent>;
using GameTAGs = MP::TypeList<TagPlayer, TagEnemy, TagInvincible>;

using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;
C++// main.cpp
#include <iostream>
#include "types.hpp"

static void applyDamage(EM& em, Entity& e, int dmg){
    if(e.hasTag<TagInvincible>()){
        std::cout << " - Entity " << e.getID() << " is invincible, no damage\n";
        return;
    }
    auto& h = em.getComponent<HealthComponent>(e);
    h.hp -= dmg;
    std::cout << " - Entity " << e.getID() 
              << " takes " << dmg << " damage -> HP: " << h.hp << "\n";
}

int main (){
    EM em {};

    // Player
    Entity& player = em.newEntity();
    em.addComponent<HealthComponent>(player).hp = 200;
    em.addTag<TagPlayer>(player);

    // Enemies
    Entity& orc   = em.newEntity();
    Entity& troll = em.newEntity();

    em.addComponent<HealthComponent>(orc);
    em.addComponent<HealthComponent>(troll).hp = 300;

    em.addTag<TagEnemy>(orc);
    em.addTag<TagEnemy>(troll);

    // Simulate player pick power-up
    std::cout << "-- Player pick power-up --\n";
    em.addTag<TagInvincible>(player);

    // Damage enemies
    std::cout << "-- Damage all enemies (TagEnemy + HealthComponent) --\n";

    em.forEach<MP::TypeList<HealthComponent>, MP::TypeList<TagEnemy>>(
        [&](Entity& e, HealthComponent&) {
            applyDamage(em, e, 25);
        }
    );

    // Damage player
    std::cout << "-- Damage player (TagPlayer + HealthComponent) --\n";

    em.forEach<MP::TypeList<HealthComponent>, MP::TypeList<TagPlayer>>(
        [&](Entity& e, HealthComponent&) {
            applyDamage(em, e, 50);
        }
    );

    // Expire power-up
    std::cout << "-- Expire power-up --\n";
    em.destroyTag<TagInvincible>(player);

    // Damage player
    std::cout << "-- Damage player (TagPlayer + HealthComponent) --\n";

    em.forEach<MP::TypeList<HealthComponent>, MP::TypeList<TagPlayer>>(
        [&](Entity& e, HealthComponent&) {
            applyDamage(em, e, 50);
        }
    );

    // Final state
    std::cout << "-- Final state --\n";

    em.forEach<MP::TypeList<HealthComponent>, MP::TypeList<>>(
        [&](Entity& e, HealthComponent& h) {
            std::string role =
                e.hasTag<TagPlayer>() ? "player" :
                e.hasTag<TagEnemy>()  ? "enemy"  : "unknown";
            std::cout << " - [" << role << "] entity " << e.getID()
                      << "  HP: " << h.hp << "\n";
        }
    );

    return 0;
}
TEXT// output.txt
-- Player pick power-up --
-- Damage all enemies (TagEnemy + HealthComponent) --
 - Entity 1 takes 25 damage -> HP: 75
 - Entity 2 takes 25 damage -> HP: 275
-- Damage player (TagPlayer + HealthComponent) --
 - Entity 0 is invincible, no damage
-- Expire power-up --
-- Damage player (TagPlayer + HealthComponent) --
 - Entity 0 takes 50 damage -> HP: 150
-- Final state --
 - [player] entity 0  HP: 150
 - [enemy] entity 1  HP: 75
 - [enemy] entity 2  HP: 275

TEXT// structure
src/
    ├── types.hpp
    └── main.cpp
    └── systems/
        ├── boundsSystem.hpp
        ├── combatSystem.hpp
        └── movementSystem.hpp
C++// types.hpp
#pragma once
#include <darkmoon/ecs.hpp>

struct PositionComponent {
    float x {}, y {};
};

struct VelocityComponent {
    float dx {}, dy {};
};

struct HealthComponent {
    int hp { 100 };
};

struct TagPlayer {};
struct TagEnemy  {};

using GameCMPs = MP::TypeList<
    PositionComponent,
    VelocityComponent,
    HealthComponent
>;
using GameTAGs = MP::TypeList<TagPlayer, TagEnemy>;

using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;
C++// boundsSystem.hpp
#pragma once
#include "../types.hpp"

// Reverses speed when leaving the playing area ("bounce" effect).

struct BoundsSystem {
    using SYSCMPs = MP::TypeList<PositionComponent, VelocityComponent>;
    using SYSTAGs = MP::TypeList<>;

    static constexpr float MIN_X {   0.f };
    static constexpr float MAX_X { 100.f };
    static constexpr float MIN_Y {   0.f };
    static constexpr float MAX_Y { 100.f };

    void update(EM& em) {
        em.forEach<SYSCMPs, SYSTAGs>(
            [](Entity&, PositionComponent& pos, VelocityComponent& vel) {
                if (pos.x < MIN_X || pos.x > MAX_X) vel.dx = -vel.dx;
                if (pos.y < MIN_Y || pos.y > MAX_Y) vel.dy = -vel.dy;
            }
        );
    }
};
C++// combatSystem.hpp
#pragma once
#include "../types.hpp"

// Simulates an attack by the player on all enemies.

struct CombatSystem {
    using SYSCMPs = MP::TypeList<HealthComponent>;
    using SYSTAGs = MP::TypeList<TagEnemy>;

    static constexpr int ATTACK_DAMAGE { 15 };

    void update(EM& em) {
        em.forEach<SYSCMPs, SYSTAGs>(
            [&](Entity&, HealthComponent& h) {
                h.hp -= ATTACK_DAMAGE;
            }
        );
    }
};
C++// movementSystem.hpp
#pragma once
#include "../types.hpp"

// Add speed to position.

struct MovementeSystem {
    using SYSCMPs = MP::TypeList<PositionComponent, VelocityComponent>;
    using SYSTAGs = MP::TypeList<>;

    void update(EM& em) {
        em.forEach<SYSCMPs, SYSTAGs>(
            [&](Entity&, PositionComponent& pos, VelocityComponent& vel) {
                pos.x += vel.dx;
                pos.y += vel.dy;
            }
        );
    }
};
C++// main.cpp
#include <iostream>
#include "types.hpp"

#include "systems/boundsSystem.hpp"
#include "systems/combatSystem.hpp"
#include "systems/movementSystem.hpp"

int main (){
    // EntityManager
    EM em {};

    // Systems
    BoundsSystem    bounds   {};
    CombatSystem    combat   {};
    MovementeSystem movement {};

    // Player
    Entity& player = em.newEntity();
    em.addComponent<PositionComponent>(player) = { 50.f, 50.f };
    em.addComponent<VelocityComponent>(player) = { 3.f, 2.f };
    em.addComponent<HealthComponent>(player).hp = 200;
    em.addTag<TagPlayer>(player);

    // Enemies
    Entity& orc = em.newEntity();
    em.addComponent<PositionComponent>(orc) = { 20.f, 80.f };
    em.addComponent<VelocityComponent>(orc) = { -1.f, 1.f };
    em.addComponent<HealthComponent>(orc);
    em.addTag<TagEnemy>(orc);

    Entity& troll = em.newEntity();
    em.addComponent<PositionComponent>(troll) = { 70.f, 30.f };
    em.addComponent<VelocityComponent>(troll) = { 0.5f, -1.5f };
    em.addComponent<HealthComponent>(troll).hp = 250;
    em.addTag<TagEnemy>(troll);

    // Loop game
    for (int frame = 1; frame <= 3; frame++) {
        movement.update(em);
        bounds.update(em);
        combat.update(em);

        // Print state
        std::cout << "Frame " << frame << "\n";

        em.forEach<MP::TypeList<PositionComponent, HealthComponent>, MP::TypeList<>>(
            [&](Entity& e, PositionComponent& pos, HealthComponent& h) {
                std::string role =
                    e.hasTag<TagPlayer>() ? "player" :
                    e.hasTag<TagEnemy>()  ? "enemy"  : "?";
                std::cout << " - [" << role << "] id=" << e.getID()
                          << "   pos=(" << pos.x << ", " << pos.y << ")"
                          << "   hp=" << h.hp << "\n";
            }
        );
    }

    return 0;
}
TEXT// output.txt
Frame 1
 - [player] id=0   pos=(53, 52)   hp=200
 - [enemy] id=1   pos=(19, 81)   hp=85
 - [enemy] id=2   pos=(70.5, 28.5)   hp=235
Frame 2
 - [player] id=0   pos=(56, 54)   hp=200
 - [enemy] id=1   pos=(18, 82)   hp=70
 - [enemy] id=2   pos=(71, 27)   hp=220
Frame 3
 - [player] id=0   pos=(59, 56)   hp=200
 - [enemy] id=1   pos=(17, 83)   hp=55
 - [enemy] id=2   pos=(71.5, 25.5)   hp=205

TEXT// structure
src/
    ├── types.hpp
    └── main.cpp
C++// types.hpp
#pragma once
#include <darkmoon/ecs.hpp>

struct PositionComponent {
    float x {}, y {};
};

struct LifetimeComponent {
    float ttl {};
};

struct HealthComponent {
    int hp { 100 };
};

struct TagBullet {};
struct TagEnemy  {};

using GameCMPs = MP::TypeList<
    PositionComponent,
    LifetimeComponent,
    HealthComponent
>;
using GameTAGs = MP::TypeList<TagBullet, TagEnemy>;

using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;
C++// main.cpp
#include <iostream>
#include "types.hpp"

int main (){
    // EntityManager
    EM em {};

    // Create bullets with time life
    for (int i = 0; i < 5; i++) {
        Entity& bullet = em.newEntity();
        em.addComponent<PositionComponent>(bullet) = { static_cast<float>(i * 10), 0.f };
        em.addComponent<LifetimeComponent>(bullet).ttl = static_cast<float>(i) * 0.5f;
        em.addTag<TagBullet>(bullet);
    }

    // Create enemies
    Entity& orc   = em.newEntity();
    Entity& troll = em.newEntity();
    em.addComponent<HealthComponent>(orc);
    em.addComponent<HealthComponent>(troll).hp = 300;
    em.addTag<TagEnemy>(orc);
    em.addTag<TagEnemy>(troll);

    std::cout << "Initial entities: " << em.aliveCount() << "\n";

    // deathSet: destroy expired bullets 
    constexpr float DT { 0.5f };
    EM::deathSet toDestroy {};

    std::cout << "-- Life Time System --\n";

    em.forEach<MP::TypeList<LifetimeComponent>, MP::TypeList<TagBullet>>(
        [&](Entity& e, LifetimeComponent& lt) {
            lt.ttl -= DT;
            std::cout << " - Bullet " << e.getID()
                      << "  ttl=" << lt.ttl << "\n";

            if (lt.ttl <= 0.f) {
                toDestroy.insert(e.getID()); // No destroy here
            }
        }
    );

    // Destroy outside the loop: safe
    em.destroyEntities(toDestroy);
    toDestroy.clear();

    std::cout << "Living entities: " << em.aliveCount() << "\n";

    // destroyAll
    std::cout << "-- destroyAll --\n";
    std::cout << " - before: " << em.aliveCount() << " entities\n";
    em.destroyAll();
    std::cout << " - after: " << em.aliveCount() << " entities\n";

    return 0;
}
TEXT// output.txt
Initial entities: 7
-- Life Time System --
 - Bullet 0  ttl=-0.5
 - Bullet 1  ttl=0
 - Bullet 2  ttl=0.5
 - Bullet 3  ttl=1
 - Bullet 4  ttl=1.5
Living entities: 5
-- destroyAll --
 - before: 5 entities
 - after: 0 entities

TEXT// structure
src/
    ├── types.hpp
    └── main.cpp
C++// types.hpp
#pragma once
#include <darkmoon/ecs.hpp>

struct PositionComponent {
    float x {}, y {};
};

struct HealthComponent {
    int hp { 100 };
};

struct RenderComponent {
    char glyph { '?' };
};

struct TagPlayer {};
struct TagEnemy  {};
struct TagPickup {};

using GameCMPs = MP::TypeList<
    PositionComponent,
    HealthComponent,
    RenderComponent
>;
using GameTAGs = MP::TypeList<TagPlayer, TagEnemy, TagPickup>;

using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;
C++// main.cpp
#include <iostream>
#include "types.hpp"

// forEach    -> AND: for all entities that have all the CMPs and all the requested TAGs
// forEachAny ->  OT: for all entities that have at least one of the requested CMPs or TAGs

// Example entities:
//   player -> Position + Health + Render | TagPlayer
//   archer -> Position + Health + Render | TagEnemy
//   statue -> Position + Render          | (no tags)
//   pickup -> Position                   | TagPickup

int main (){
    EM em {};

    Entity& player = em.newEntity();
    em.addComponent<PositionComponent>(player) = { 10.f, 10.f };
    em.addComponent<HealthComponent>(player).hp = 200;
    em.addComponent<RenderComponent>(player).glyph = '@';
    em.addTag<TagPlayer>(player);

    Entity& archer = em.newEntity();
    em.addComponent<PositionComponent>(archer) = { 40.f, 20.f };
    em.addComponent<HealthComponent>(archer);
    em.addComponent<RenderComponent>(archer).glyph = 'A';
    em.addTag<TagEnemy>(archer);

    Entity& statue = em.newEntity();
    em.addComponent<PositionComponent>(statue) = { 60.f, 60.f };
    em.addComponent<RenderComponent>(statue).glyph = '&';

    Entity& pickup = em.newEntity();
    em.addComponent<PositionComponent>(pickup) = { 25.f, 25.f };
    em.addTag<TagPickup>(pickup);

    std::cout << "Entities: " << em.aliveCount() << "\n";

    std::cout << "forEach<Health + Render, {} > -> player and archer\n";
    em.forEach<MP::TypeList<HealthComponent, RenderComponent>, MP::TypeList<>>(
        [](Entity& e, HealthComponent& h, RenderComponent& r) {
            std::cout << " - id=" << e.getID()
                      << "  glyph='" << r.glyph
                      << "'  hp=" << h.hp << "\n";
        }
    );

    std::cout << "forEach<Health, {TagEnemy}> -> only archer\n";
    em.forEach<MP::TypeList<HealthComponent>, MP::TypeList<TagEnemy>>(
        [](Entity& e, HealthComponent& h) {
            std::cout << " - id=" << e.getID() << "  hp=" << h.hp << "\n";
        }
    );

    std::cout << "forEach<Render, {}> -> player, archer, statue\n";
    em.forEach<MP::TypeList<RenderComponent>, MP::TypeList<>>(
        [](Entity& e, RenderComponent& r) {
            std::cout << " - id=" << e.getID()
                      << "  glyph='" << r.glyph << "'\n";
        }
    );

    std::cout << "forEach<Position, {}> -> all (4 entities)\n";
    em.forEach<MP::TypeList<PositionComponent>, MP::TypeList<>>(
        [](Entity& e, PositionComponent& pos) {
            std::cout << " - id=" << e.getID()
                      << "  pos=(" << pos.x << ", " << pos.y << ")\n";
        }
    );

    std::cout << "forEachAny<{}, {TagPlayer, TagEnemy}> -> player and archer\n";
    em.forEachAny<MP::TypeList<>, MP::TypeList<TagPlayer, TagEnemy>>(
        [&](Entity& e) {
            std::string role = e.hasTag<TagPlayer>() ? "player" : "enemy";
            std::cout << " - id=" << e.getID() << "  role=" << role << "\n";
        }
    );

    std::cout << "forEachAny<{}, {TagEnemy, TagPickup}> -> archer and pickup\n";
    em.forEachAny<MP::TypeList<>, MP::TypeList<TagEnemy, TagPickup>>(
        [&](Entity& e) {
            std::cout << " - id=" << e.getID()
                      << "  hasPosition=" << e.hasComponent<PositionComponent>() << "\n";
        }
    );

    return 0;
}
TEXT// output.txt
Entities: 4
forEach<Health + Render, {} > -> player and archer
 - id=0  glyph='@'  hp=200
 - id=1  glyph='A'  hp=100
forEach<Health, {TagEnemy}> -> only archer
 - id=1  hp=100
forEach<Render, {}> -> player, archer, statue
 - id=0  glyph='@'
 - id=1  glyph='A'
 - id=2  glyph='&'
forEach<Position, {}> -> all (4 entities)
 - id=0  pos=(10, 10)
 - id=1  pos=(40, 20)
 - id=2  pos=(60, 60)
 - id=3  pos=(25, 25)
forEachAny<{}, {TagPlayer, TagEnemy}> -> player and archer
 - id=0  role=player
 - id=1  role=enemy
forEachAny<{}, {TagEnemy, TagPickup}> -> archer and pickup
 - id=1  hasPosition=1
 - id=3  hasPosition=1