From 695cc2dfe37853226090e101b41dedbddaf74edb Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:22 -0300 Subject: [PATCH] Add Component Negation Fixes #32 --- concord/component.lua | 4 ++++ concord/components.lua | 32 ++++++++++++++++++++++++++------ concord/pool.lua | 10 +++++++--- concord/system.lua | 25 ++++++++++++++----------- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/concord/component.lua b/concord/component.lua index d8678f0..7a9db98 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -19,6 +19,10 @@ function Component.new(name, populate) error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) end + if (string.match(name, Components.__REJECT_MATCH) ~= "") then + error("bad argument #1 to 'Component.new' (Component names can't start with '"..Components.__REJECT_PREFIX.."', got "..name..")", 2) + end + if (rawget(Components, name)) then error("bad argument #1 to 'Component.new' (ComponentClass with name '"..name.."' was already registerd)", 2) -- luacheck: ignore end diff --git a/concord/components.lua b/concord/components.lua index bf20d2d..b2743e7 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -1,12 +1,11 @@ --- Container for registered ComponentClasses -- @module Components -local PATH = (...):gsub('%.[^%.]+$', '') - -local Type = require(PATH..".type") - local Components = {} +Components.__REJECT_PREFIX = "!" +Components.__REJECT_MATCH = "^(%"..Components.__REJECT_PREFIX.."?)(.+)" + --- Returns true if the containter has the ComponentClass with the specified name -- @string name Name of the ComponentClass to check -- @treturn boolean @@ -14,22 +13,43 @@ function Components.has(name) return rawget(Components, name) and true or false end +--- Prefix a component's name with the currently set Reject Prefix +-- @string name Name of the ComponentClass to reject +-- @treturn string +function Components.reject(name) + local ok, err = Components.try(name) + + if not ok then error(err, 2) end + + return Components.__REJECT_PREFIX..name +end + --- Returns true and the ComponentClass if one was registered with the specified name -- or false and an error otherwise -- @string name Name of the ComponentClass to check +-- @boolean acceptRejected Whether to accept names prefixed with the Reject Prefix. -- @treturn boolean -- @treturn Component or error string -function Components.try(name) +-- @treturn true if acceptRejected was true and the name had the Reject Prefix, false otherwise. +function Components.try(name, acceptRejected) if type(name) ~= "string" then return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" end + local rejected = false + if acceptRejected then + local prefix + prefix, name = string.match(name, Components.__REJECT_MATCH) + + rejected = prefix ~= "" and name + end + local value = rawget(Components, name) if not value then return false, "ComponentClass '"..name.."' does not exist / was not registered" end - return true, value + return true, value, rejected end --- Returns the ComponentClass with the specified name diff --git a/concord/pool.lua b/concord/pool.lua index f1c80eb..f5cd401 100644 --- a/concord/pool.lua +++ b/concord/pool.lua @@ -30,10 +30,14 @@ end -- @tparam Entity e Entity to check -- @treturn boolean function Pool:eligible(e) - for i=#self.__filter, 1, -1 do - local component = self.__filter[i].__name + for i=#self.__filter.require, 1, -1 do + local name = self.__filter.require[i] + if not e[name] then return false end + end - if not e[component] then return false end + for i=#self.__filter.reject, 1, -1 do + local name = self.__filter.reject[i] + if e[name] then return false end end return true diff --git a/concord/system.lua b/concord/system.lua index 756b124..fe0c9fe 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -33,7 +33,7 @@ System.mt = { Utils.shallowCopy(systemClass, system) end - for name, filter in pairs(systemClass.__filter) do + for name, filter in pairs(systemClass.__filters) do local pool = Pool(name, filter) system[name] = pool @@ -46,10 +46,10 @@ System.mt = { end, } -local validateFilters = function (baseFilters) +local validateFilters = function (definition) local filters = {} - for name, componentsList in pairs(baseFilters) do + for name, componentsList in pairs(definition) do if type(name) ~= 'string' then error("invalid name for filter (string key expected, got "..type(name)..")", 3) end @@ -58,18 +58,19 @@ local validateFilters = function (baseFilters) error("invalid component list for filter '"..name.."' (table expected, got "..type(componentsList)..")", 3) end - local filter = {} + filters[name] = { require = {}, reject = {} } + for n, component in ipairs(componentsList) do - local ok, componentClass = Components.try(component) + local ok, componentClass, rejected = Components.try(component, true) if not ok then error("invalid component for filter '"..name.."' at position #"..n.." ("..componentClass..")", 3) + elseif rejected then + table.insert(filters[name].reject, rejected) + else + table.insert(filters[name].require, component) end - - filter[#filter + 1] = componentClass end - - filters[name] = filter end return filters @@ -78,9 +79,11 @@ end --- Creates a new SystemClass. -- @param table filters A table containing filters (name = {components...}) -- @treturn System A new SystemClass -function System.new(filters) +function System.new(definition) + local filters = validateFilters(definition) + local systemClass = setmetatable({ - __filter = validateFilters(filters), + __filters = filters, __name = nil, __isSystemClass = true,