diff --git a/src/openvic-simulation/ecs/ArchetypeAlias.hpp b/src/openvic-simulation/ecs/ArchetypeAlias.hpp new file mode 100644 index 00000000..7ccadbc2 --- /dev/null +++ b/src/openvic-simulation/ecs/ArchetypeAlias.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +namespace OpenVic::ecs { + using archetype_alias_id_t = uint64_t; + + template + struct ArchetypeAlias { + using component_tuple = std::tuple; + }; + + // Helper to hold a component_tuple as a type + template + struct archetype_component_list {}; + + template + struct archetype_component_list_extract; + + template + struct archetype_component_list_extract> { + using type = archetype_component_list; + }; + + template class Templ, typename Tuple> + struct archetype_component_list_apply; + + template class Templ, typename... Ts> + struct archetype_component_list_apply> { + using type = Templ; + }; + + template + concept archetype_function = requires(Fn fn, A::component_tuple tuple) { + std::apply(fn, tuple); + }; + + template + concept archetype_args_function = requires(Fn fn, A::component_tuple tuple, Args... args) { + std::apply(std::bind_front(fn, args...), tuple); + }; + + template typename C> + concept archetype_contained_function = requires(Fn fn, archetype_component_list_apply::type container) { + fn(container); + }; +} + +// Usage Example: +// ECS_DEFINE_ARCHETYPE(LeaderArchetype, LeaderTemplate); +#define ECS_DEFINE_ARCHETYPE(Type, ...) \ + struct Type : OpenVic::ecs::ArchetypeAlias<__VA_ARGS__> {}; diff --git a/src/openvic-simulation/ecs/World.hpp b/src/openvic-simulation/ecs/World.hpp index abce3479..ac8fc430 100644 --- a/src/openvic-simulation/ecs/World.hpp +++ b/src/openvic-simulation/ecs/World.hpp @@ -5,12 +5,14 @@ #include #include #include +#include #include #include #include #include #include "openvic-simulation/ecs/Archetype.hpp" +#include "openvic-simulation/ecs/ArchetypeAlias.hpp" #include "openvic-simulation/ecs/Chunk.hpp" #include "openvic-simulation/ecs/ChunkPool.hpp" #include "openvic-simulation/ecs/ChunkView.hpp" @@ -88,6 +90,9 @@ namespace OpenVic::ecs { template EntityID create_entity(Cs&&... values); + template + EntityID create_entity_archetype(Cs&&... values); + void destroy_entity(EntityID id); bool is_alive(EntityID id) const; @@ -130,11 +135,17 @@ namespace OpenVic::ecs { template void for_each(Fn&& fn); + template Fn> + void for_each_archetype(Fn&& fn); + // Same, but the function also receives the EntityID. Useful for collecting IDs to // destroy later (you can't destroy during iteration without invalidating columns). template void for_each_with_entity(Fn&& fn); + template Fn> + void for_each_archetype_with_entity(Fn&& fn); + // Query overloads — match archetypes against Query::require_ids and reject any that // overlap Query::exclude_ids. The lambda must accept C&... matching the call site's // `Cs...` template arguments (the exclude-set isn't reflected in the call signature). @@ -153,6 +164,9 @@ namespace OpenVic::ecs { template void for_each_chunk(Fn&& fn); + template Fn> + void for_each_archetype_chunk(Fn&& fn); + template void for_each_chunk(Query const& query, Fn&& fn); @@ -469,6 +483,13 @@ namespace OpenVic::ecs { return eid; } + template + EntityID World::create_entity_archetype(Cs&&... values) { + // TODO: should probably assign the archetype for later checks, but that's runtime behavior + static_assert(std::is_same_v...>, typename A::component_tuple>, "create_entity_archetype requires the archetype's components to match"); + return create_entity(std::forward(values)...); + } + template C* World::get_component(EntityID id) { if (!is_alive(id)) { @@ -762,6 +783,14 @@ namespace OpenVic::ecs { for_each(q, std::forward(fn)); } + template Fn> + void World::for_each_archetype(Fn&& fn) { + // TODO: should probably validate the archetype, deceptively applies to other archetypes otherwise, but that's a runtime check + [this, fn](archetype_component_list) { + for_each(fn); + }(typename archetype_component_list_extract::type {}); + } + template void World::for_each_with_entity(Fn&& fn) { static_assert(sizeof...(Cs) > 0, "for_each_with_entity requires at least one component"); @@ -771,6 +800,14 @@ namespace OpenVic::ecs { for_each_with_entity(q, std::forward(fn)); } + template Fn> + void World::for_each_archetype_with_entity(Fn&& fn) { + // TODO: should probably validate the archetype, deceptively applies to other archetypes otherwise, but that's a runtime check + [this, fn](archetype_component_list) { + for_each_with_entity(fn); + }(typename archetype_component_list_extract::type {}); + } + template void World::for_each(Query const& query, Fn&& fn) { static_assert(sizeof...(Cs) > 0, "for_each requires at least one component"); @@ -828,6 +865,14 @@ namespace OpenVic::ecs { for_each_chunk(q, std::forward(fn)); } + template Fn> + void World::for_each_archetype_chunk(Fn&& fn) { + // TODO: should probably validate the archetype, deceptively applies to other archetypes otherwise, but that's a runtime check + [this, fn](archetype_component_list) { + for_each_chunk(fn); + }(typename archetype_component_list_extract::type {}); + } + template void World::for_each_chunk(Query const& query, Fn&& fn) { static_assert(sizeof...(Cs) > 0, "for_each_chunk requires at least one component");