Mindless Games Network
News & Community
Back to Raven-Games
Welcome!
News Archive
Messageboard
Essential Files
SoF 2 Info
SoF 2 Walkthrough
SoF 2 FAQ
SoF 2 Mapping
SoF 2 Cheats
SoF 2 Gallery
SoF 2 Bulletpoints
SoF 2 Cast
SoF 1 Info
SoF Walkthrough
SoF FAQ
Articles
John Mullins
Weapons
Characters
SoF Awards
Video Cards
What if?
SoF Mailbag
SoF Cheats
SoF Quick FAQ
SoF Reviews
SoF Links
Official Links
Raven Software
Activision
id Software
Keith's Got Code!


Welcome to the first page o’ answers to all of those zany mod questions out there. First off, allow me to introduce myself…I’m Keith Fuller, a programmer at Raven Software and one of the few people on Earth lucky enough to have been on the Soldier of Fortune development team. My role on the team was that of “game coder”, which means I didn’t focus on the techy rendering stuff or the network pipeline but rather on most of the stuff comprising the actual gameplay mechanics of SOF. In other words, of all of the code in the game I’m most familiar with the game dll and the player dll, which is the code we’ve released to the general public.

I’m going to have to explain a few ground rules before I can continue. In various other public forums I have made myself available for general questions and the occasional support role, but here I’m only going to talk about code-related issues. That’s going to be a letdown for the three dozen people who emailed me in the past week asking me to give them a CD key for the game or tell them how to bypass the violence lockout. Nonetheless, that’s the way it is. Also, this isn’t the place for questions like, “How do I turn on the console” or “How do you get past the second level” or anything like that. We have several wonderful forums scattered about the Web that can help you with such issues. I’m only going to discuss code here. And now, on with the show.

QUESTION 1: Adding new weapons seems to be very different to how it worked under Quake 2 or Kingpin, could you take us through step by step how to add a new weapon with an alternate fire mode.

I’ll also include as part (b) of Question 1 another piece of mail that asked something very similar:

QUESTION 1b: How can you make John woo style dual pistols??

May as well start with the tricky ones, eh? OK, I’m going to give you the necessary information to replace one of the existing weapons, let’s say the 9mm pistol, with another weapon of your choosing, let’s say some twin pearl-handled .45’s that’d make Chow Yun Fat drool. Allow me to start off with a bit of a warning, though.

If you think you can just take the existing in-view weapon models for either of the pistols that shipped with SOF and create a “dual pistols” mode with them, I’m sorry to say that’s not going to happen. You’ll need someone to create some weapon models and animations for you if you want to perform this particular modification. If you need tips on how to do that, send me some mail and I’ll get one of our artist-types to point you in the right direction. That having been said we can get on with the tutorial.

A brief explanation of the game’s basic architecture is in order. For the sake of our discussion, there are five main parts to SOF:

1)       The client.  That’s the code on the user’s end that handles stuff like keyboard input and putting stuff on the screen. The client code is part of the SOF.exe file you probably have hanging out in c:\program files\raven\sof.

2)       The renderer dll, also known as ref_gl.dll. This code is responsible for actually drawing stuff on the screen.

3)       The server. Also part of SOF.exe, this code only runs on, you guessed it, the server’s computer. It determines what actually happens in the game and continually tells all of the player’s computers (or clients) what’s going on by sending a whole bunch of data across the LAN or Internet.

4)       The game dll, gamex86.dll. This code is loaded by the server and contains all of the game mechanics. Which body part gets hit by a bullet, which sound should be player, which damage texture should be used…all of that’s determined here.

5)       The player dll. This guy may be a little harder to understand, but here are the basics. Both the server and the client load this exact same chunk of code and call the exact same functions inside of that code so that certain information such as your ammo count and your weapon inventory is kept properly synchronized across the network during multiplayer games. Remember, the client takes your keyboard input, like the fact that you just fired the rocket launcher, sends it across the network to the server, the server calls some functions in the game dll to figure out just what that rocket launch means, and then the server sends some information back to the client, like “draw cloud of body parts at location of rocket impact.” Because network connections can be pretty laggy (No, Keith. Really? I didn’t know that.) there could be some big differences at any point in time between what the server thinks about the player and what the client thinks about the player. By managing inventory-related stuff in the player dll, though, the game dll (which loads player.dll) can call the exact same functions as the client (which also loads player.dll) and therefore the server-side code can have a picture of the player’s inventory that’s very similar to the client’s. So you’ll see a lot of code in the player dll that looks like:

if (isClient)
{
// I’ve been loaded by the client so perform some client-//related task here
}
else
{    
// I’ve been loaded by the server-side game dll so perform //some server-related task here
}

So there you have it. SOF in a nutshell. Hopefully that gives you an idea of the kinds of things we’ll need to do in the player dll and why we’ll be monkeying with it to produce our stylin’ dual .45’s.

