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 --===== levels =====-- Level = {} Level.__index = Level function Level.new(_) local self = {} self.systems = {} self.entities = {} self.nextId = 1 setmetatable(self, Level) return self end setmetatable(Level, {__call=Level.new}) local function systemLt(a, b) return (a.priority or 50) < (b.priority or 50) end function Level.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) if system.setup then system.setup(system) end end local function addEntityToSystem(system, id, entity) -- check if entity is already present if system.entities[id] then return end if system.onAddEntity then system:onAddEntity(id, entity) end system.entities[id] = true end local function removeEntityFromSystem(system, id, entity) -- check if entity is already absent if not system.entities[id] then return end if system.onRemoveEntity then system:onRemoveEntity(id, entity) end system.entities[id] = nil end function Level.addEntity(self, entity) table.insert(self.entities, entity) local id = self.nextId self.nextId = id + 1 for _, system in ipairs(self.systems) do if system.filter(entity) then addEntityToSystem(system, id, entity) end end return id end function Level.reconfigureEntity(self, id) local entity = self.entities[id] for _, system in ipairs(self.systems) do if system.filter(entity) then addEntityToSystem(system, id, entity) else removeEntityFromSystem(system, id, entity) end end end function Level.removeEntity(self, id) local entity = self.entities[id] for _, system in ipairs(self.systems) do removeEntityFromSystem(system, id, entity) end self.entities[id] = nil end function Level.reconfigureAllEntities(self) for id in ipairs(self.entities) do self:reconfigureEntity(id) end end function Level.update(self, dt, paused) local paused = paused or false for _, system in ipairs(self.systems) do if (not paused) or (paused and system.nopause) then if system.preUpdate then system:preUpdate() end if system.prepareEntity then for id in pairs(system.entities) do local entity = self.entities[id] system:prepareEntity(entity) end end for id in pairs(system.entities) do local entity = self.entities[id] system:update(entity, dt) end if system.postUpdate then system:postUpdate() end end end end return module