#pragma once #include "dbc.hpp" #include #include #include #include #include #include #include #include namespace DinkyECS { typedef unsigned long Entity; using EntityMap = std::unordered_map; template struct ComponentStorage { std::vector data; std::queue free_indices; }; struct Event { int event = 0; Entity entity = 0; std::any data; }; typedef std::queue EventQueue; struct World { unsigned long entity_count = 0; std::unordered_map $components; std::unordered_map $facts; std::unordered_map $events; std::unordered_map $component_storages; std::vector $constants; Entity entity() { return ++entity_count; } void clone_into(DinkyECS::World &to_world) { to_world.$constants = $constants; to_world.$facts = $facts; to_world.entity_count = entity_count; to_world.$component_storages = $component_storages; for(auto eid : $constants) { for(const auto &[tid, eid_map] : $components) { auto &their_map = to_world.$components[tid]; if(eid_map.contains(eid)) { their_map.insert_or_assign(eid, eid_map.at(eid)); } } } } void make_constant(DinkyECS::Entity entity) { $constants.push_back(entity); } template size_t make_component() { auto &storage = component_storage_for(); size_t index; if(!storage.free_indices.empty()) { index = storage.free_indices.front(); storage.free_indices.pop(); } else { storage.data.emplace_back(); index = storage.data.size() - 1; } return index; } template ComponentStorage &component_storage_for() { auto type_index = std::type_index(typeid(Comp)); $component_storages.try_emplace(type_index, ComponentStorage{}); return std::any_cast &>( $component_storages.at(type_index)); } template EntityMap &entity_map_for() { return $components[std::type_index(typeid(Comp))]; } template EventQueue &queue_map_for() { return $events[std::type_index(typeid(Comp))]; } template void remove(Entity ent) { EntityMap &map = entity_map_for(); if(map.contains(ent)) { size_t index = map.at(ent); component_storage_for().free_indices.push(index); } map.erase(ent); } template void set_the(Comp val) { $facts.insert_or_assign(std::type_index(typeid(Comp)), val); } template Comp &get_the() { auto comp_id = std::type_index(typeid(Comp)); dbc::check($facts.contains(comp_id), fmt::format("!!!! ATTEMPT to access world fact that hasn't " "been set yet: {}", typeid(Comp).name())); // use .at to get std::out_of_range if fact not set std::any &res = $facts.at(comp_id); return std::any_cast(res); } template bool has_the() { auto comp_id = std::type_index(typeid(Comp)); return $facts.contains(comp_id); } template void set(Entity ent, Comp val) { EntityMap &map = entity_map_for(); if(has(ent)) { get(ent) = val; return; } map.insert_or_assign(ent, make_component()); get(ent) = val; } template Comp &get(Entity ent) { EntityMap &map = entity_map_for(); auto &storage = component_storage_for(); auto index = map.at(ent); return storage.data[index]; } template bool has(Entity ent) { EntityMap &map = entity_map_for(); return map.contains(ent); } template void query(std::function cb) { EntityMap &map = entity_map_for(); for(auto &[entity, index] : map) { cb(entity, get(entity)); } } template void query(std::function cb) { EntityMap &map_a = entity_map_for(); EntityMap &map_b = entity_map_for(); for(auto &[entity, index_a] : map_a) { if(map_b.contains(entity)) { cb(entity, get(entity), get(entity)); } } } template void send(Comp event, Entity entity, std::any data) { EventQueue &queue = queue_map_for(); queue.push({event, entity, data}); } template Event recv() { EventQueue &queue = queue_map_for(); Event evt = queue.front(); queue.pop(); return evt; } template bool has_event() { EventQueue &queue = queue_map_for(); return !queue.empty(); } template std::optional get_if(DinkyECS::Entity entity) { if(has(entity)) { return std::make_optional(get(entity)); } else { return std::nullopt; } } }; } // namespace DinkyECS