Add Component Negation

Fixes #32
This commit is contained in:
Pablo Ariel Mayobre 2023-02-14 18:14:22 -03:00
parent 89eab3fb72
commit 695cc2dfe3
4 changed files with 51 additions and 20 deletions

View file

@ -19,6 +19,10 @@ function Component.new(name, populate)
error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2)
end 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 if (rawget(Components, name)) then
error("bad argument #1 to 'Component.new' (ComponentClass with name '"..name.."' was already registerd)", 2) -- luacheck: ignore error("bad argument #1 to 'Component.new' (ComponentClass with name '"..name.."' was already registerd)", 2) -- luacheck: ignore
end end

View file

@ -1,12 +1,11 @@
--- Container for registered ComponentClasses --- Container for registered ComponentClasses
-- @module Components -- @module Components
local PATH = (...):gsub('%.[^%.]+$', '')
local Type = require(PATH..".type")
local Components = {} local Components = {}
Components.__REJECT_PREFIX = "!"
Components.__REJECT_MATCH = "^(%"..Components.__REJECT_PREFIX.."?)(.+)"
--- Returns true if the containter has the ComponentClass with the specified name --- Returns true if the containter has the ComponentClass with the specified name
-- @string name Name of the ComponentClass to check -- @string name Name of the ComponentClass to check
-- @treturn boolean -- @treturn boolean
@ -14,22 +13,43 @@ function Components.has(name)
return rawget(Components, name) and true or false return rawget(Components, name) and true or false
end 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 --- Returns true and the ComponentClass if one was registered with the specified name
-- or false and an error otherwise -- or false and an error otherwise
-- @string name Name of the ComponentClass to check -- @string name Name of the ComponentClass to check
-- @boolean acceptRejected Whether to accept names prefixed with the Reject Prefix.
-- @treturn boolean -- @treturn boolean
-- @treturn Component or error string -- @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 if type(name) ~= "string" then
return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" return false, "ComponentsClass name is expected to be a string, got "..type(name)..")"
end 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) local value = rawget(Components, name)
if not value then if not value then
return false, "ComponentClass '"..name.."' does not exist / was not registered" return false, "ComponentClass '"..name.."' does not exist / was not registered"
end end
return true, value return true, value, rejected
end end
--- Returns the ComponentClass with the specified name --- Returns the ComponentClass with the specified name

View file

@ -30,10 +30,14 @@ end
-- @tparam Entity e Entity to check -- @tparam Entity e Entity to check
-- @treturn boolean -- @treturn boolean
function Pool:eligible(e) function Pool:eligible(e)
for i=#self.__filter, 1, -1 do for i=#self.__filter.require, 1, -1 do
local component = self.__filter[i].__name 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 end
return true return true

View file

@ -33,7 +33,7 @@ System.mt = {
Utils.shallowCopy(systemClass, system) Utils.shallowCopy(systemClass, system)
end end
for name, filter in pairs(systemClass.__filter) do for name, filter in pairs(systemClass.__filters) do
local pool = Pool(name, filter) local pool = Pool(name, filter)
system[name] = pool system[name] = pool
@ -46,10 +46,10 @@ System.mt = {
end, end,
} }
local validateFilters = function (baseFilters) local validateFilters = function (definition)
local filters = {} local filters = {}
for name, componentsList in pairs(baseFilters) do for name, componentsList in pairs(definition) do
if type(name) ~= 'string' then if type(name) ~= 'string' then
error("invalid name for filter (string key expected, got "..type(name)..")", 3) error("invalid name for filter (string key expected, got "..type(name)..")", 3)
end end
@ -58,18 +58,19 @@ local validateFilters = function (baseFilters)
error("invalid component list for filter '"..name.."' (table expected, got "..type(componentsList)..")", 3) error("invalid component list for filter '"..name.."' (table expected, got "..type(componentsList)..")", 3)
end end
local filter = {} filters[name] = { require = {}, reject = {} }
for n, component in ipairs(componentsList) do 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 if not ok then
error("invalid component for filter '"..name.."' at position #"..n.." ("..componentClass..")", 3) 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 end
filter[#filter + 1] = componentClass
end end
filters[name] = filter
end end
return filters return filters
@ -78,9 +79,11 @@ end
--- Creates a new SystemClass. --- Creates a new SystemClass.
-- @param table filters A table containing filters (name = {components...}) -- @param table filters A table containing filters (name = {components...})
-- @treturn System A new SystemClass -- @treturn System A new SystemClass
function System.new(filters) function System.new(definition)
local filters = validateFilters(definition)
local systemClass = setmetatable({ local systemClass = setmetatable({
__filter = validateFilters(filters), __filters = filters,
__name = nil, __name = nil,
__isSystemClass = true, __isSystemClass = true,