diff --git a/src/ecs.lua b/src/ecs.lua index 14db8fd..bb21600 100644 --- a/src/ecs.lua +++ b/src/ecs.lua @@ -1,10 +1,29 @@ -local ENTITY_STORAGE = {} -local PLACEHOLDER = {} - ecs = { } +local ALL_ENTITIES = {} + +---@alias System { callback: fun(delta: number, a: any, b: any, c: any, d: any, e: any, any), shapes: {}, keys: string[], entityCache: table } + +---@type System[] +local SYSTEMS = {} + +local PLACEHOLDER = {} + +-- TODO: Add entity to any existing systems function ecs.addEntity(entity) - ENTITY_STORAGE[entity] = true + ALL_ENTITIES[entity] = true + for _, system in pairs(SYSTEMS) do + if entityMatchesShapes(entity, system.shapes) then + system.entityCache[entity] = true + end + end +end + +function ecs.removeEntity(entity) + ALL_ENTITIES[entity] = nil + for _, system in pairs(SYSTEMS) do + system.entityCache[entity] = nil + end end ---@return any @@ -21,49 +40,15 @@ function allKeysIncluded(entity, filter) return true end -function allFiltersMatch(entity, filters) - for _, filter in pairs(filters) do - if not allKeysIncluded(entity, filter) then +function entityMatchesShapes(entity, shapes) + for _, shape in pairs(shapes) do + if not allKeysIncluded(entity, shape) then return false end end return true end ----@generic T ----@param shape T ----@return fun(): T[] -function ecs.buildEntityFilter(shape) - local filters = {shape} - return function() - -- TODO? Cache all this? - local ret = {} - for entity,_ in pairs(ENTITY_STORAGE) do - if allFiltersMatch(entity, filters) then - ret[#ret + 1] = entity; - end - end - return ret - end -end - ----@generic T ----@return fun(): T[] -function ecs.buildRequiredFields(...) - local filters = arg - return function() - -- TODO? Cache all this? - local ret = {} - for entity,_ in pairs(ENTITY_STORAGE) do - if allKeysIncluded(entity, filters) then - ret[#ret + 1] = entity; - end - end - return ret - end -end --- --@overload fun(tShape: `T`): fun(callback: fun(componentT: `T`)) - ---@generic T ---@generic U ---@generic V @@ -73,11 +58,55 @@ end ---@param vShape V? ---@param wShape W? ---@return fun(callback: fun(componentT: T, componentU: U, componentV: V, componentW: W)) -function ecs.entitiesWithShapes(tShape, uShape, vShape, wShape) +function ecs.entitiesHavingShapes(tShape, uShape, vShape, wShape) return function() end end +-- Print contents of `tbl`, with indentation. +-- `indent` sets the initial level of indentation. +function tprint (tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(" ", indent) .. k .. ": " + if type(v) == "table" then + print(formatting) + tprint(v, indent+1) + elseif type(v) == 'boolean' then + print(formatting .. tostring(v)) + else + print(formatting .. v) + end + end +end + +function addSystem(callback, keys, shapes) + SYSTEMS[#SYSTEMS + 1] = { + callback = callback, + keys = keys, + shapes = shapes, + entityCache = nil + } +end + +---@param deltaSeconds number +function ecs.update(deltaSeconds) + for _,system in pairs(SYSTEMS) do + if not system.entityCache then + system.entityCache = {} + for entity,_ in pairs(ALL_ENTITIES) do + if entityMatchesShapes(entity, system.shapes) then + system.entityCache[entity] = true + end + end + end + for entity,_ in pairs(system.entityCache) do + local keys = system.keys + system.callback(deltaSeconds, entity[keys[1]], entity[keys[2]], entity[keys[3]], entity[keys[4]], entity) + end + end +end + --- Returns a function that accepts a callback. This callback will receive one argument for each Shape provided. --- ---@generic T @@ -89,82 +118,51 @@ end ---@generic W ---@generic WKey ---@param tShape { [TKey]: T } ----@param uShape { [UKey]: U }? ----@param vShape { [VKey]: V }? ----@param wShape { [WKey]: W }? ----@return fun(callback: fun(componentT: T, componentU: U, componentV: V, componentW: W)) -function ecs.entitiesWithShapes2(tShape, uShape, vShape, wShape) - local filters = {tShape, uShape, vShape, wShape} - return function(callback) - -- TODO? Cache all this? - local ret = {} - for entity,_ in pairs(ENTITY_STORAGE) do - if allFiltersMatch(entity, filters) then - callback(entity, entity, entity, entity) - end +---@param uShape { [UKey]: U } | fun(componentT: T, any) | nil +---@param vShape { [VKey]: V } | fun(componentT: T, componentU: U, any) | nil +---@param wShape { [WKey]: W } | fun(componentT: T, componentU: U, componentV: V, any) | nil +---@param finalFunc fun(componentT: T, componentU: U, componentV: V, componentW: W, any) | nil +function ecs.entitiesWithShapes(tShape, uShape, vShape, wShape, finalFunc) + local maybeShapes = {tShape, uShape, vShape, wShape, finalFunc} + local shapes = {} + local callback + + for _,maybeShape in pairs(maybeShapes) do + if type(maybeShape) == "table" then + shapes[#shapes + 1] = maybeShape + elseif type(maybeShape) == "function" then + callback = maybeShape end - return ret end + + local keys = {} + for _,shape in pairs(shapes) do + for key,_ in pairs(shape) do + keys[#keys + 1] = key + end + end + + addSystem(callback, keys, shapes) end local f = ecs.field() ----@alias XYPair { x:number, y: number } - ----@type XYPair +---@type { x: number, y: number } local XYPair = { x = f, y = f } -local HasPosition = { position = XYPair } -local HasVelocity = { velocity = XYPair } +local Position = { position = XYPair } +local Velocity = { velocity = XYPair } -local entIt = ecs.entitiesWithShapes(HasPosition, HasVelocity) -entIt(function(hasPos, hasVel) - hasPos.position.x = hasPos.position.x + hasVel.velocity.y -end) - -local entIt2 = ecs.entitiesWithShapes2(HasPosition) -entIt2(function(position, velocity) - ---@type number - local num = 0 - num = position.x - num = velocity.z - print(num) -end) - ----@return fun(callback: fun(T)) -function ecs.entitiesWithFields(...) - local filters = {...} - return function(callback) - -- TODO? Cache all this? - for entity,_ in pairs(ENTITY_STORAGE) do - if allKeysIncluded(entity, filters) then - callback(entity) - end - end - end -end - ----@type unknown -function ecs.shape(...) - local shape = {} - for _,incomingShape in ipairs(arg) do - for k,v in pairs(incomingShape) do - shape[k] = v - end - end - return shape -end - -local exampleEntity = { +local someMovable = { position = { x = 0, y = 0 }, - velocity = { x = 1, y = 1 } + velocity = { x = 2, y = 1 } } -ecs.addEntity(exampleEntity) +ecs.addEntity(someMovable) ----@type fun(system: fun(entity: { position: XYPair, velocity: XYPair })) -local forEachPosVel = ecs.entitiesWithFields('position', 'velocity') - -forEachPosVel(function(hasVelPos) - print(hasVelPos.velocity.x) +ecs.entitiesWithShapes(Position, Velocity, function (delta, pos, vel) + print("position:") + tprint(pos, 1) + pos.x = pos.x + (vel.x * delta) + pos.y = pos.y + (vel.y * delta) end) \ No newline at end of file