Post tutorial Report RSS Basics of SOF Deathmatch Coding

This is an official tutorial written by Keith Fuller from Raven Software in 2000. It documents how to create multiplayer mod.

Posted by on - Advanced Client Side Coding

Tutorial originally written by Raven software. Mirrored here for archival purposes.


Keith Fuller

Programmer, Raven Software

3/17/00

Basics of SOF Deathmatch Coding

Note: any time I use a word or phrase you’ll actually find in the code,

Ill try to put it in Courier font, like this

Also, something I should mention is that more than 90% of the deathmatch code is written in C++ and makes heavy use of the language’s inheritance and polymorphism features. If you want to really understand why the SOF multiplayer game code is arranged the way it is and how to properly add to it or change it, you need a firm grasp of C++.

Intro

Just about everything you need to look at can be found in the gamecpp files beginning with “dm”. Each multiplayer gametype (Standard, Arsenal, Assassin, CTF, Realistic, Control, Conquer) has its own class and its behavior is coded in its own file. In fact – and this may sound strange, but it actually makes quite a bit of sense – singleplayer mode has its own dm_none.cpp file.

The idea is that every multiplayer class is derived from the gamerules_c class and every function that a multiplayer class might need is present in gamerules_c. If it’s a function that only dm_realist needs, its in gamerules_c. If it’s a function that only dm_arsenal needs, its in gamerules_c. When you define a function in gamerules_c, you give it some default behavior. For instance, FlagCaptured() is a function that only ever has meaning when you’re playing CTF. It’s still defined in gamerules_c, but its default behavior there is to do nothing. Only when the function is overridden in the dm_ctf class does it do anything.

Why set it up this way? The following example may help clarify the value of this arrangement. Take a look at gamerules_c::checkItemSpawn(). When a map is loaded and the game code tries to spawn all of the items in a map, checkItemSpawn is called before each item is spawned to see if the current set of rules (in other words, the multiplayer gametype we’re currently playing) allows that type of item. dm_arsenal overrides it to prevent any pickups from being spawned. dm_none, also known as singleplayer, overrides it to allow all pickups to be spawned.

Now it’s a little tricky due to the fact that the global variable we use to access the current game type, gamemode_ic* dm, isn’t actually an instance of gamerules_c. It’s an instance of what’s called an interface class. The definition of class gamemode_ic lets you know what functions will be available for all deathmatch modes. When you begin a new map, the deathmatch system is initialized and our global gamemode_ic* dm sets itself up with a new instance of the current game type by calling gamemode_ic::setDMMode(). IMPORTANT: After that occurs, any time you call a function with the global variable dm you’re actually calling the same function for the current game type. So when you’re in singleplayer and you see dm->checkItemSpawn() somewhere in the code, that line is actually calling dm_none::checkItemSpawn(). If you’re playing CTF, however, that same line of code winds up calling dm_ctf::checkItemSpawn(). Sounds a little screwy, but I hope it makes sense.

A Quick Sample Mod

What if you wanted to have armor pickups always show up in realistic mode deathmatch, regardless of deathmatch flag settings? Well, the function we mentioned above, dm_real::checkItemSpawn(), is what determines which pickups are allowed in dm_real. If you look at this function’s code in gamecpp/dm_real.cpp you’ll notice these lines at the top of the function:

if ((*pickup)->GetPickupListIndex() == OBJ_CTF_FLAG)
    {
  G_FreeEdict (ent);
  return(0);
 }

This chunk of code says, “if the object we just spawned is a CTF flag, remove it from the game.” The next several lines do something fairly similar and they will be the target of this mod.

 if(dmRule_NO_ARMOR())
 {
  if ((*pickup)->GetType() == PU_ARMOR)
  {
   G_FreeEdict(ent);
   return(0);
  }
 }

Here we’re checking one of dm_real’s member functions, dmRule_NO_ARMOR(), to find out if our current deathmatch flags (dm flags) allow armor in the game. If they don’t, and the object we just spawned is armor, we remove it from the game. Next, we perform a similar check to remove any non-bullet weapons and their ammo if that’s what the current dm flags tell us to do.

if(dmRule_BULLET_WPNS_ONLY())
 {
  if ( ((*pickup)->GetType() == PU_WEAPON) && (!(*pickup)->IsBulletWpn()) ||
    ((*pickup)->GetType() == PU_AMMO) && (!(*pickup)->IsBulletAmmo()) )
  {
   G_FreeEdict(ent);
   return(0);
  }
 }

Later on in the function we replace any regular health pickups that might be in the map with medkits, and then finish up with some basic housekeeping stuff that pertains to realistic mode’s handling of weapon re-spawning.

Anyway, our original goal for this mod was to make sure that armor pickups always show up in the map. So we go back to the code we saw earlier…

if(dmRule_NO_ARMOR())
 {
  if ((*pickup)->GetType() == PU_ARMOR)
  {
   G_FreeEdict(ent);
   return(0);
  }
 }

As mentioned previously, the call to dmRule_NO_ARMOR() is what decides whether or not an armor pickup gets removed. If that rule says it’s not OK, then we proceed to check the type of item we just spawned. The item we just spawned is referred to by pickup and we find out its type by calling pickup::GetType(). If the item does turn out to be armor, then we call G_FreeEdict() on the pickup object itself and therefore it gets removed from the game. What we want to do is not check the dm flags and instead just leave this checkItemSpawn function if the object we just spawned is armor. To do that, we replace the above lines of code with the following:

if ((*pickup)->GetType() == PU_ARMOR)
{
 return 1;
}

That check for PU_ARMOR makes sure that the item we’re dealing with is, in fact, armor, and then we return 1 to let the calling code know we want to keep this item. Compile that and we’re done.

Something important to note here is that you didn’t have to change any of the code that calls checkItemSpawn and you didn’t change any of the item spawning behavior for any of the other multiplayer game types. That’s the benefit of the current C++ setup we use in SOF for deathmatch code.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: