I made a Filter module for funzies.
--[[
Filter.Property(name, op, value)
Filter.Tag(name)
Filter.DescendantOf(ancestor)
Filter.AncestorOf(descendant)
Filter.ChildOf(parent)
Filter.ParentOf(child)
Filter.Class(name)
Filter.ClassOf(name)
Filter.Callback(callback)
Filter.Group(...)
Filter.ComparisonOp.Equal
Filter.ComparisonOp.NotEqual
Filter.ComparisonOp.Less
Filter.ComparisonOp.LessOrEqual
Filter.ComparisonOp.Greater
Filter.ComparisonOp.GreaterOrEqual
Filter.LogicalOp.And
Filter.LogicalOp.Or
Filter.LogicalOp.Not
filter:Eval(instance)
]]
local ComparisonOps
local mtComparisonOps = {
__tostring = function(self)
return ComparisonOps[self]
end,
}
local Equal = setmetatable({}, mtComparisonOps)
local NotEqual = setmetatable({}, mtComparisonOps)
local Less = setmetatable({}, mtComparisonOps)
local LessOrEqual = setmetatable({}, mtComparisonOps)
local Greater = setmetatable({}, mtComparisonOps)
local GreaterOrEqual = setmetatable({}, mtComparisonOps)
ComparisonOps = {
[Equal] = "==",
[NotEqual] = "~=",
[Less] = "<",
[LessOrEqual] = "<=",
[Greater] = ">",
[GreaterOrEqual] = ">=",
}
local compare = {
[Equal] = function(a, b) return a == b end,
[NotEqual] = function(a, b) return a ~= b end,
[Less] = function(a, b) return a < b end,
[LessOrEqual] = function(a, b) return a <= b end,
[Greater] = function(a, b) return a > b end,
[GreaterOrEqual] = function(a, b) return a >= b end,
}
local LogicalOps
local mtLogicalOps = {
__tostring = function(self)
return LogicalOps[self]
end,
}
local And = setmetatable({}, mtLogicalOps)
local Or = setmetatable({}, mtLogicalOps)
local Not = setmetatable({}, mtLogicalOps)
LogicalOps = {
[And] = "and",
[Or] = "or",
[Not] = "not",
}
local Filter = {
ComparisonOp = {
Equal = Equal,
NotEqual = NotEqual,
Less = Less,
LessOrEqual = LessOrEqual,
Greater = Greater,
GreaterOrEqual = GreaterOrEqual,
},
LogicalOp = {
And = And,
Or = Or,
Not = Not,
},
}
local filterTypes = {}
--------------------------------
--------------------------------
local function get(t, k)
return t[k]
end
local mtFilterProperty = {
__index = {
Eval = function(self, instance)
local ok, result = pcall(get, instance, self.name)
if not ok then
return false
end
return compare[self.op](result, self.value)
end,
},
__tostring = function(self)
return string.format("v.%s %s (%s)", self.name, tostring(self.op), tostring(self.value))
end,
}
filterTypes[mtFilterProperty] = true
function Filter.Property(name, op, value)
local filter = {
name = name,
op = op,
value = value,
}
return setmetatable(filter, mtFilterProperty)
end
--------------------------------
--------------------------------
local CollectionService = game:GetService("CollectionService")
local mtFilterTag = {
__index = {
Eval = function(self, instance)
return CollectionService:HasTag(instance, self.name)
end,
},
__tostring = function(self)
return string.format("CollectionService:HasTag(v, %q)", self.name)
end,
}
filterTypes[mtFilterTag] = true
function Filter.Tag(name)
local filter = {
name = name,
}
return setmetatable(filter, mtFilterTag)
end
--------------------------------
--------------------------------
local mtFilterDescendantOf = {
__index = {
Eval = function(self, instance)
return instance:IsDescendantOf(self.instance)
end,
},
__tostring = function(self)
return string.format("v:IsDescendantOf(%s)", self.instance:GetFullName())
end,
}
filterTypes[mtFilterDescendantOf] = true
function Filter.DescendantOf(instance)
local filter = {
instance = instance,
}
return setmetatable(filter, mtFilterDescendantOf)
end
--------------------------------
--------------------------------
local mtFilterAncestorOf = {
__index = {
Eval = function(self, instance)
return instance:IsAncestorOf(self.instance)
end,
},
__tostring = function(self)
return string.format("v:IsAncestorOf(%s)", self.instance:GetFullName())
end,
}
filterTypes[mtFilterAncestorOf] = true
function Filter.AncestorOf(instance)
local filter = {
instance = instance,
}
return setmetatable(filter, mtFilterAncestorOf)
end
--------------------------------
--------------------------------
local mtFilterChildOf = {
__index = {
Eval = function(self, instance)
return instance.Parent == self.instance
end,
},
__tostring = function(self)
return string.format("v.Parent == %s", self.instance:GetFullName())
end,
}
filterTypes[mtFilterChildOf] = true
function Filter.ChildOf(instance)
local filter = {
instance = instance,
}
return setmetatable(filter, mtFilterChildOf)
end
--------------------------------
--------------------------------
local mtFilterParentOf = {
__index = {
Eval = function(self, instance)
return self.Parent == instance
end,
},
__tostring = function(self)
return string.format("%s.Parent == v", self.instance:GetFullName())
end,
}
filterTypes[mtFilterParentOf] = true
function Filter.ParentOf(instance)
local filter = {
instance = instance,
}
return setmetatable(filter, mtFilterParentOf)
end
--------------------------------
--------------------------------
local mtFilterClass = {
__index = {
Eval = function(self, instance)
return instance.ClassName == self.name
end,
},
__tostring = function(self)
return string.format("v.ClassName == %q", self.name)
end,
}
filterTypes[mtFilterClass] = true
function Filter.Class(name)
local filter = {
name = name,
}
return setmetatable(filter, mtFilterClass)
end
--------------------------------
--------------------------------
local mtFilterClassOf = {
__index = {
Eval = function(self, instance)
return instance:IsA(self.name)
end,
},
__tostring = function(self)
return string.format("v:IsA(%q)", self.name)
end,
}
filterTypes[mtFilterClassOf] = true
function Filter.ClassOf(name)
local filter = {
name = name,
}
return setmetatable(filter, mtFilterClassOf)
end
--------------------------------
--------------------------------
local mtFilterCallback = {
__index = {
Eval = function(self, instance)
return self.callback(instance)
end,
},
__tostring = function(self)
return "callback(v)"
end,
}
filterTypes[mtFilterCallback] = true
function Filter.Callback(callback)
local filter = {
callback = callback,
}
return setmetatable(filter, mtFilterCallback)
end
--------------------------------
--------------------------------
local mtFilterGroup = {
__index = {
Eval = function(self, instance)
local result = false
local op = Or
local items = self.items
local i = 1
while i <= #items do
local c = true
if items[i] == Not then
i = i + 1
if i > #items then
return false
end
c = false
end
if not filterTypes[getmetatable(items[i])] then
return false
end
if op == And then
result = result and items[i]:Eval(instance) == c
elseif op == Or then
result = result or items[i]:Eval(instance) == c
end
i = i + 1
if i < #items and not LogicalOps[items[i]] then
return false
end
op = items[i]
i = i + 1
end
if LogicalOps[items[#items]] then
return false
end
return result
end,
},
__tostring = function(self)
local items = self.items
if #items == 0 then
return "(false)"
end
local result = "("
local i = 1
while i <= #items do
if items[i] == Not then
result = result .. tostring(items[i]) .. " "
i = i + 1
if i > #items then
return false
end
end
if not filterTypes[getmetatable(items[i])] then
return "(false)"
end
result = result .. tostring(items[i])
i = i + 1
if i < #items and not LogicalOps[items[i]] then
return "(false)"
end
if LogicalOps[items[i]] then
result = result .. " " .. tostring(items[i]) .. " "
end
i = i + 1
end
if LogicalOps[items[#items]] then
return "(false)"
end
return result .. ")"
end,
}
filterTypes[mtFilterGroup] = true
function Filter.Group(...)
local filter = {
items = {},
}
local items = {...}
if #items == 0 then
error(string.format("group must contain at least one filter"))
end
local i = 1
while i <= #items do
if items[i] == Not then
filter.items[i] = items[i]
i = i + 1
if i > #items then
error(string.format("expected filter after argument #%d", i))
end
end
if not filterTypes[getmetatable(items[i])] then
error(string.format("expected filter at argument #%d", i))
end
filter.items[i] = items[i]
i = i + 1
if i < #items and not LogicalOps[items[i]] then
error(string.format("expected LogicalOp at argument #%d", i))
end
filter.items[i] = items[i]
i = i + 1
end
if LogicalOps[items[#items]] then
error(string.format("expected filter after argument #%d", #items))
end
return setmetatable(filter, mtFilterGroup)
end
--------------------------------
--------------------------------
return Filter
When used with the previous example,
local Filter = require(game.ServerStorage.Filter)
local Transparent = Filter.Property("Transparency", Filter.ComparisonOp.Less, 0.95)
local Uncollidable = Filter.Property("CanCollide", Filter.ComparisonOp.Equal, true)
local BlockRays = Filter.Tag("BlockRays")
local AllowRays = Filter.Tag("AllowRays")
local Model = Filter.DescendantOf(workspace.Model)
local propertyFilter = Filter.Group(Transparent, Filter.LogicalOp.And, Uncollidable)
local tagFilter = Filter.Group(BlockRays, Filter.LogicalOp.And, Filter.LogicalOp.Not, AllowRays)
local raycastFilter = Filter.Group(propertyFilter, Filter.LogicalOp.Or, tagFilter, Filter.LogicalOp.And, Filter.LogicalOp.Not, Model)
print(Filter.Group(Filter.LogicalOp.Not, raycastFilter))
-- (not ((v.Transparency < (0.95) and v.CanCollide == (true)) or
-- (CollectionService:HasTag(v, "BlockRays") and
-- not CollectionService:HasTag(v, "AllowRays")) and
-- not v:IsDescendantOf(Workspace.Model)))