Bitflags
Let’s say we wanted to store the abilities of a player. We could create a set of variables like this:
/mob/player
var/can_walk = TRUE
var/can_swim = FALSE
var/can_fly = FALSE
var/can_sing = TRUE
var/can_dance = FALSE
var/can_read = TRUE
This isn’t very clean or extensible. If we added a new ability, we’d have to add a new variable. Instead, we can store all of these in a single variable using bitflags.
Bitflags are a way of storing many pieces of information in a single number.
This is a simplification, but when you assign a number to a variable, that number is stored in binary. If you write:
var/player_abilities = 41
Internally, BYOND stores the number 41 as the binary value 101001
. If we think
of every 0
and 1
in binary as representing an on/off switch, then we can
store lots of switches in a single number, by associating every binary digit
with a setting.
To be able to access each “on/off” switch in that number, we create a set of
defines. Each one represents one thing we want to toggle. We use the bitwise
left shift operator, <<
, to make reading them easier:
#define WALK (1 << 0) // 000001 = 1 in binary
#define SWIM (1 << 1) // 000010 = 2 in binary
#define FLY (1 << 2) // 000100 = 4 in binary
#define SING (1 << 3) // 001000 = 8 in binary
#define DANCE (1 << 4) // 010000 = 16 in binary
#define READ (1 << 5) // 100000 = 32 in binary
A bitwise left shift “shifts” the value 1 to each place in a binary
digit. If our binary value has 6 places, i.e. 000000
, then (1 << 0)
represents a 1
in the “zeroth” place: 000001
. Then (1 << 1)
represents a
1
in the first place: 000010
, and so on. These are still specific numbers;
we are just representing them in a unique way. For example, (1 << 3)
is equal
to 8 in base ten, and is equal to 001000
in binary.
Warning
Because of how BYOND represents numbers, a single number can only hold 24
flags. In other words, once the amount of flags you wish to represent in a
number reaches (1 << 23)
, you have run out of available places to store
flags in that variable.
The technical explanation is: BYOND has a single numeric datatype stored as 32-bit IEEE 754 floating point. Performing bitwise operations on numbers in BYOND converts the number to its integer representation, using the 24 bits of the significand in the floating point representation, and then back to floating point afterwards.
Operating on Bitflags
There will be several kinds of operations you’ll want to perform on bitflags. These operations are performed using binary arithemetic operators.
Note
This guide only describes the most common bitflag operations. For a deeper dive into binary arithmetic and more complex operators, see the Advanced Bitflags reference.
Setting and Unsetting
In order to set flags, use the OR bitwise operator, |
:
var/player_abilities = WALK | SING | READ
This “toggles” the slots for the provided flags and returns the result. In this
case, the value of player_abilities
is now the number 41, because that is the
sum of the values represented by those three individual flags.
In other words, the value of these two variables is the same:
var/alice_abilities = 41
var/brian_abilities = WALK | SING | READ
The OR bitwise operator can also be used in assignment. For example:
var/player_abilities = WALK
player_abilities |= SING
player_abilities |= READ
This results in the same value as above.
If you have a flag you wish to toggle “off”, you will use a combination of
bitwise AND (&
) and negation (~
). For example, if we wanted to remove
SING
from the bitflag above:
player_abilities &= ~SING
This removes SING
from the bitflag while keeping the other values set.
Checking
In order to see if a bitflag has a specific flag toggled, use the bitwise AND
(&
) operator in a conditional:
if(player_abilities & READ)
world << "Player can read!"
if(!(player_abilities & SING))
world << "Player can't sing!"
Important Notes
Use flags for unique settings
Bitflags should be used when it makes sense that multiple flags can be set simultaneously. For example, it would not make sense to make the following bitflag:
#define CAN_WALK (1 << 0)
#define CANNOT_WALK (1 << 1)
Because then both values can be toggled on in a single variable. Only use bitflags when it makes sense to toggle any or all of the flags simultaneously.