TALES OF INTEREST - Doom 3

Small picture of Mark
Founder & Expert Button Pusher of Teaspoon Consulting

The Doom 3 code is a treasure trove for those who read source code recreationally—it's a real-life code base, available as open source and widely regarded as high quality.

I've never worked in the games industry so, for me, reading game code is like an exotic holiday. Most games are soft real-time simulations at their core, and this makes (predictable) performance paramount. As an outsider, it's often interesting to see how programming trade-offs are made when performance is taken seriously.

Recently I thought it might be fun to see how a mouse click ultimately causes a weapon to be fired. Starting in the Win32 input layer, I found the code that handles the click, traced that through the key binding mappings to find the code that actually ran in response, and eventually landed in the fireWeapon method of the idPlayer class.

In there, the following code made me smile:

 void idPlayer::FireWeapon() {
     [... elided ...]

     if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
         if ( g_infiniteAmmo.GetBool() || weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
             AI_ATTACK_HELD = true;
             weapon.GetEntity()->BeginAttack();
             if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
                 if ( hud ) {
                     hud->UpdateSoulCube( false );
                 }
                 SelectWeapon( previousWeapon, false );
             }

The initial logic is about what you'd expect: if the weapon isn't hidden, and if it's ready to fire, check that we've got enough ammo and fire if we do. Then, suddenly, "Oh, and if there's a Soul Cube in this game, and it's the current weapon, also update that".

This made me smile because I think a lot of programmers would take issue with having the logic for a particular weapon in a particular game mixed into a generic Player class like this. Three-letter principles would be name-checked and harrumphs would be harrumphed until the code was "fixed". Maybe an observer pattern would be added and the soul-cube-specific code pulled out into a separate class.

Or, maybe if you're running at 60fps, you don't want to spend your ~16ms budget chasing pointers to callbacks just in the name of an abstract notion of software cleanliness. I have no insight into this code—maybe it really was hacked in at the last minute to the chagrin of all concerned. But I do find it refreshing in a way: it's light on abstraction, very straightforward code, and fast.