diff --git a/concord/component.lua b/component.lua similarity index 95% rename from concord/component.lua rename to component.lua index 7a81a52..068caeb 100644 --- a/concord/component.lua +++ b/component.lua @@ -1,36 +1,36 @@ -local Component = {} -Component.__index = Component - ---- Creates a new Component. --- @param populate A function that populates the Bag with values --- @return A Component object -function Component.new(populate) - local component = setmetatable({ - __populate = populate, - __inherit = inherit, - - __isComponent = true, - }, Component) - - component.__mt = {__index = component} - - return component -end - ---- Creates and initializes a new Bag. --- @param ... The values passed to the populate function --- @return A new initialized Bag -function Component:__initialize(...) - if self.__populate then - local bag = setmetatable({}, self.__mt) - self.__populate(bag, ...) - - return bag - end - - return true -end - -return setmetatable(Component, { - __call = function(_, ...) return Component.new(...) end, -}) +local Component = {} +Component.__index = Component + +--- Creates a new Component. +-- @param populate A function that populates the Bag with values +-- @return A Component object +function Component.new(populate) + local component = setmetatable({ + __populate = populate, + __inherit = inherit, + + __isComponent = true, + }, Component) + + component.__mt = {__index = component} + + return component +end + +--- Creates and initializes a new Bag. +-- @param ... The values passed to the populate function +-- @return A new initialized Bag +function Component:__initialize(...) + if self.__populate then + local bag = setmetatable({}, self.__mt) + self.__populate(bag, ...) + + return bag + end + + return true +end + +return setmetatable(Component, { + __call = function(_, ...) return Component.new(...) end, +}) diff --git a/concord/init.lua b/concord/init.lua deleted file mode 100644 index 8770362..0000000 --- a/concord/init.lua +++ /dev/null @@ -1,46 +0,0 @@ -local PATH = (...):gsub('%.init$', '') - -local Type = require(PATH..".type") - -local Concord = {} - ---- Initializes the library with some optional settings --- @param settings Table of settings: { --- useEvents Flag to overwrite love.run and use events. Defaults to false --- } --- @return Concord -function Concord.init(settings) - Concord.entity = require(PATH..".entity") - Concord.component = require(PATH..".component") - Concord.system = require(PATH..".system") - Concord.instance = require(PATH..".instance") - - if settings and settings.useEvents then - Concord.instances = {} - - Concord.addInstance = function(instance) - if not Type.isInstance(instance) then - error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) - end - - table.insert(Concord.instances, instance) - end - - Concord.removeInstance = function(instance) - if not Type.isInstance(instance) then - error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) - end - - for i, instance in ipairs(Concord.instances) do - table.remove(Concord.instances, i) - break - end - end - - love.run = require(PATH..".run") - end - - return Concord -end - -return Concord diff --git a/concord/entity.lua b/entity.lua similarity index 96% rename from concord/entity.lua rename to entity.lua index f251002..25fe897 100644 --- a/concord/entity.lua +++ b/entity.lua @@ -1,99 +1,99 @@ -local PATH = (...):gsub('%.[^%.]+$', '') - -local Type = require(PATH..".type") -local List = require(PATH..".list") - -local Entity = {} -Entity.__index = Entity - ---- Creates and initializes a new Entity. --- @return A new Entity -function Entity.new() - local e = setmetatable({ - components = {}, - removed = {}, - instances = List(), - - __isEntity = true, - }, Entity) - - return e -end - ---- Gives an Entity a component with values. --- @param component The Component to add --- @param ... The values passed to the Component --- @return self -function Entity:give(component, ...) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2) - end - - self.components[component] = component:__initialize(...) - - return self -end - ---- Removes a component from an Entity. --- @param component The Component to remove --- @return self -function Entity:remove(component) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")") - end - - self.removed[component] = true - - return self -end - ---- Checks the Entity against the pools again. --- @return self -function Entity:apply() - for i = 1, self.instances.size do - self.instances:get(i):checkEntity(self) - end - - for component, _ in pairs(self.removed) do - self.components[component] = nil - self.removed[component] = nil - end - - return self -end - ---- Destroys the Entity. --- @return self -function Entity:destroy() - for i = 1, self.instances.size do - self.instances:get(i):removeEntity(self) - end - - return self -end - ---- Gets a Component from the Entity. --- @param component The Component to get --- @return The Bag from the Component -function Entity:get(component) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:get' (Component expected, got "..type(component)..")") - end - - return self.components[component] -end - ---- Returns true if the Entity has the Component. --- @params component The Component to check against --- @return True if the entity has the Bag. False otherwise -function Entity:has(component) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:has' (Component expected, got "..type(component)..")") - end - - return self.components[component] ~= nil -end - -return setmetatable(Entity, { - __call = function(_, ...) return Entity.new(...) end, -}) +local PATH = (...):gsub('%.[^%.]+$', '') + +local Type = require(PATH..".type") +local List = require(PATH..".list") + +local Entity = {} +Entity.__index = Entity + +--- Creates and initializes a new Entity. +-- @return A new Entity +function Entity.new() + local e = setmetatable({ + components = {}, + removed = {}, + instances = List(), + + __isEntity = true, + }, Entity) + + return e +end + +--- Gives an Entity a component with values. +-- @param component The Component to add +-- @param ... The values passed to the Component +-- @return self +function Entity:give(component, ...) + if not Type.isComponent(component) then + error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2) + end + + self.components[component] = component:__initialize(...) + + return self +end + +--- Removes a component from an Entity. +-- @param component The Component to remove +-- @return self +function Entity:remove(component) + if not Type.isComponent(component) then + error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")") + end + + self.removed[component] = true + + return self +end + +--- Checks the Entity against the pools again. +-- @return self +function Entity:apply() + for i = 1, self.instances.size do + self.instances:get(i):checkEntity(self) + end + + for component, _ in pairs(self.removed) do + self.components[component] = nil + self.removed[component] = nil + end + + return self +end + +--- Destroys the Entity. +-- @return self +function Entity:destroy() + for i = 1, self.instances.size do + self.instances:get(i):removeEntity(self) + end + + return self +end + +--- Gets a Component from the Entity. +-- @param component The Component to get +-- @return The Bag from the Component +function Entity:get(component) + if not Type.isComponent(component) then + error("bad argument #1 to 'Entity:get' (Component expected, got "..type(component)..")") + end + + return self.components[component] +end + +--- Returns true if the Entity has the Component. +-- @params component The Component to check against +-- @return True if the entity has the Bag. False otherwise +function Entity:has(component) + if not Type.isComponent(component) then + error("bad argument #1 to 'Entity:has' (Component expected, got "..type(component)..")") + end + + return self.components[component] ~= nil +end + +return setmetatable(Entity, { + __call = function(_, ...) return Entity.new(...) end, +}) diff --git a/init.lua b/init.lua index 2a73dea..aff4bb4 100644 --- a/init.lua +++ b/init.lua @@ -1,3 +1,46 @@ local PATH = (...):gsub('%.init$', '') -return require(PATH..".concord") \ No newline at end of file +local Type = require(PATH..".type") + +local Concord = {} + +--- Initializes the library with some optional settings +-- @param settings Table of settings: { +-- useEvents Flag to overwrite love.run and use events. Defaults to false +-- } +-- @return Concord +function Concord.init(settings) + Concord.entity = require(PATH..".entity") + Concord.component = require(PATH..".component") + Concord.system = require(PATH..".system") + Concord.instance = require(PATH..".instance") + + if settings and settings.useEvents then + Concord.instances = {} + + Concord.addInstance = function(instance) + if not Type.isInstance(instance) then + error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) + end + + table.insert(Concord.instances, instance) + end + + Concord.removeInstance = function(instance) + if not Type.isInstance(instance) then + error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) + end + + for i, instance in ipairs(Concord.instances) do + table.remove(Concord.instances, i) + break + end + end + + love.run = require(PATH..".run") + end + + return Concord +end + +return Concord diff --git a/concord/instance.lua b/instance.lua similarity index 96% rename from concord/instance.lua rename to instance.lua index b4a75f2..7128163 100644 --- a/concord/instance.lua +++ b/instance.lua @@ -1,209 +1,209 @@ -local PATH = (...):gsub('%.[^%.]+$', '') - -local Entity = require(PATH..".entity") -local System = require(PATH..".system") -local Type = require(PATH..".type") -local List = require(PATH..".list") - -local Instance = {} -Instance.__index = Instance - ---- Creates a new Instance. --- @return The new instance -function Instance.new() - local instance = setmetatable({ - entities = List(), - systems = List(), - events = {}, - removed = {}, - - __isInstance = true, - }, Instance) - - return instance -end - ---- Adds an Entity to the Instance. --- @param e The Entity to add --- @return self -function Instance:addEntity(e) - if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:addEntity' (Entity expected, got "..type(e)..")", 2) - end - - e.instances:add(self) - self.entities:add(e) - self:checkEntity(e) - - return self -end - ---- Checks an Entity against all the systems in the Instance. --- @param e The Entity to check --- @return self -function Instance:checkEntity(e) - if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:checkEntity' (Entity expected, got "..type(e)..")", 2) - end - - for i = 1, self.systems.size do - self.systems:get(i):__check(e) - end - - return self -end - ---- Marks an Entity as removed from the Instance. --- @param e The Entity to mark --- @return self -function Instance:removeEntity(e) - if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2) - end - - self.removed[#self.removed + 1] = e - - return self -end - ---- Completely removes all marked Entities in the Instance. --- @return self -function Instance:flush() - if #self.removed > 0 then - for i = 1, #self.removed do - local e = self.removed[i] - - e.instances:remove(self) - self.entities:remove(e) - - for i = 1, self.systems.size do - self.systems:get(i):__remove(e) - end - end - - self.removed = {} - end - - return self -end - ---- Adds a System to the Instance. --- @param system The System to add --- @param eventName The Event to register to --- @param callback The function name to call. Defaults to eventName --- @param enabled If the system is enabled. Defaults to true --- @return self -function Instance:addSystem(system, eventName, callback, enabled) - if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:addSystem' (System expected, got "..type(system)..")", 2) - end - - if system.__instance and system.__instance ~= self then - error("System already in instance '" ..tostring(system.__instance).."'") - end - - if not self.systems:has(system) then - self.systems:add(system) - system.__instance = self - end - - if eventName then - self.events[eventName] = self.events[eventName] or {} - - local i = #self.events[eventName] + 1 - self.events[eventName][i] = { - system = system, - callback = callback or eventName, - enabled = enabled == nil and true or enabled, - } - end - - return self -end - ---- Enables a System in the Instance. --- @param system The System to enable --- @param eventName The Event it was registered to --- @param callback The callback it was registered with. Defaults to eventName --- @return self -function Instance:enableSystem(system, eventName, callback) - if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:enableSystem' (System expected, got "..type(system)..")", 2) - end - - return self:setSystem(system, eventName, callback, true) -end - ---- Disables a System in the Instance. --- @param system The System to disable --- @param eventName The Event it was registered to --- @param callback The callback it was registered with. Defaults to eventName --- @return self -function Instance:disableSystem(system, eventName, callback) - if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:disableSystem' (System expected, got "..type(system)..")", 2) - end - - return self:setSystem(system, eventName, callback, false) -end - ---- Sets a System 'enable' in the Instance. --- @param system The System to set --- @param eventName The Event it was registered to --- @param callback The callback it was registered with. Defaults to eventName --- @param enable The state to set it to --- @return self -function Instance:setSystem(system, eventName, callback, enable) - if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:setSystem' (System expected, got "..type(system)..")", 2) - end - - callback = callback or eventName - - if callback then - local listeners = self.events[eventName] - - if listeners then - for i = 1, #listeners do - local listener = listeners[i] - - if listener.system == system and listener.callback == callback then - listener.enabled = enable - break - end - end - end - end - - return self -end - ---- Emits an Event in the Instance. --- @param eventName The Event that should be emitted --- @param ... Parameters passed to listeners --- @return self -function Instance:emit(eventName, ...) - if not eventName or type(eventName) ~= "string" then - error("bad argument #1 to 'Instance:emit' (String expected, got "..type(eventName)..")") - end - - self:flush() - - local listeners = self.events[eventName] - - if listeners then - for i = 1, #listeners do - local listener = listeners[i] - - if listener.enabled then - listener.system[listener.callback](listener.system, ...) - end - end - end - - return self -end - -return setmetatable(Instance, { - __call = function(_, ...) return Instance.new(...) end, -}) +local PATH = (...):gsub('%.[^%.]+$', '') + +local Entity = require(PATH..".entity") +local System = require(PATH..".system") +local Type = require(PATH..".type") +local List = require(PATH..".list") + +local Instance = {} +Instance.__index = Instance + +--- Creates a new Instance. +-- @return The new instance +function Instance.new() + local instance = setmetatable({ + entities = List(), + systems = List(), + events = {}, + removed = {}, + + __isInstance = true, + }, Instance) + + return instance +end + +--- Adds an Entity to the Instance. +-- @param e The Entity to add +-- @return self +function Instance:addEntity(e) + if not Type.isEntity(e) then + error("bad argument #1 to 'Instance:addEntity' (Entity expected, got "..type(e)..")", 2) + end + + e.instances:add(self) + self.entities:add(e) + self:checkEntity(e) + + return self +end + +--- Checks an Entity against all the systems in the Instance. +-- @param e The Entity to check +-- @return self +function Instance:checkEntity(e) + if not Type.isEntity(e) then + error("bad argument #1 to 'Instance:checkEntity' (Entity expected, got "..type(e)..")", 2) + end + + for i = 1, self.systems.size do + self.systems:get(i):__check(e) + end + + return self +end + +--- Marks an Entity as removed from the Instance. +-- @param e The Entity to mark +-- @return self +function Instance:removeEntity(e) + if not Type.isEntity(e) then + error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2) + end + + self.removed[#self.removed + 1] = e + + return self +end + +--- Completely removes all marked Entities in the Instance. +-- @return self +function Instance:flush() + if #self.removed > 0 then + for i = 1, #self.removed do + local e = self.removed[i] + + e.instances:remove(self) + self.entities:remove(e) + + for i = 1, self.systems.size do + self.systems:get(i):__remove(e) + end + end + + self.removed = {} + end + + return self +end + +--- Adds a System to the Instance. +-- @param system The System to add +-- @param eventName The Event to register to +-- @param callback The function name to call. Defaults to eventName +-- @param enabled If the system is enabled. Defaults to true +-- @return self +function Instance:addSystem(system, eventName, callback, enabled) + if not Type.isSystem(system) then + error("bad argument #1 to 'Instance:addSystem' (System expected, got "..type(system)..")", 2) + end + + if system.__instance and system.__instance ~= self then + error("System already in instance '" ..tostring(system.__instance).."'") + end + + if not self.systems:has(system) then + self.systems:add(system) + system.__instance = self + end + + if eventName then + self.events[eventName] = self.events[eventName] or {} + + local i = #self.events[eventName] + 1 + self.events[eventName][i] = { + system = system, + callback = callback or eventName, + enabled = enabled == nil and true or enabled, + } + end + + return self +end + +--- Enables a System in the Instance. +-- @param system The System to enable +-- @param eventName The Event it was registered to +-- @param callback The callback it was registered with. Defaults to eventName +-- @return self +function Instance:enableSystem(system, eventName, callback) + if not Type.isSystem(system) then + error("bad argument #1 to 'Instance:enableSystem' (System expected, got "..type(system)..")", 2) + end + + return self:setSystem(system, eventName, callback, true) +end + +--- Disables a System in the Instance. +-- @param system The System to disable +-- @param eventName The Event it was registered to +-- @param callback The callback it was registered with. Defaults to eventName +-- @return self +function Instance:disableSystem(system, eventName, callback) + if not Type.isSystem(system) then + error("bad argument #1 to 'Instance:disableSystem' (System expected, got "..type(system)..")", 2) + end + + return self:setSystem(system, eventName, callback, false) +end + +--- Sets a System 'enable' in the Instance. +-- @param system The System to set +-- @param eventName The Event it was registered to +-- @param callback The callback it was registered with. Defaults to eventName +-- @param enable The state to set it to +-- @return self +function Instance:setSystem(system, eventName, callback, enable) + if not Type.isSystem(system) then + error("bad argument #1 to 'Instance:setSystem' (System expected, got "..type(system)..")", 2) + end + + callback = callback or eventName + + if callback then + local listeners = self.events[eventName] + + if listeners then + for i = 1, #listeners do + local listener = listeners[i] + + if listener.system == system and listener.callback == callback then + listener.enabled = enable + break + end + end + end + end + + return self +end + +--- Emits an Event in the Instance. +-- @param eventName The Event that should be emitted +-- @param ... Parameters passed to listeners +-- @return self +function Instance:emit(eventName, ...) + if not eventName or type(eventName) ~= "string" then + error("bad argument #1 to 'Instance:emit' (String expected, got "..type(eventName)..")") + end + + self:flush() + + local listeners = self.events[eventName] + + if listeners then + for i = 1, #listeners do + local listener = listeners[i] + + if listener.enabled then + listener.system[listener.callback](listener.system, ...) + end + end + end + + return self +end + +return setmetatable(Instance, { + __call = function(_, ...) return Instance.new(...) end, +}) diff --git a/concord/list.lua b/list.lua similarity index 95% rename from concord/list.lua rename to list.lua index b02ae05..b464a27 100644 --- a/concord/list.lua +++ b/list.lua @@ -1,75 +1,75 @@ -local List = {} -local mt = {__index = List} - ---- Creates a new List. --- @return A new list -function List.new() - return setmetatable({ - objects = {}, - pointers = {}, - size = 0, - }, mt) -end - ---- Clears the List completely. --- @return self -function List:clear() - self.objects = {} - self.pointers = {} - self.size = 0 - - return self -end - ---- Adds an object to the List. --- @param obj The object to add --- @return self -function List:add(obj) - local size = self.size + 1 - - self.objects[size] = obj - self.pointers[obj] = size - self.size = size - - return self -end - ---- Removes an object from the List. --- @param obj The object to remove --- @return self -function List:remove(obj) - local index = self.pointers[obj] - local size = self.size - - if index == size then - self.objects[size] = nil - else - local other = self.objects[size] - - self.objects[index] = other - self.pointers[other] = index - - self.objects[size] = nil - end - - self.pointers[obj] = nil - self.size = size - 1 -end - ---- Gets an object by numerical index. --- @param index The index to look at --- @return The object at the index -function List:get(index) - return self.objects[index] -end - ---- Gets if the List has the object. --- @param obj The object to search for --- @param true if the list has the object, false otherwise -function List:has(obj) - return self.pointers[obj] and true -end - -return setmetatable(List, { - __call = function() return List.new() end, -}) +local List = {} +local mt = {__index = List} + +--- Creates a new List. +-- @return A new list +function List.new() + return setmetatable({ + objects = {}, + pointers = {}, + size = 0, + }, mt) +end + +--- Clears the List completely. +-- @return self +function List:clear() + self.objects = {} + self.pointers = {} + self.size = 0 + + return self +end + +--- Adds an object to the List. +-- @param obj The object to add +-- @return self +function List:add(obj) + local size = self.size + 1 + + self.objects[size] = obj + self.pointers[obj] = size + self.size = size + + return self +end + +--- Removes an object from the List. +-- @param obj The object to remove +-- @return self +function List:remove(obj) + local index = self.pointers[obj] + local size = self.size + + if index == size then + self.objects[size] = nil + else + local other = self.objects[size] + + self.objects[index] = other + self.pointers[other] = index + + self.objects[size] = nil + end + + self.pointers[obj] = nil + self.size = size - 1 +end + +--- Gets an object by numerical index. +-- @param index The index to look at +-- @return The object at the index +function List:get(index) + return self.objects[index] +end + +--- Gets if the List has the object. +-- @param obj The object to search for +-- @param true if the list has the object, false otherwise +function List:has(obj) + return self.pointers[obj] and true +end + +return setmetatable(List, { + __call = function() return List.new() end, +}) diff --git a/concord/pool.lua b/pool.lua similarity index 95% rename from concord/pool.lua rename to pool.lua index 0faa85f..0e91a0a 100644 --- a/concord/pool.lua +++ b/pool.lua @@ -1,39 +1,39 @@ -local PATH = (...):gsub('%.[^%.]+$', '') - -local List = require(PATH..".list") - -local Pool = {} -Pool.__index = Pool - ---- Creates a new Pool --- @param name Identifier for the Pool. --- @param filter Table containing the required Components --- @return The new Pool -function Pool.new(name, filter) - local pool = setmetatable(List(), Pool) - - pool.name = name - pool.filter = filter - - pool.__isPool = true - - return pool -end - ---- Checks if an Entity is eligible for the Pool. --- @param e The Entity to check --- @return True if the entity is eligible, false otherwise -function Pool:eligible(e) - for _, component in ipairs(self.filter) do - if not e.components[component] or e.removed[component] then - return false - end - end - - return true -end - -return setmetatable(Pool, { - __index = List, - __call = function(_, ...) return Pool.new(...) end, -}) +local PATH = (...):gsub('%.[^%.]+$', '') + +local List = require(PATH..".list") + +local Pool = {} +Pool.__index = Pool + +--- Creates a new Pool +-- @param name Identifier for the Pool. +-- @param filter Table containing the required Components +-- @return The new Pool +function Pool.new(name, filter) + local pool = setmetatable(List(), Pool) + + pool.name = name + pool.filter = filter + + pool.__isPool = true + + return pool +end + +--- Checks if an Entity is eligible for the Pool. +-- @param e The Entity to check +-- @return True if the entity is eligible, false otherwise +function Pool:eligible(e) + for _, component in ipairs(self.filter) do + if not e.components[component] or e.removed[component] then + return false + end + end + + return true +end + +return setmetatable(Pool, { + __index = List, + __call = function(_, ...) return Pool.new(...) end, +}) diff --git a/concord/run.lua b/run.lua similarity index 100% rename from concord/run.lua rename to run.lua diff --git a/concord/system.lua b/system.lua similarity index 96% rename from concord/system.lua rename to system.lua index 36fbd94..9c4c89f 100644 --- a/concord/system.lua +++ b/system.lua @@ -1,171 +1,171 @@ -local PATH = (...):gsub('%.[^%.]+$', '') - -local Component = require(PATH..".component") -local Pool = require(PATH..".pool") - -local System = {} -System.mt = { - __index = System, - __call = function(systemProto, ...) - local system = setmetatable({ - __all = {}, - __pools = {}, - __instance = nil, - - __isSystem = true, - }, systemProto) - - for _, filter in pairs(systemProto.__filter) do - local pool = system:__buildPool(filter) - if not system[pool.name] then - system[pool.name] = pool - system.__pools[#system.__pools + 1] = pool - else - error("Pool with name '"..pool.name.."' already exists.") - end - end - - system:init(...) - return system - end, -} - ---- Creates a new System prototype. --- @param ... Variable amounts of filters --- @return A new System prototype -function System.new(...) - local systemProto = setmetatable({ - __filter = {...}, - }, System.mt) - systemProto.__index = systemProto - - return systemProto -end - ---- Default initialization function. --- @param ... Varags -function System:init(...) -end - ---- Builds a Pool for the System. --- @param baseFilter The 'raw' Filter --- @return A new Pool -function System:__buildPool(baseFilter) - local name = "pool" - local filter = {} - - for i, v in ipairs(baseFilter) do - if type(v) == "table" then - filter[#filter + 1] = v - elseif type(v) == "string" then - name = v - end - end - - return Pool(name, filter) -end - ---- Checks and applies an Entity to the System's pools. --- @param e The Entity to check --- @return True if the Entity was added, false if it was removed. Nil if nothing happend -function System:__check(e) - local systemHas = self:__has(e) - - for _, pool in ipairs(self.__pools) do - local poolHas = pool:has(e) - local eligible = pool:eligible(e) - - if not poolHas and eligible then - pool:add(e) - self:entityAddedTo(e, pool) - self:__tryAdd(e) - - return true - elseif poolHas and not eligible then - pool:remove(e) - self:entityRemovedFrom(e, pool) - self:__tryRemove(e) - - return false - end - end -end - ---- Removed an Entity from the System. --- @param e The Entity to remove -function System:__remove(e) - if self:__has(e) then - for _, pool in ipairs(self.__pools) do - if pool:has(e) then - pool:remove(e) - self:entityRemovedFrom(e, pool) - end - end - - self.__all[e] = nil - self:entityRemoved(e) - end -end - ---- Tries to add an Entity to the System. --- @param e The Entity to add -function System:__tryAdd(e) - if not self:__has(e) then - self.__all[e] = 0 - self:entityAdded(e) - end - - self.__all[e] = self.__all[e] + 1 -end - ---- Tries to remove an Entity from the System. --- @param e The Entity to remove -function System:__tryRemove(e) - if self:__has(e) then - self.__all[e] = self.__all[e] - 1 - - if self.__all[e] == 0 then - self.__all[e] = nil - self:entityRemoved(e) - end - end -end - ---- Returns the Instance the System is in. --- @return The Instance -function System:getInstance() - return self.__instance -end - ---- Returns if the System has the Entity. --- @param The Entity to check for --- @return True if the System has the Entity. False otherwise -function System:__has(e) - return self.__all[e] and true -end - ---- Default callback for adding an Entity. --- @param e The Entity that was added -function System:entityAdded(e) -end - ---- Default callback for adding an Entity to a pool. --- @param e The Entity that was added --- @param pool The pool the Entity was added to -function System:entityAddedTo(e, pool) -end - ---- Default callback for removing an Entity. --- @param e The Entity that was removed -function System:entityRemoved(e) -end - ---- Default callback for removing an Entity from a pool. --- @param e The Entity that was removed --- @param pool The pool the Entity was removed from -function System:entityRemovedFrom(e, pool) -end - -return setmetatable(System, { - __call = function(_, ...) return System.new(...) end, -}) +local PATH = (...):gsub('%.[^%.]+$', '') + +local Component = require(PATH..".component") +local Pool = require(PATH..".pool") + +local System = {} +System.mt = { + __index = System, + __call = function(systemProto, ...) + local system = setmetatable({ + __all = {}, + __pools = {}, + __instance = nil, + + __isSystem = true, + }, systemProto) + + for _, filter in pairs(systemProto.__filter) do + local pool = system:__buildPool(filter) + if not system[pool.name] then + system[pool.name] = pool + system.__pools[#system.__pools + 1] = pool + else + error("Pool with name '"..pool.name.."' already exists.") + end + end + + system:init(...) + return system + end, +} + +--- Creates a new System prototype. +-- @param ... Variable amounts of filters +-- @return A new System prototype +function System.new(...) + local systemProto = setmetatable({ + __filter = {...}, + }, System.mt) + systemProto.__index = systemProto + + return systemProto +end + +--- Default initialization function. +-- @param ... Varags +function System:init(...) +end + +--- Builds a Pool for the System. +-- @param baseFilter The 'raw' Filter +-- @return A new Pool +function System:__buildPool(baseFilter) + local name = "pool" + local filter = {} + + for i, v in ipairs(baseFilter) do + if type(v) == "table" then + filter[#filter + 1] = v + elseif type(v) == "string" then + name = v + end + end + + return Pool(name, filter) +end + +--- Checks and applies an Entity to the System's pools. +-- @param e The Entity to check +-- @return True if the Entity was added, false if it was removed. Nil if nothing happend +function System:__check(e) + local systemHas = self:__has(e) + + for _, pool in ipairs(self.__pools) do + local poolHas = pool:has(e) + local eligible = pool:eligible(e) + + if not poolHas and eligible then + pool:add(e) + self:entityAddedTo(e, pool) + self:__tryAdd(e) + + return true + elseif poolHas and not eligible then + pool:remove(e) + self:entityRemovedFrom(e, pool) + self:__tryRemove(e) + + return false + end + end +end + +--- Removed an Entity from the System. +-- @param e The Entity to remove +function System:__remove(e) + if self:__has(e) then + for _, pool in ipairs(self.__pools) do + if pool:has(e) then + pool:remove(e) + self:entityRemovedFrom(e, pool) + end + end + + self.__all[e] = nil + self:entityRemoved(e) + end +end + +--- Tries to add an Entity to the System. +-- @param e The Entity to add +function System:__tryAdd(e) + if not self:__has(e) then + self.__all[e] = 0 + self:entityAdded(e) + end + + self.__all[e] = self.__all[e] + 1 +end + +--- Tries to remove an Entity from the System. +-- @param e The Entity to remove +function System:__tryRemove(e) + if self:__has(e) then + self.__all[e] = self.__all[e] - 1 + + if self.__all[e] == 0 then + self.__all[e] = nil + self:entityRemoved(e) + end + end +end + +--- Returns the Instance the System is in. +-- @return The Instance +function System:getInstance() + return self.__instance +end + +--- Returns if the System has the Entity. +-- @param The Entity to check for +-- @return True if the System has the Entity. False otherwise +function System:__has(e) + return self.__all[e] and true +end + +--- Default callback for adding an Entity. +-- @param e The Entity that was added +function System:entityAdded(e) +end + +--- Default callback for adding an Entity to a pool. +-- @param e The Entity that was added +-- @param pool The pool the Entity was added to +function System:entityAddedTo(e, pool) +end + +--- Default callback for removing an Entity. +-- @param e The Entity that was removed +function System:entityRemoved(e) +end + +--- Default callback for removing an Entity from a pool. +-- @param e The Entity that was removed +-- @param pool The pool the Entity was removed from +function System:entityRemovedFrom(e, pool) +end + +return setmetatable(System, { + __call = function(_, ...) return System.new(...) end, +}) diff --git a/concord/type.lua b/type.lua similarity index 100% rename from concord/type.lua rename to type.lua