Flags and AND/OR Operations

Frequently, you can see operations like “if player.cmd.buttons & BT_CUSTOM1”. But what does that & do?

It follows the bit logic we discussed earlier, of course!

The ampersand (&) is an operation like plus, minus, multiply and divide, called the AND operation. It applies a certain calculation between two numbers. Unlike the math we are used to, however, these two numbers are represented in binary. The pipe (|) is also an operation between two binary numbers, called the OR operation.

What does this have to do with SRB2 code, then? The truth is, most “flags” in the game prefer to be stored this way. Those MF_SPECIAL|MF_SOLID flags you see in object definitions? The button values  BT_CUSTOM1|BT_SPIN ? They are all combinations of bits. They are also all powers of 2.

1 -> 0000 0001

2 -> 0000 0010

4 -> 0000 0100

8 -> 0000 1000

This is what makes them special. When you do an AND or OR operation for two ingame flags, you only do an operation for ONE digit on each value. This is how you can see if you have a particular flag or not.

The AND Operation

True to its name, the ampersand, or AND operator, compares each bit of the two values, bit by bit. If both bits are 1, the result’s bit is also 1. This is repeated for each digit of the two binary numbers.

0100 0011
0011 1111
________&
0000 0011

-If “A” is true and “B” is true, only THEN will “C” be true.

The AND operation retis completely harmless when used against “1”s, simply returning the result you put in. This means every bit will remain unchanged, EXCEPT for where the flag bit used to be, which will be forced into being 0, removing the flag if it existed in our number. If it wasn’t there, nothing happens.

Example: The 8 bits here are an example, the actual number is way larger (32 bits for most flag based variables)

PF_GODMODE -> 16 -> 0001 0000
player.pflags -> 23 -> 0001 0111

0001 0111
0001 0000
________&
0001 0000

From here, we can see that the player does indeed have the PF_GODMODE flag! PF_GODMODE’s value equates to the 5th bit being active (2 (5-1) equals 16), and player.pflags has its 5th bit active, as seen by the binary representation. The AND operator allows us to see this without needing to check binary values ourselves.

Another Example:

PF_AUTOBRAKE -> 8 -> 0000 1000
player.pflags -> 23 -> 0001 0111

0001 0111
0000 1000
________&
0000 0000

The player does NOT have the PF_AUTOBRAKE flag! That would make sense, as the 4th bit is not active on player.pflags.

You can see a list of all player flags here. (https://wiki.srb2.org/wiki/Constants#Internal_player_flags)

Of course, there are flags for a lot more stuff, including:

  • Object flags
  • Object flags 2
  • Object special flags
  • Mapthing flags (the ones you place in Zone Builder)
  • Player button inputs
  • Damage types
  • Frame settings
  • Render flags
  • Translucency levels
  • Sector special flags

And more! Check out the wiki page above to see their values.

A Minor Pitfall:

player.pflags & (PF_FLAG1 | PF_FLAG2) will return true if EITHER of those flags are enabled, not BOTH of them.
player.pflags & (PF_FLAG1 & PF_FLAG2) will NEVER return true.
If you wish to check if exactly ONE of multiple flags is enabled, try this:
player.pflags & (PF_FLAG1 | PF_FLAG2) == PF_FLAG1
player.pflags & (PF_FLAG1 | PF_FLAG2) == PF_FLAG2

The OR Operation

The “OR” operation, | , follows the same principle as the & operation., but instead of checking A AND B are true, it checks if A OR B are true.

0100 0011
0011 1111
________|
0111 1111

-If “A” is true or “B” is true, only THEN will “C” be true.

The | operation is much simpler when it comes to calculation. It only exists to add a flag to a number. If it already has that flag, then this operation does nothing, and is completely harmless.

If you OR a 0 and 1, you get 1. If you OR a 1 and 1, you still get 1. Therefore, the result is unaffected.

PF_AUTOBRAKE -> 8 -> 0000 1000
player.pflags -> 23 -> 0001 0111
0001 0111
0000 1000
________|
0001 1111

player.pflags now includes PF_AUTOBRAKE! If it already had it, the result would have been exactly the same.

A Minor Pitfall:

The & operation is for checking, the | operation is for setting. If you do:

player.pflags = player.pflags | PF_FLAG1

You will add the flag to yourself. However, if you do:

player.pflags = player.pflags & PF_FLAG1

Would result in you either ONLY having FLAG1 if you already had it, or NO flags if you didn’t.
This is because it assigns the result of the operation to you directly.

Negation

player.pflags = player.pflags & ~PF_FLAG1

Would result in ONLY FLAG1 being removed from you.

Let’s pretend PF_FLAG1 = 0010 0000.

Then its inverse would be ~PF_FLAG = 1101 1111