Take a look at the file player/w_types.h. You’ll find an enumerated type that looks like this:

typedef enum
{
      SFW_EMPTYSLOT = 0,
      SFW_KNIFE,
      SFW_PISTOL2,
      SFW_PISTOL1,
      SFW_MACHINEPISTOL,
      SFW_ASSAULTRIFLE,
      SFW_SNIPER,
      SFW_AUTOSHOTGUN,
      SFW_SHOTGUN,
      SFW_MACHINEGUN,
      SFW_ROCKET,
      SFW_MICROWAVEPULSE,
      SFW_FLAMEGUN,
      SFW_HURTHAND,
      SFW_THROWHAND,
      SFW_NUM_WEAPONS
} weapons_t;

You’ll probably note that this looks a lot like the list of weapons in the game. That’s because this is the list of weapons in the game. Everywhere in the code where you see the prefix SFW_ you know the code is referring to one of the available weapons’ model or sound or ammo count or something (with the exception of SFW_HURTHAND and SFW_THROWHAND, but I’ll leave it to you to figure out what those are). An important point here is that this same list is also found in qcommon\w_types.h, meaning the client code and the game dll will also refer to this enum, so you’ll want to do something like a find-and-replace in all of the client, game, and player files swapping SFW_PISTOL1 (which is the 9mm pistol) for SFW_DUAL_PISTOLS.

A quick search for SFW_PISTOL1 reveals a fairly long list of occurrences – I’m just going to touch on the highlights.

Later on in the list of SFW_PISTOL1 references you’ll see several mentions in the player dll. First, let’s discuss the array in player\w_weapons.cpp, attackInfoExtra_t attacks[]. This array contains some useful information about the noise, accuracy, and kick of each weapon, but it also contains another important item – the attack ID. Every item in this array that begins with ATK_ refers to a specific attack. The rocket launcher, for instance, is referred to in the code as SFW_ROCKET, while its attack is referred to as ATK_ROCKET and it’s alternate attack is ATK_ROCKET_ALT. So, to continue our replacement of the 9mm with the dual pistols, you’ll want to hunt down references to ATK_PISTOL1 and replace them with something like ATK_DUAL_PISTOLS. To give our dual pistols an alternate attack, look at what’s going on with the ATK_ROCKET_ALT attack ID and add ATK_DUAL_PISTOLS_ALT. I advise starting with the main list of attack ID’s (attacks_e) found at the top of player\w_public.h.

One of the most important places that the attack ID’s are mentioned is the CWeaponInfo wInfo[] array, because it lists a bunch of important data for each attack in the game. It’s pretty self-explanatory, just make sure you check it out.

There’s some more important stuff to replace in player\w_weapons.cpp. You’ll want to change the “Pistol1” entry in the weaponInfoExtra_t extraInfo[] array to reflect the new “dual pistols” information. Incidentally, the name portion of the weaponInfoExtra_t struct is what tells the game where to look for your new twin-pistols models. So if you put “JohnWoo” in there it’ll look for your models in the ghoul\Weapon\Inview\JohnWoo folder.

Just below the extraInfo array you’ll need something like…

dualPistolsInfo               dual45s(&extraInfo[3]);
…to replace the line…
pistol1Info             glock(&extraInfo[3]);

This means you’ll have to create a new class, dualPistolsInfo. Take a look at what’s going on in the existing class, pistol1Info, and you should be able to copy that. Then, in the weaponInfo_c *weapInfo[SFW_NUM_WEAPONS] array, replace &glock with &dual45s and you’ve done a lot of the setup.

There are many more places in the code where you’ll need to replace references to SFW_PISTOL1, but most of those are pretty straightforward after this.

I hope that helps. If you get yourself the models you need and you try all of the above and still have more specific questions, send ‘em in.

QUESTION 2: I wonder if i can do somting, so i can carry more ammo and weapons.I think the limit is little to lo.Is there any way i can change it ?is there a file there i can change it in or ??

Actually, you can do this by starting the game and, under “Custom Settings”, click “Cheats Available”. But since that answer has nothing to do with code, allow me to give you one that does. Look for the int ammoMaxes[] array in player\w_inven.cpp. That lists, for each of the ammo types in the game, the maximum amount of ammo the player can carry at any given time. If you up those numbers, you can pick up ammo to your heart’s content and Mullins can become even more of a walking arsenal than he already is.

QUESTION 3: Could you point me to an example in the source or provide a snippett of code on how to detect if a projectile is hitting a wall at an angle or perpendicular. And . . . how to determine the velocity it is traveling at the time of impact.

I want a grenade launched to explode on impact on touch ONLY if the grenade is impacting at between a 30 and 90 degree angle to the normal and only if the velocity is above a certain amount.

This is a cool question, IMHO, cuz it involves a wee bit of vector mathematics, which is something of a favorite of mine.

The first part is pretty simple…finding the velocity. I’m going to assume you know how to get your hands on the edict_t* that points to the grenade in question. Once you have that, let’s call it edict_t *gren, the velocity of the object is in gren->velocity.

