From b744faa2e42ed37459fe3edb69c1149146233e5b Mon Sep 17 00:00:00 2001 From: ne_mene Date: Sun, 8 Mar 2026 22:21:42 +0100 Subject: let there be light --- src/ecs.lua | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/ecs.lua (limited to 'src/ecs.lua') diff --git a/src/ecs.lua b/src/ecs.lua new file mode 100644 index 0000000..1df5421 --- /dev/null +++ b/src/ecs.lua @@ -0,0 +1,169 @@ + +local current_scene = nil +local compfuncs = {} + +function TAGCOMP(_) end + +function new_scene() + local newscene = { + compmask = {}, + entities = {}, + entities_size = 0, + + free_entids = {}, + free_entids_size = 0, + + killset = {}, + killq = {}, + killq_size = 0, + + -- Events + on_draw = new_event(), + on_update = new_event(), + + comp_removeq = {}, + comp_entq = {}, + comp_removeq_size = 0, + } + for comp, _ in pairs(compfuncs) do + newscene.compmask[comp] = { + sparse = {}, + dense = {}, + size = 0, + } + end + return newscene +end + +function set_scene(newscene) + current_scene = newscene +end + +function get_current_scene() + return current_scene +end + +function register_comp(name, func) + assert(compfuncs[name] == nil, "Component '"..name.."' is already registered.") + + compfuncs[name] = func +end + +function add_comp(ent, comp, ...) + assert(current_scene, "No scene up.") + assert(compfuncs[comp], "Unknown component '"..tostring(comp).."'") + assert(current_scene.entities[ent.id], "Entity "..tostring(ent.id).." doesn't exist.") + + local mask = current_scene.compmask[comp] + assert(not mask.sparse[ent.id], "Entity "..tostring(ent.id).." already has component of type '"..comp.."'.") + + mask.size = mask.size + 1 + mask.sparse[ent.id] = mask.size + mask.dense[mask.size] = ent.id + + compfuncs[comp](current_scene.entities[ent.id], ...) +end + +local function remove_comp(ent, comp) + assert(current_scene, "No scene up.") + assert(compfuncs[comp], "Unknown component '"..tostring(comp).."'") + assert(current_scene.entities[ent.id], "Entity "..tostring(ent.id).." doesn't exist.") + + local mask = current_scene.compmask[comp] + assert(mask.sparse[ent.id], "Entity "..tostring(ent.id).." does not have component of type '"..comp.."'.") + + local index = mask.sparse[ent.id] + local lastid = mask.dense[mask.size] + + mask.sparse[ent.id] = nil + mask.sparse[lastid] = index + + mask.dense[index] = mask.dense[mask.size] + mask.dense[mask.size] = nil + mask.size = mask.size - 1 +end + +function run_system(comp, func, ...) + assert(current_scene, "No scene up.") + assert(compfuncs[comp], "Unknown component '"..tostring(comp).."'") + + for _, entid in ipairs(current_scene.compmask[comp].dense) do + func(current_scene.entities[entid], ...) + end +end + +function queue_entity_kill(ent) + assert(current_scene, "No scene up.") + assert(current_scene.entities[ent.id], "Entity "..tostring(ent.id).." doesn't exist.") + + if current_scene.killset[ent.id] then + return + end + current_scene.killset[ent.id] = true + current_scene.killq_size = current_scene.killq_size + 1 + current_scene.killq[current_scene.killq_size] = ent.id +end + +function has_comp(ent, comp) + assert(current_scene, "No scene up.") + assert(compfuncs[comp], "Unknown component '"..tostring(comp).."'") + assert(current_scene.entities[ent.id], "Entity "..tostring(ent.id).." doesn't exist.") + + return current_scene.compmask[comp].sparse[ent.id] ~= nil +end + +function queue_remove_comp(ent, comp) + assert(current_scene, "No scene up.") + assert(compfuncs[comp], "Unknown component '"..tostring(comp).."'") + + if not has_comp(ent, comp) then return end + + current_scene.comp_removeq_size = current_scene.comp_removeq_size + 1 + current_scene.comp_removeq[current_scene.comp_removeq_size] = comp + current_scene.comp_entq[current_scene.comp_removeq_size] = ent +end + +function flush_scene() + assert(current_scene, "No scene up.") + + for i=1, current_scene.comp_removeq_size do + remove_comp(current_scene.comp_entq[i], current_scene.comp_removeq[i]) + end + current_scene.comp_removeq_size = 0 + + for i=current_scene.killq_size, 1, -1 do + local ent = current_scene.entities[current_scene.killq[i]] + for comp, _ in pairs(current_scene.compmask) do + if has_comp(ent, comp) then + remove_comp(ent, comp) + end + end + + current_scene.killset[ent.id] = nil + + current_scene.free_entids_size = current_scene.free_entids_size + 1 + current_scene.free_entids[current_scene.free_entids_size] = ent.id + end + current_scene.killq_size = 0 +end + +function new_entity() + assert(current_scene, "No scene up.") + + if current_scene.free_entids_size > 0 then + local entid = current_scene.free_entids[current_scene.free_entids_size] + current_scene.free_entids_size = current_scene.free_entids_size - 1 + + local ent = current_scene.entities[entid] + for key in pairs(ent) do + ent[key] = nil + end + ent.id = entid + return ent + end + local newid = current_scene.entities_size + 1 + current_scene.entities[newid] = {id = newid} + current_scene.entities_size = newid + + return current_scene.entities[newid] +end -- cgit v1.3-2-g0d8e