This is a small resource that I find useful for bitmasking that I couldn’t find anywhere else. Bitmasking is basically just having a lot of true/false values in a single number. For example 511
can equate to 0b111111111
, or 9 true/false values.
Flags come in as textual representations of each bit. This allows you to essentially name each bit and have control over it.
Here’s the module:
Module
local bitmask = {}
bitmask.__index = bitmask
function getFlagValue(flags, flag)
local index = table.find(flags, flag)
if index == nil then
error("Flag " .. flag .. " not present in the bitmask")
end
return 2^(index-1)
end
function bitmask.new(...)
local mask = {}
setmetatable(mask, bitmask)
mask.flags = {}
mask.mask = 0
local tempFlags = table.pack(...)
if typeof(tempFlags[#tempFlags]) == "number" then
mask.mask = tempFlags[#tempFlags]
table.remove(tempFlags, #tempFlags)
end
tempFlags["n"] = nil
mask.flags = tempFlags
tempFlags = nil
return mask
end
function bitmask:GetMask()
return self.mask
end
function bitmask:SetMask(newMask)
self.mask = newMask
end
function bitmask:ToggleFlag(flag)
local value = getFlagValue(self.flags, flag)
self.mask = bit32.bxor(self.mask, value)
end
function bitmask:EnableFlag(flag)
local value = getFlagValue(self.flags, flag)
self.mask = bit32.bor(self.mask, value)
end
function bitmask:DisableFlag(flag)
local value = getFlagValue(self.flags, flag)
self.mask = bit32.band(self.mask, bit32.bnot(value))
end
function bitmask:HasFlag(flag)
local value = getFlagValue(self.flags, flag)
return bit32.band(self.mask, value) == value
end
function bitmask:HasAllFlags(...)
for _, flag in ipairs(table.pack(...)) do
if not self:HasFlag(flag) then return false end
end
return true
end
function bitmask:HasAnyFlags(...)
for _, flag in ipairs(table.pack(...)) do
if self:HasFlag(flag) then return true end
end
return false
end
return bitmask
To make a new bitmask do this:
local bitmask = require(path.to.bitmask)
local mask = bitmask.new("Flag1", "Flag2", "Flag3", "Etc")
The strings can be any string you want. Additionally at the end you can provide a number which is the initial mask value. I recommend using the 0b syntax which allows you to type it in binary. Remember, it’s reversed, so the last flag "Etc"
is actually binary 0b1000, and "Flag1"
is represented as 0b0001 (or just 1).
Then you have multiple functions on the bitmask itself to check which flags are on, toggle flags, and enable/disable flags.
You can override the mask with mask:SetMask(newMask)
You can get the current mask as a number with mask:GetMask()
You can toggle a flag with mask:ToggleFlag("Flag")
this will flip the current state, so if it’s currently enabled, it will disable it, and vice versa.
You can enable a flag with mask:EnableFlag("Flag")
and disable it with mask:DisableFlag("Flag")
You can check if a mask holds flag(s) in 3 ways:
- Using
mask:HasFlag("Flag")
will check for a single flag. If it’s enabled, it will return true, false otherwise. - Using
mask:HasAllFlags("Flag1", "Flag2", "Etc")
will check if the bitmask has all of the flags are enabled. - And
mask:HasAnyFlags("Flag1", "Flag2", "Etc")
will check if the bitmask has any of the flags listed.
Feel free to suggest new functions for more control, but bitmasks are a simple, yet powerful tool so I don’t want anything too overly ambitious to creep into the code.