You want to make sure that when you set up your grenade object (when it first gets thrown/launched) you give it a touch function, a line of code that’ll look something like…

gren->touch = GrenadeTouch;

Then, in the GrenadeTouch function you’ll do all of the impact code testing for angles and such. If you look at existing touch functions you’ll see that they’re defined much like the following…

void itemAmmoTouch(edict_t *self, edict_t *other, cplane_t *plane, mtexinfo_t *surf)

In the case of our grenade, self refers to the grenade, other refers to whatever it hits (that’d be the worldspawn if you’re talking about hitting walls and floors), plane refers to the geometric properties of the polygon that was touched, and surf contains the surface properties of that polygon. If we’re talking about the angle of impact, we’re going to be concerned with plane.

Here’s the definition of the cplane_t structure…

typedef struct cplane_s
{
      vec3_t      normal;
      float      dist;
      byte      type;             // for fast side tests
      byte      signbits;         // signx + (signy<<1) + (signz<<1)
} cplane_t;

It’s called cplane_t because any given polygon is represented in the game as a plane, the geometric term for a flat surface. What we care about here is the normal field which, oddly enough, refers to the normal vector of the polygon. What is the normal vector, you might ask. That’s the vector pointing out perpendicularly from the polygon’s surface. So if the polygon you hit was a flat floor, the normal vector for that surface would point straight up [0, 0, 1].

Now, a handy little function you should become familiar with is DotProduct(), which takes two unit vectors and basically returns the cosine of the angle between them. That sentence may have been a little term-heavy, so I’ll break it down. A unit vector is a vector pointing in any direction with a length of one. Like the “up” vector, [0, 0, 1]. As for the cosine of an angle, I won’t bore you with trigonometric definitions here, but suffice it to say that the cosine of the angle between two unit vectors is a number anywhere between -1 and 1, -1 if the vectors are pointing exactly opposite each other, 0 if the vectors are exactly perpendicular to each other, and 1 if the vectors point in exactly the same direction.

You can get a unit vector from any vector in the game by calling VectorNormalize() and passing in the non-unit length vector. In the case of cplane_t’s normal, that vector is always a unit vector so you don’t have to sweat it, but for the purposes of this question we’ll consider the velocity vector of the grenade, which undoubtedly is not a unit vector. First, you’d want to make a copy of the velocity vector using VectorCopy(), then normalize it to get the unit vector in the direction of the grenade’s velocity. The code inside your grenade’s touch function might look like this:

vec3_t grenVelocity;
VectorCopy(gren->velocity, grenVelocity);
VectorNormalize(grenVelocity);

To bring this all together, you’ll want to get the unit vector for the grenade’s velocity, perform the dot product of this unit vector and the normal for the plane the grenade hit. Since the grenade will be heading toward this surface and the normal is pointing away from the surface, the result of the dot product will  be negative, so I’d say negate it (which will turn it into a positive number) to make things easier on yourself. Then compare the result with the cosine of whatever angles you’re after, in this case 0 and 60 degrees (because we’re looking at 90 and 30 degrees from the surface, not the normal). Mind you, before you do any of this, you might want to make sure that what the grenade hit is, in fact, level architecture rather than a player or something. So here’s what your grenade touch function might look like…

void GrenadeTouch(edict_t *gren, edict_t *other, cplane_t *plane, mtexinfo_t *surf)
{
       vec3_t grenVelocity;
       float dotResult = 0, cosine60 = 0.5, cosine0 = 1;
       // did we hit the world?
       if (other == &g_edicts[0])
{
              // get the unit vector for grenade’s velocity
              VectorCopy(gren->velocity, grenVelocity);
              VectorNormalize(grenVelocity);
              // get the dot product of the velocity and the normal of our impact surface
              dotResult = DotProduct(grenVelocity, surf->normal);
              // negate it so it’s easier to read
              dotResult *= -1;
              // if we impact sharply enough, detonate
              if ( (dotResult <= cosine0) && (dotResult >= cosine60) )
{
       // BLAMMO!!
}
       }
       else
       {
              // looks like I hit a player or something. maybe I should just explode.
       }
}

There you go. One point I’ll make about the above function is that you really only need to check if the dot product result is greater than or equal to the cosine of 60 degrees, because, by definition, it’ll always be less than or equal to the cosine of 0 degrees. I just left that check in there for simplicity in understanding the process.

That’s all I’ve got time for this week. I’ll try to wade through the remaining questions you folks sent me and provide more answers next time. If you need more clarification on an issue, or if you come up with more puzzlers, send ‘em my way.

Keith Fuller
Programmer
Raven Software
kfuller@ravensoft.com

© 1998-2008 Mindless Games & Entertainment. All Rights Reserved.
Soldier of Fortune™ and any associated characters are ® ™ Raven Software &/or Activision, Inc. All Rights Reserved.