aboutsummaryrefslogtreecommitdiff
path: root/src/ecs.lua
diff options
context:
space:
mode:
authorne_mene <[email protected]>2026-03-08 22:21:42 +0100
committerne_mene <[email protected]>2026-03-08 22:21:42 +0100
commitb744faa2e42ed37459fe3edb69c1149146233e5b (patch)
tree25f238d3e467c7cf43e619b579e0df89762b1cca /src/ecs.lua
parent95d50b15634bf4799a2005e381d82c110fbff39b (diff)
let there be light
Diffstat (limited to 'src/ecs.lua')
-rw-r--r--src/ecs.lua169
1 files changed, 169 insertions, 0 deletions
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