Bitflags and FTEQCC (for beginners) =================================== By Marco Hladik Last updated: 8th of August 2018 FTEQCC has quite a number of cool ways to help you work with bitflags. But first of all, you might not know how they work. Note that they exist in most if not all programming languages and are not exclusive to QuakeC in any way. There have been books written about the notation decades ago, but very few people try to apply C programming skills to QuakeC codebases, as seen on literally every QuakeC project ever. Obviously, the stock Quake codebase uses bitflags a lot. It's the key to making the inventory system work, for example. What are bitflags?! =================== They are used for making Quake's weapon system work, as I've said. You've got a datatype which consists of bytes. Those bytes consists of bits, where can be either 0 or 1. A bitflag is simply a check for a specific bit being set or unset on a variable. This makes it possible to cram a lot more information into fewer datatypes. All of the inventory system fits into one field: .items self.items = IT_SHOTGUN | IT_SUPER_NAILGUN; Is in reality just this: self.items = 1 | 8; So what's going on here? Well, let's look at it in simplefied 1-byte side-view: 1 Byte = 8 Bits 0 0 0 0 0 0 0 0 And those 8 bits can represent many decimal values. The above is 0. 0 0 0 0 0 0 0 1 ^ This example is 1. 0 0 0 0 0 0 1 0 ^ This is 2. 0 0 0 0 0 0 1 1 ^ This is 3. The explanation for how these are added together can be more easily seen with the following overlay: 128 64 32 16 8 4 2 1 0 0 0 1 0 0 1 1 == 1 + 2 + 16 = 19 Whenever a bit is positive, in the example of the Quake item system, that represents that one item is present. This means that the items have to be defined as multiples of 2. That's why the shotgun is 1, and the nailgun is 4 instead of 3. Because 3 would present that the shotgun and the super shotgun are present. Flag Definitions ================ Quake defined the values of bitflags in a `straightforward` manner: // items float IT_AXE = 4096; float IT_SHOTGUN = 1; float IT_SUPER_SHOTGUN = 2; float IT_NAILGUN = 4; float IT_SUPER_NAILGUN = 8; float IT_GRENADE_LAUNCHER = 16; float IT_ROCKET_LAUNCHER = 32; float IT_LIGHTNING = 64; float IT_EXTRA = 128; float IT_SHELLS = 256; float IT_NAILS = 512; float IT_ROCKETS = 1024; float IT_CELLS = 2048; float IT_ARMOR1 = 8192; float IT_ARMOR2 = 16384; float IT_ARMOR3 = 32768; float IT_SUPERHEALTH = 65536; float IT_KEY1 = 131072; float IT_KEY2 = 262144; float IT_INVISIBILITY = 524288; float IT_INVULNERABILITY = 1048576; float IT_SUIT = 2097152; float IT_QUAD = 4194304; If you wanted to add a new weapon it was getting kind of annoying getting a calulator out and ending up with weird, long numbers like 2147483648, which represents 0x80000000 in hex, btw. Anyway, FTEQCC adds a great new cousin to our beloved enum {} declaration! It's called enumflags {}: enumflags { IT_SHOTGUN, IT_SUPER_SHOTGUN, IT_NAILGUN, IT_SUPER_NAILGUN, IT_GRENADE_LAUNCHER, IT_ROCKET_LAUNCHER, IT_LIGHTNING, IT_SHELLS, IT_NAILS, IT_ROCKETS, IT_CELLS, IT_AXE, IT_ARMOR1, IT_ARMOR2, IT_ARMOR3, IT_SUPERHEALTH, IT_KEY1, IT_KEY2, IT_INVISIBILITY, IT_INVULNERABILITY, IT_SUIT, IT_QUAD }; The first entry has the value of 1, the second the value of 2 and after that the value of 4. All it does it represent a bit shift. Thanks to that, you no longer have to manually specify multiples of 2. What a time to be alive. However, you can also choose to do it via macros and manually specify which bits you want to use via hex. #define IT_SHOTGUN 0x1i #define IT_SUPER_SHOTGUN 0x2i #define IT_NAILGUN 0x4i #define IT_SUPER_NAILGUN 0x8i ... You get the idea. You want to specify the `i` at the end to indicate that you're working with an integer value and not a floating point one. Comparisons =========== How do you check how a `weapon` is present in our .items field? Simple: if ( self.items & IT_NAILGUN ) { // We got the the nailgun in our inventory! } ^ Checks if the bit for `4` is positive. The result will return `4` (not 1). This is helpful later when unsetting the bit using subtraction. if ( !( self.items & IT_SUPER_NAILGUN ) ) { // We do not have the super nailgun at this time. } ^ Checks if the bit for `8`, aka the super nailgun, is not positive. You get the idea! Setting an flag reliably ======================== This and the next chapter are about bitwise operations. For simplicities sake, I'll tell you the quickest and safest way of working with them. If you want to learn more, look up Bitwise Operations on your favorite wiki and or book. Check a library some time. The easiest and most reliable way, without killing the other bits is: self.items |= IT_SHOTGUN; ^ Will tell it to keep whatever was in self.items before, but make sure that the bit for IT_SHOTGUN is positive. This effectively adds a shotgun into your inventory. If you were to simply do: self.items += IT_SHOTGUN; The resulting bits would mess up if the shotgun was already present. For example, imagine you had the shotgun and super shortgun already: 128 64 32 16 8 4 2 1 0 0 0 0 0 0 1 1 == 1 + 2 = 3 An operation of `self.items += IT_SHOTGUN` would effectively do 3 + 1. That results in the decimal result of self.items being 4: 128 64 32 16 8 4 2 1 0 0 0 0 0 1 0 0 == 4 So suddenly, our shotgun and super shotgun has been taken out the inventory! This effectively replaced those two with the nailgun, as IT_NAILGUN = 4. Unsetting a flag reliably ========================= One easy to read way is the notation of: self.items -= ( self.items & IT_SUPER_NAILGUN ); Basically what this does will keep whatever was in self.items and subtract whatever the result inside the ( ) is. self.items & IT_SUPER_NAILGUN will return `8` if present. So that means it'll do: self.items -= 8; If the super nailgun is present, and do: self.items -= 0; ...if not. Which of course results in self.items not changing. A more effective alternative would be to do: self.items &= ~IT_SUPER_NAILGUN; But that's hard for most beginners to remember and to understand. I'm just trying to keep things simple here. Most QuakeC starters are not engine programmers who know a lot about bit manipulation and bitwise operations.