Quake Strogganoff: Bitflags
If there’s any important lesson of which to learn, knowing how bitflags work is probably somewhere near the top. In QuakeC, this is used to create spawnflags for entities and can also be thought of as a container of sorts to store other entities on an entity like a suitcase, only this suitcase is a float. And it can carry a maximum of 23 items. Why 23? In short, math.
For a better understanding as to why 23, see the movie “The Number 23” starring Jim Carry during his ‘I’m a serious actor phase’ which was just after his ‘character actor phase’ and right before his ‘I’m out of my fucking mind’ phase (where we currently reside at the time of writing). Seriously though, you can google bitwise operations if you want to go more in depth (and I suggest you do) however for this article, I don’t want to get bogged down in a whole ball of confusion and want to keep the focus on how to actually use them in QuakeC.
So, let’s get straight to the biscuits. In defs.qc, you will find a section that looks like this (or similar, maybe with float instead of #define):
// items
#define IT_AXE 4096
#define IT_SHOTGUN 1
#define IT_SUPER_SHOTGUN 2
#define IT_NAILGUN 4
#define IT_SUPER_NAILGUN 8
#define IT_GRENADE_LAUNCHER 16
#define IT_ROCKET_LAUNCHER 32
#define IT_LIGHTNING 64
#define IT_EXTRA_WEAPON 128
#define IT_SHELLS 256
#define IT_BULLETS 512
#define IT_ROCKETS 1024
#define IT_CELLS 2048
#define IT_ARMOR1 8192
#define IT_ARMOR2 16384
#define IT_ARMOR3 32768
#define IT_SUPERHEALTH 65536
#define IT_KEY1 131072
#define IT_KEY2 262144
#define IT_INVISIBILITY 524288
#define IT_INVULNERABILITY 1048576
#define IT_SUIT 2097152
#define IT_QUAD 4194304
If you add them all up, you’ll get 23 items that were used in the original Quake which even includes a bonus extra weapon for mods to use which is nice.
You may have noticed that IT_AXE is 4096. This is done to annoy anyone with OCD and the order of these actually aren’t important, it’s just for readability. The important pattern to note is the flag doubling each time.
These flags represent their respective items and are stored in:
.float items; // bit flags
As maybe covered before, .float is known as a field type, meaning all entities can be given any of the above items which is stored as a field float like self.items with 23 slots in the bag.
So how about putting something into self.items?
self.items = self.items | IT_ROCKET_LAUNCHER;
That will change the bit and essentially give the entity a rocket launcher (this is what happens when a player picks up an item). It’s like turning on a switch that says “the slot holding IT_ROCKET_LAUNCHER is now occupied”.
You could then check to see if the slot is occupied by seeing if self.items = self.item & IT_ROCKET_LAUNCHER is true, and if so, run some code that only the holder of a rocket launcher can do.
For fun (or not): try modifying func_button to only activate if the player is carrying a rocket launcher.
Alternatively, you can remove an item using:
self.items = self.items - (self.items & (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );
Found in SetChangeParms in client.qc. This ensures any powerups and keys don’t cross over when the player crosses over to a new level.
Doors use this as well, a gold key door is essentially a door that holds IT_KEY1 in its own self.items. When the player bumps into the door, it checks to see whether the player also has IT_KEY1 in their self.items, and if so, will open, otherwise it’ll return a message saying that a key is required. Then it removes the item from the player’s inventory.
In doors.qc under door_touch:
NOTE: other in this case is the entity that is touching the door and self.owner is the door entity itself, since doors are made up of several entities such as a trigger volume.
if ( (self.items & other.items) != self.items )
{
if (self.owner.items == IT_KEY1)
{
if (world.worldtype == 2)
{
centerprint (other, "You need the silver keycard");
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
}
else if (world.worldtype == 1)
{
centerprint (other, "You need the silver runekey");
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
}
else if (! world.worldtype)// == 0)
{
centerprint (other, "You need the silver key");
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
}
}
else
{
if (world.worldtype == 2)
{
centerprint (other, "You need the gold keycard");
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
}
else if (world.worldtype == 1)
{
centerprint (other, "You need the gold runekey");
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
}
else if (! world.worldtype) // == 0)
{
centerprint (other, "You need the gold key");
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
}
}
return;
}
other.items = other.items - self.items;
NOTE: Quake has different world types to re-use keys with different messages and models. Original source may look a bit different (as this is 1.06 recoded) but it’s all essentially doing the same thing.
In the door’s spawn code, the item is set via a spawnflag:
if (self.spawnflags & DOOR_SILVER_KEY)
self.items = IT_KEY1;
if (self.spawnflags & DOOR_GOLD_KEY)
self.items = IT_KEY2;
Door spawn flags are the same as defining items:
#define DOOR_START_OPEN 1
#define DOOR_DONT_LINK 4
#define DOOR_GOLD_KEY 8
#define DOOR_SILVER_KEY 16
#define DOOR_TOGGLE 32
Hopefully this has made some sense in terms of how items are held, given and taken and even compared between different entities for a useful purpose.
For a much more coherent and in depth article regarding bitflags, check out this article by Marco Hladik.
Happy modding!