local module = {} setmetatable(module, {__index=_G}) setfenv(1, module) --===== filters =====-- Filter = {} -- base filter creation function Filter.new(_, operation, tbl) local self = {} self.keys = {} self.filters = {} self.op = operation for _, v in ipairs(tbl) do if type(v) == "function" then table.insert(self.filters, v) elseif type(v) == "string" then table.insert(self.keys, v) end end return function(entity, _self) local entity = _self or entity -- able to call as . or : return Filter.check(self, entity) end end setmetatable(Filter, {__call=Filter.new}) -- base filter checking function Filter.check(self, entity) local funcname = "check" .. self.op return Filter[funcname](self, entity) end -- AND filter (all keys and subfilters must match) function Filter.AND(tbl) return Filter("AND", tbl) end function Filter.checkAND(self, entity) for _, subfilter in ipairs(self.filters) do if not subfilter(entity) then return false end end for _, key in ipairs(self.keys) do if entity[key] == nil then return false end end return true end -- OR filter (at least one key or subfilter must match) function Filter.OR(tbl) return Filter("OR", tbl) end function Filter.checkOR(self, entity) for _, subfilter in ipairs(self.filters) do if subfilter(entity) then return true end end for _, key in ipairs(self.keys) do if entity[key] ~= nil then return true end end return false end -- NAND filter (at least one key or subfilter must NOT match) function Filter.NAND(tbl) return Filter("NAND", tbl) end function Filter.checkNAND(self, entity) for _, subfilter in ipairs(self.filters) do if not subfilter(entity) then return true end end for _, key in ipairs(self.keys) do if entity[key] == nil then return true end end return false end -- NOR filter (no keys or subfilters may match) function Filter.NOR(tbl) return Filter("NOR", tbl) end function Filter.checkNOR(self, entity) for _, subfilter in ipairs(self.filters) do if subfilter(entity) then return false end end for _, key in ipairs(self.keys) do if entity[key] ~= nil then return false end end return true end --===== worlds =====-- World = {} World.__index = World function World.new(_) local self = {} self.systems = {} self.entities = {} setmetatable(self, World) return self end setmetatable(World, {__call=World.new}) local function systemLt(a, b) return (a.priority or 50) < (b.priority or 50) end function World.addSystem(self, system) assert(system.update, "systems must have an 'update' key") assert(system.filter, "systems must have a 'filter' key") system.entities = {} table.insert(self.systems, system) table.sort(self.systems, systemLt) end local function addEntityToSystem(system, entity) -- check if entity is already present if system.entities[entity] then return end if system.onAddEntity then system.onAddEntity(entity) end system.entities[entity] = true end local function removeEntityFromSystem(system, entity) -- check if entity is already absent if not system.entities[entity] then return end if system.onRemoveEntity then system.onRemoveEntity(entity) end system.entities[entity] = nil end function World.addEntity(self, entity) self.entities[entity] = true for _, system in ipairs(self.systems) do if system.filter(entity) then addEntityToSystem(system, entity) end end end function World.reconfigureEntity(self, entity) for _, system in ipairs(self.systems) do if system.filter(entity) then addEntityToSystem(system, entity) else removeEntityFromSystem(system, entity) end end end function World.removeEntity(self, entity) self.entities[entity] = nil for _, system in ipairs(self.systems) do removeEntityFromSystem(system, entity) end end function World.reconfigureAllEntities(self) for entity in pairs(self.entities) do self:reconfigureEntity(entity) end end function World.update(self, dt) for _, system in ipairs(self.systems) do for entity in pairs(system.entities) do system.update(entity, dt) end end end return module