Skip to content

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.