Posted by: Josiwe
Date posted: Jan 06 2005 User Rating: 5 out of 5.0 | Number of views: 7883 Number of comments: 4 | Description: A fully documented exploration into the weapon_rpg class, starting with the basecombatweapon class and moving up the inheritance stack. The first part of the article disects basecombatweapon_shared. This article is aimed at intermediate to advanced coders, as well as SDK beginners and up. |
Hello World!
Welcome to my first Source SDK coding article. I'm as new at this as you are, so bare with me.
Assumptions ________________
I make several assumptions about you, the reader, before we even get started. They are as follows:
- You are familiar with C++
I was spoon-fed C++ from day 1 of my freshman year in the computer science department at Boston University. If you were not so lucky, there are (literally) hundreds of thousands of C++ tutorials and resources available on the web. If you can't find them with google, give up on making a mod immediately.
- You have access to an IDE, preferrably VS.NET 2003
I'm sorry, but I have neither the time nor the patience to figure out how to compile the source code in any other environment. My work gave me a copy of vs.net, and I'm using it. For those of you who don't have access to this IDE, kdevelop is free and maybe you could write some tutorials about your own adventures in compilation. For the rest of you, well, I just hope some kind of suprnova lightbulb goes off above your head telling you what to do. Update: Check out this forum thread for info on free compilers and IDEs.
- My word is not law
While I am a professional developer, I have never really delved into an SDK of this magnitute before and all my previous modding experience with Half-Life involved modeling, animating, and voice acting. If I make a mistake in one of these articles, I will correct it as soon as it is pointed out to me. But don't expect everything I type to be Valve Certified.
- You have access to the SDK
Get Steam, babies.
Purpose ___________
The purpose of this article is to be a learning tool for me as much as for you. I basically plan to document my initial forays into the SDK, so these articles should be viewed as more of a research tool into the overall structure and behavior of the SDK. My plan is to begin with the RPG weapon. This first part of the series deals with the basecombatweapon class, and in particular the basecombatweapon_shared header and implementation files. That being said, let's get started!
Do Re Mi ___________
As Ms. Poppins always said, the beginning is a very good place to start. With a codebase this large, often times the hardest part is knowing where to begin! So we should take our cue from the good folks at Valve, who provided a handy-dandy MyFirstMod tutorial. For my first effort I plan to delve a little deeper into the mysteries of the SDK, using MyFirstMod as a starting point. Please take a moment to read this (discouragingly short) document before we continue. Don't worry, I'll wait. I'm a webpage. I have nothing else to do except chat with Cortana.
Ok, welcome back! Let's get down to business. Follow the MyFirstMod instructions for installing the SDK and open up game_sdk.sln in Visual Studio.NET. Right off the bat you'll notice that Valve's instructions are somewhat ambiguous - "Open source file hl2_dll/weapon_rpg.cpp." It seems straightforward until you get to the IDE and notice there are 2 projects, 'client' and 'hl':

There's a lot of files in both projects, and both have a sub-folder called hl2_dll. However, if you go digging around you will see that only the hl project has a file entitled weapon_rpg.cpp.
<ü>
| | | Time for a digression. (By the way, ümlauts signify digressions from the main discussion because I really like ümlauts. Also the reason this text is inside a Code box is that wavelength won't let me use a <blockquote> tag.) What are these two projects and why should you care?
The 'client' project is the client dll which mainly deals with user interface. The 'hl' project is the server or game dll which deals with game rules such as how fast the rocket fired by the rpg travels, as well as physics and network code. Believe it or not, the single player game runs on a client/server paradigm - however instead of "serving" packets in the traditional sense, it serves game information to the client. |
This is mostly guesswork. If anyone can correct me here please do so asap - my email is at the bottom of the page.
</ü>
Since we want to modify the speed the rocket takes but also learn something about how the class hierarchy works, we'll open up hl/hl2_dll/weapon_rpg.h before we look at the .cpp file.
The first thing you'll notice is that there are a bunch of includes at the top of the file; I often find that getting down to the lowest base class and working my way up gives me a better overall understanding of the way an object graph functions. The first include is "basehlcombatweapon.h", so let's open that file before we go any further in this one, and follow the includes all the way through "basehlcombatweapon_shared.h" to "basecombatweapon_shared.h" which is one level up in the main source files folder. For those of you a little confused at this point, basecombatweapon is a Source engine class, whereas basehlcombatweapon is a half-life/counter-strike/deathmatch specific class. This header includes a number of other headers, but I feel it is sufficiently low level that I can get a grasp of the whole weapon hierarchy. I plan to guess at what included functions do, or take a quick detoür into other source files when necessary.
Basecombatweapon_shared.h _______________________________________
So here we are, delving into our first real header file. I intend to parse most of the code in this header until I have a thorough understanding of what it is intended to do, at which point I will look over the implementation in the .cpp file, after which I will head back up the stack to weapon_rpg. When I finally get there I should have a solid understanding of why I'm changing the variables Valve tells me to in MyFirstMod and how they will affect the gameplay when I'm finally done typing and get around to compiling and testing my mod.
The very first thing in this header is an empty comment telling us that the code is copywrite 1996-2005 Valve Corporation, and that the header file's purpose is:
Yeah, they left it blank. Oh well, they made a kickass game and released the SDK a couple weeks after the release. Are you telling me all of your code is documented? I know there's something you're working on at that isn't. Don't even try to tell me otherwise. If you do, you're either OCD or a liar. OR a technical writer (I pity you...)
Next we see some compiler directives. As I mentioned earlier I expect you to be familiar with C++ but even the best of us sometimes get a little rusty on these, so for this article I will go over them.
| | | #ifndef COMBATWEAPON_SHARED_H #define COMBATWEAPON_SHARED_H #ifdef _WIN32 #pragma once #endif
|
MSDN has a nice little refresher on preprocessor directives here. The first directive, #ifndef COMBATWEAPON_SHARED_H, actually encompasses the entire file. Every #if must have an accompanying #endif - scroll all the way down to the bottom of the file and you'll see what I mean. This all encompassing #if basically tells the compiler, if you haven't defined a constant called COMBATWEAPON_SHARED_H, do everything on the page, thereby compiling the combatweapon_shared class. You'll notice the very next directive defines that constant. The reason Valve does this is that countless other files reference combatweapon_shared - if the compiler had to recompile the class every time it was compiling a referencing entity, you would be compiling for hours or days. This way the class is only compiled once - the second time the compiler sees this header, it will think to itself "Oh yeah, COMBATWEAPON_SHARED_H is defined, which means that I already compiled this class, so I will skip over all this stuff."
You'll also notice that if the compiler knows about a constant called _WIN32, it needs to use #pragma once. This is a windows specific compiler directive that tells the compiler, hey, only compile this class once. It's sort of overkill, but that's Microsoft for you.
Next up we have a bunch of includes:
| | | #include "vphysics_interface.h" #include "predictable_entity.h" #include "soundflags.h" #include "weapon_parse.h" #include "baseviewmodel_shared.h" #include "weapon_proficiency.h"
|
vphysics_interface - I'd guess this header provides an interface to some part of the Havok physics engine. I don't know yet, but again there is always the option to go digging around in there and see what we can see, if necessary.
predictable_entity - I would bet this has something to do with network prediction code. That's the code used in multiplayer games to say, if someone shot a bullet at a certain velocity x milliseconds ago, it's a fair bet as to where it will be on the local machine over the next y milliseconds even though I won't be getting a packet from the server for a while. Remember that this file is called combatweapon_shared, so even though we are only interested in the single player side of the world, this class will contain useful classes and functions for multiplayer coding as well.
soundflags - Something to do with the sound system. Probably bitflag definitions for making function calls that needcertain options turned on or off.
weapon_parse - Dunno. What the hell would you parse a weapon for?
baseviewmodel_shared - Something to do with rendering the weapon model onto the player's screen.
weapon_proficiency - Not sure. Possibly used by the AI to determine how good an NPC is with a particular weapon?
Next up is a particularly persnickety snippet of code:
| | | #if defined( CLIENT_DLL ) #define CBaseCombatWeapon C_BaseCombatWeapon #endif
#if !defined( CLIENT_DLL ) extern void OnBaseCombatWeaponCreated( CBaseCombatWeapon * ); extern void OnBaseCombatWeaponDestroyed( CBaseCombatWeapon * ); #endif
|
Ah, crap. This looks complicated, doesn't it? Well it's really not that tough once you get past the intimidation factor. CLIENT_DLL is a token defined by the compiler somewhere during the compilation process, and our basecombatweapon_shared file needs to know if it's been defined for some reason. But a quick find-in-files shows there is no code snippet looking like #define CLIENT_DLL anywhere in the source! So where is this ellusive token defined?
As I was writing this part of the article, I realized I didn't know either. Then the magic of the internets swooped in to the rescue. After a quick visit to chatbear's Half-Life 2 / Source Engine / Source Coding forum, the inestimable so.Suddenly answered my question for me in this thread.
If you open up the properties window on the client project, you'll see a number of categories to choose from. Under C/C++ => Command Line you can see all the compiler command line arguments, one of which is /D "CLIENT_DLL". If you look at the same option in the hl project, you'll see /D "GAME_DLL". Basically what the #if directives are doing is deciding whether we are currently compiling the client or the server dlls.

Basecombatweapon_shared.h is used by classes in both the client and the server (hence the _shared). Since we are ultimately including it from the server file weapon_rpg.h, the CLIENT_DLL token will not be defined. As such, we can skip the first 3 lines. But if you were wondering what they do, it's pretty simple. Basically, at compile time, the compiler will replace all occurences of CBaseCombatWeapon with C_BaseCombatWeapon. By using this #definition, Valve saved themselves the hassle of writing that damned underscore all the time without compromising the client naming scheme, which defines all classes as C_ClassName.
If I'm wrong here and there is another purpose to that #define, please let me know asap.
Since we're not in the client during the weapon_rpg compilation, the phrase #if !defined( CLIENT_DLL) is true, and the compiler references the two external functions OnBaseCombatWeaponCreated and OnBaseCombatWeaponDestroyed, both of which are defined outside the hl2_dll context in the main source files for the hl project. This way, we can use those functions even though they aren't defined in hl2_dll - the linker will take care of it for us. Note that both functions take a pointer to a CBaseCombatWeapon, most likely a this pointer from the calling CBaseCombatWeapon upon creation or destruction. In fact if you take a quick peek into basecombatweapon_shared.cpp for the implementation of the class and do a find on OnBaseCombatWeaponCreated, you'll see that it does indeed pass a this pointer.
SO! We've defined our type token BASECOMBATWEAPON_SHARED_H for the compiler's benefit, included a bunch of header files, and referenced a couple important external functions. What's next?
| | | class CBasePlayer; class CBaseCombatCharacter; class IPhysicsConstraint;
|
Your initial reaction upon seeing these statements may be similar to what mine was; namely "WTF?"
These declarations are actually using an interesting feature of the c++ standard to speed up compile time. I'll quote here from a post in comp.lang.c++.moderated by David Tribble:
| | | "TRIBBLE: David R Tribble <dtribble@technologist.com>
Your compile time can also be cut dramatically by using forward
declarations of classes instead of #including class header files:
// slow way
#include "myclass.h"
...
// fast way
class MyClass;
...
Provided that your class header file only uses references and pointers
to MyClass, you don't need to #include "myclass.h" at all.
If you must #include header files, you can speed up the process somewhat
by reducing redundant #includes:
// myheader.h
...
#ifndef myclass_h // #defined in "myclass.h"
#include "myclass.h"
#endif
...
This helps if you #include several headers like "myheader.h" that all
#include "myclass.h" themselves." |
I never knew that. Well, learning is half the battle! (Well, technically, for the purposes of this article, learning is the whole battle. But no disrespect to G.I. Joe.) The three forward declarations are used instead of #including their respective header files in order to speed up the compile time. Next up is another slightly odd bit of code:
| | | #define SF_WEAPON_START_CONSTRAINED (1<<0) |
(1<<0) is a weird way of creating the value 1. Basically it is saying take the value 1 (the compiler automatically interprets this as an integer, 32-bit by default in Windows XP) and shift it left by 0 bits. I would imagine the dev team used this notation so that if they decided later on to give something else the 1 value it would be easy to change the bitshift parameter to 1, or 2, or whatever. SF_WEAPON_START_CONSTRAINED is used elsewhere in the code to determine if the spawned weapon is constrained or not. By setting the 0th bit on the spawn flags for a CBaseCombatWeapon to true (by unary ANDing the spawn flags with this oddly defined constant), we tell the game to constrain the weapon; i.e. gravity does not affect it.
<ü>
| | | I'd like to take a moment to delve a little deeper here, but since it doesn't exactly pertain to our discussion of basecombatweapon_shared.h, it's umlaut time! Take a minute to do a find-in-files on SF_WEAPON_START_CONSTRAINED. You'll see that aside from our favorite header file, it's only used in two other places - basecombatweapon_shared.cpp and basecombatweapon.cpp. In the shared implementation you'll see that it's a parameter to a function called RemoveSpawnFlag inside the implementation of CBaseCombatWeapon::Equip. That makes sense; if we're equiping the weapon we want to remove the initial spawn constraint so that the weapon can move with us. You'll see that the Equip function also removes the solid flags which render the weapon model and sets up the CBaseCombatCharacter parameter as the owner of the weapon, as well as prepping the weapon to actually fire.
If you look at the other .cpp file, you'll see that SF_WEAPON_START_CONSTRAINED is used in the CBaseCombatWeapon::FallInit function. If the weapon is constrained (i.e., if the bit is set), we do some magic with the physics engine to hold the weapon in place so that it can't move. Interestingly, the forceLimit is 10,000 lbs; I wonder if there is some way to create a force larger than that and move an object that Valve didn't mean to be moved? |
<ü>
Next we see this:
| | | #define CLIP_PERC_THRESHOLD 0.75f
|
Fairly straightforward. The clip percentage threshold is set to a float value of 0.75. What is the clip percentage threshold used for? Beats me. Time for a quick find-in-files.... Eureka! It turns out that 1.0f - CLIP_PERC_THRESHOLD is the point at which we warn the player the clip (of ammo) is running low. For those of you with an adventurous turn of character, check out hud_quickinfo.cpp line 302 for more details.
| | | typedef struct { int baseAct; int weaponAct; bool required; } acttable_t;
|
Here we see our first stuct, acttable_t. While it is simple to understand the code, what elludes us is the purpose of this struct. What is it used for? How are the two integers and the boolean used elsewhere in the code? Let's resort to our old friend, find-in-files, to see where else acttable_t is referenced.

Find in files reveals 34 references to acttable_t in 28 files. You can see that a good chunk of these references are used by weapon implementations, including our Grail, weapon_rpg. If you look in that august .cpp file you'll see the following code:
| | | acttable_t CWeaponRPG::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true },
{ ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true }, { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true },
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, true },
{ ACT_IDLE, ACT_IDLE_RPG, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_RPG, true }, { ACT_WALK, ACT_WALK_RPG, true }, { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RPG, true }, { ACT_RUN, ACT_RUN_RPG, true }, { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true }, { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true }, }; |
It's clear that these weapon files are using an array of acttable_t structures for some purpose, and this code intitializes weapon_rpg's array to include a number of behaviors or (wait for it...) activities. The first parameter in each line is a generic activity such as ACT_IDLE_STIMULATED and ACT_WALK. These are then linked in some way to weapon specific activities such as ACT_IDLE_ANGRY_RPG and ACT_WALK_RPG. If you take a look in one of the other weapon classes, for example the shotgun, these same generic activities are linked to shotgun-specific activity values like ACT_IDLE_SHOTGUN_STIMULATED and ACT_WALK_RIFLE. It's unclear at this point exactly what the boolean is used for, but note that in the struct declaration the boolean is called 'required'. All will be revealed with time, grasshopper. In fact there is an acttable_t array defined for non-weapon entities like citizenpackage and citizensuitcase; also note that for these files our boolean value is set to false.
Here's a Valve developer's comment:
Aha! So our bool instructs the game as to whether a particular animation must be available in the resource files for the weapon to operate. You see? Patience is a virtue (not to be confused with virtual).
After each of these array definition/initializations, a call is made to IMPLEMENT_ACTTABLE, a macro which is defined in basecombatweapon.h - ho ho! basecombatweapon.h declares non-shared stuff (for lack of a better word) for our CBaseCombatWeapon class, and as such we should be interested. We'll take a deeper look at this non-shared file in a later article but for now let's just examine this macro:
| | | #define IMPLEMENT_ACTTABLE(className) \ acttable_t *className::ActivityList( void ) { return m_acttable; } \ int className::ActivityListCount( void ) { return ARRAYSIZE(m_acttable); } \ |
First of all, you should know that the \ is a line-continuation marker, so the compiler treats this code snippet as one really long line. This macro expects one parameter, className, which it then uses to declare two functions - ActivityList and ActivityListCount. When this call is made from weapon_rpg, it looks like this:
| | | | IMPLEMENT_ACTTABLE(CWeaponRPG); |
The compiler translates this into the following:
| | | acttable_t *CWeaponRPG::ActivityList( void ) { return m_acttable; } int CWeaponRPG::ActivityListCount( void ) { return ARRAYSIZE(m_acttable); } |
So the macro is really a convenience method to declare two inline class methods, ActivityList, which returns a pointer to an array of activity stucts (acttable_t * m_acttable) and ActivityListCount, which returns the number of activities defined in the array. The acttable_t struct is used later in our basecombatweapon class, so keep it in mind as we proceed onto the next chunk of code from basecombatweapon_shared.h:
| | | class CHudTexture; class Color;
namespace vgui2
{ typedef unsigned long HFont; } |
Well, the forward declarations of CHudTexture and Color are familiar, so we can figure out what they do later. But the namespace typedef is more intriguing.
First of all, for a clear, consice description of the namespace keyword and its purpose, click here. Second, for a similarly excellent description of what typedef does, click here.
If you read those pages you should now understand that the above code snippet defines an HFont as an unsigned long in the vui2 namespace, and that if we wished we could reference this type like so:
| | | | vgui2::HFont myHFontVariable; |
or like so:
| | | using vgui2; HFont myHFontVariable; |
Recall from CS101 that by using an unsigned long we can cram another power of 2 into an HFont, but it can't be negative.
Next up is a large chunk of definitions that are easy to understand from a c++ perspective, but much more difficult to understand from a computer graphics frame of thought.
| | |
#define VECTOR_CONE_PRECALCULATED vec3_origin #define VECTOR_CONE_1DEGREES Vector( 0.00873, 0.00873, 0.00873 ) #define VECTOR_CONE_2DEGREES Vector( 0.01745, 0.01745, 0.01745 ) #define VECTOR_CONE_3DEGREES Vector( 0.02618, 0.02618, 0.02618 ) #define VECTOR_CONE_4DEGREES Vector( 0.03490, 0.03490, 0.03490 ) #define VECTOR_CONE_5DEGREES Vector( 0.04362, 0.04362, 0.04362 ) #define VECTOR_CONE_6DEGREES Vector( 0.05234, 0.05234, 0.05234 ) #define VECTOR_CONE_7DEGREES Vector( 0.06105, 0.06105, 0.06105 ) #define VECTOR_CONE_8DEGREES Vector( 0.06976, 0.06976, 0.06976 ) #define VECTOR_CONE_9DEGREES Vector( 0.07846, 0.07846, 0.07846 ) #define VECTOR_CONE_10DEGREES Vector( 0.08716, 0.08716, 0.08716 ) #define VECTOR_CONE_15DEGREES Vector( 0.13053, 0.13053, 0.13053 ) #define VECTOR_CONE_20DEGREES Vector( 0.17365, 0.17365, 0.17365 )
|
I'll try to put this the simplest way I can. These constants represent values that will be passed to the FireBullets function to determine the spread. They are vectors representing the angle of expansion of a cone where the point of the cone is at the muzzle of your gun and the base is expanding away in the direction of the bullet. The larger the value for x, y, and z, the faster the cone will expand and the larger the area-of-effect will be at a given range. So if you are calling FireBullets with a cone spread vector of VECTOR_CONE_1DEGREES, your gun will be very accurate. This would be good for a pistol. An AK-47, on the other hand, is much more unpredictable, especially at distance. You might pass in a value of VECTOR_CONE_5DEGREES. In fact you might implement your firing code so that the longer the user holds down the trigger, the higher the degree of the vector you pass until you reach a threshold, so that after 5 seconds all your calls to FireBullets are passing in VECTOR_CONE_10DEGREES.
Note in the comment that valve has told you how they got these values; if for some bizarre reason you need a VECTOR_CONE_40DEGREES, just get a calculator and figure out what sin(20) is. Also note that vec3_origin resolves to (0,0,0). If you pass that value in, your gun will be perfectly accurate, although the comment indicates it is also possible (and advisable) to pass in a separate precalculated trajectory to the FireBullets code; passing VECTOR_CONE_PRECALCULATED tells FireBullets you've already got a trajectory in mind and not to alter it with the spreading code.
The comments following the vector_cone definitions are confusing; I believe them to be deprecated.
Specifically, I think the first comment is supposed to be at the top of the file, as it describes the basecombatweapon class to a _t (ha ha, get it?) Also, there is no CBaseTFCombat Weapon class defined in the SDK, and I think this comment is left over from Half-Life 1 Team Fortress development. I would advise you to ignore these comments and look at what comes after them:
| | | class CBaseCombatWeapon : public CBaseAnimating { public:
|
Holy crap! Finally, we have gotten past all the preparation code and arrived at the actual class declaration. The CBaseCombatWeapon is the base weapon class used to define all the weapons in the Source engine, including Half-Life weapons like, you guessed it, the RPG. We will be examining the implementations of these declarations in the .cpp file at length, and they compromise the meat of our understanding of the weapon base class, how it works, and what we can do to it. Get ready, because here comes enlightenment.
| | | DECLARE_CLASS( CBaseCombatWeapon, CBaseAnimating ); DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE();
|
Aw crap.
Well, these functions are important, and you should learn about them. Sadly, I am at present bewildered as to what they do, other than the vague notion that they somehow tell the game what kind of class CBaseCombatWeapon is, and set it up to work with certain other parts of the SDK. They are actually at the top of my todo list, so I may come back and edit this part of the article to better explain what the heck they are. Certainly I intend, once my weapon_rpg article series is complete, to write an article about my exploration into these functions, for they are far-reaching and widespread. But for now, ignore them.
Update: I have gained some insight into what DECLARE_CLASS does. Essentially, it creates a variable BaseClass which you can use to reference your base class inside a function. It's sort of like MyBase in VB. So in our CBaseCombatWeapon class function definitions, we can use BaseClass to refer to CBaseAnimating. I'm not entirely sure but I think this is just a convenience macro. When I learn more, I'll come back and update again.
<ü>
| | | Just a quick digression here. As you know, I've mentioned that I often find it useful to go as low down the stack as possible when attempting to understand an object graph. However for the purposes of this article I had to limit my scope - going down much further than basecombatweapon would be counter-productive. But for those interested, CBaseCombatWeapon inherits from CBaseAnimating, which in turn inherits from CBaseEntity. Here is Valve's comment about CBaseEntity:
/* ========= CBaseEntity ======== All objects in the game are derived from this. a list of all CBaseEntitys is kept in gEntList ================================ */ |
</ü>
Now we arrive at more familiar territory; loads and loads of method declarations. If you don't remember exactly what the virtual keyword is used for, go brush up at MSDN because Valve uses a lot of them. That way they can pass around base class pointers to derived classes and execute derived class function implementations at runtime. (It's called dynamic binding and it's very useful.) For example, elsewhere in the SDK we could have a function that expects a pointer to a CBaseCombatWeapon. We could pass in a pointer to either an rpg or a shotgun, and the function could make a call to CBaseCombatWeapon* myBaseCombatWeapon->Spawn; the runtime would bind this function to either the rpg's spawn implementation or the shotgun's, respectively. Take a look:
| | | CBaseCombatWeapon(); virtual ~CBaseCombatWeapon();
virtual bool IsPredicted( void ) const { return false; }
virtual void Spawn( void ); virtual void Precache( void );
void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType );
|
Remember that so far we're still in the class's Public: domain. All of these functions are exposed to the outside world. We have the contstructor and the virtual destructor (as any good c++ programmer should know, ALWAYS make a base class destructor virtual). As far as I can tell, IsPredicted is used by the sound system to add certain filters to sounds. Spawn obviously deals with spawning an entity, and Precache probably deals with loading resources to memory before they are needed. MakeTracer was a bit trickier to figure out, but I followed it down to util_shared.cpp where the UTIL_tracer function is defined. A comment there says that its purpose is to make a tracer effect when you fire a weapon. I wonder what a tracer added to the RPG would look like?
| | | virtual int GetSubType( void ) { return m_iSubType; } virtual void SetSubType( int iType ) { m_iSubType = iType; }
virtual void Equip( CBaseCombatCharacter *pOwner ); virtual void Drop( const Vector &vecVelocity );
|
Equip expects a pointer to a CBaseCombatCharacter and (duh) equips the weapon to him/her. Drop expects a constant vector by reference, and as such guarantees that the vector won't be modified. Because the runtime won't have to copy the vector, the function is more performant.
| | | virtual int UpdateClientData( CBasePlayer *pPlayer ); virtual bool IsAllowedToSwitch( void ); virtual bool CanBeSelected( void ); virtual bool VisibleInWeaponSelection( void ); virtual bool HasAmmo( void );
|
UpdateClientData alerts the client about updated state information for the weapon. A quick glance shows that, at least for CBaseCombatWeapon, that is whether or not the weapon is pointing at a target. The other functions do pretty much what you would expect.
| | | virtual void SetPickupTouch( void ); virtual void DefaultTouch( CBaseEntity *pOther );
|
Slightly more mysterious, although the comments give us solid clues. SetPickupTouch uses what Valve describes as a "sleezy hack" to set a base class method pointer to a derived class method pointer. In other words, it sets CBaseEntity::Touch to some CBaseCombatWeapon method. As it turns out in the .cpp, that method is DefaultTouch, which defines the expected behavior when the weapon is touched. (FYI, if the toucher is a player, it gets picked up).
| | | virtual void SetViewModelIndex( int index = 0 ); virtual bool SendWeaponAnim( int iActivity ); virtual void SendViewModelAnim( int nSequence ); float GetViewModelSequenceDuration();
bool IsViewModelSequenceFinished( void );
|
SetViewModelIndex sets the index of the weapon's corresponding viewmodel in the viewmodel list (m_nViewModelIndex for CBaseCombatWeapon). SendWeaponAnim sets the activity that the weapon and it's viewmodel should perform next, which allows the viewmodel rendering code to create a transitional animation between whatever the weapon is doing now, and the activity requested by the function. Note that the grand list of activities is defined in an enumeration in ai_activity.h. I'm not sure about SendViewModelAnim, but I think it has to be used in conjunction with SendWeaponAnim when you are sending an animation to the player. I think it sets up the viewmodel animation to align with the interpolated animation created by SendWeaponAnim. I could be very wrong here. If I am, please notify me and I will correct this. The comments on the remaining two functions speak for themselves.
| | | virtual void SetViewModel();
virtual bool HasWeaponIdleTimeElapsed( void ); virtual void SetWeaponIdleTime( float time ); virtual float GetWeaponIdleTime( void );
|
SetViewModel does the work of actually setting the model indicated by m_nViewModelIndex, which you alter with SetViewModelIndex, as the currently displayed ViewModel. The remaining 3 functions are self-documenting.
| | | virtual bool HasAnyAmmo( void ); virtual bool HasPrimaryAmmo( void ); virtual bool HasSecondaryAmmo( void ); bool UsesPrimaryAmmo( void ); bool UsesSecondaryAmmo( void );
virtual bool CanHolster( void ) { return TRUE; }; virtual bool DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt ); virtual bool CanDeploy( void ) { return true; } virtual bool Deploy( void ); virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); virtual CBaseCombatWeapon *GetLastWeapon( void ) { return this; } virtual void SetWeaponVisible( bool visible ); virtual bool IsWeaponVisible( void ); virtual bool ReloadOrSwitchWeapons( void );
|
DefaultDeploy does not actually use the szViewModel or szWeaponModel variables in the CBaseCombatWeapon implementation, so I'm not sure what they're for. They are probably used in derived classes. What the CBaseCombatWeapon implementation of this function does do, however, is use some of the functions we just used to set the viewmodel, set the next attack time to after the deploy animation, and set the animation extension, which I don't know anything about.
ReloadOrSwitchWeapons can be understood by looking at the developer's comment in the .cpp file:
Next up, some weapon behaviour declarations:
| | | virtual void ItemPreFrame( void ); virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); virtual void ItemHolsterFrame( void ) {}; virtual void WeaponIdle( void ); virtual void HandleFireOnEmpty();
|
If you're wondering about those Think functions, so am I; we'll get to them later.
| | | virtual void CheckReload( void ); virtual void FinishReload( void ); virtual void AbortReload( void ); virtual bool Reload( void ); bool DefaultReload( int iClipSize1, int iClipSize2, int iActivity );
|
DefaultReload is the default reload behavior for the class, and does simple things like check that you have enough ammo to reload, and that you aren't already reloading. It also sends the reload animation when necessary.
| | | virtual void PrimaryAttack( void ); virtual void SecondaryAttack( void ) { return; }
virtual Activity GetPrimaryAttackActivity( void ); virtual Activity GetSecondaryAttackActivity( void ); virtual Activity GetDrawActivity( void ); virtual float GetDefaultAnimSpeed( void ) { return 1.0; }
|
GetDrawActivity returns the Activity code for drawing the weapon from the holster, also called deploying the weapon.
| | | virtual int GetBulletType( void ); virtual const Vector& GetBulletSpread( void ); virtual Vector GetBulletSpread( WeaponProficiency_t proficiency ) { return GetBulletSpread(); } virtual float GetSpreadBias( WeaponProficiency_t proficiency ) { return 1.0; } virtual float GetFireRate( void ); virtual int GetMinBurst() { return 1; } virtual int GetMaxBurst() { return 1; } virtual float GetMinRestTime() { return 0.3; } virtual float GetMaxRestTime() { return 0.6; } virtual int GetRandomBurst() { return random->RandomInt( GetMinBurst(), GetMaxBurst() ); } virtual void WeaponSound( WeaponSound_t sound_type, float soundtime = 0.0f ); virtual void StopWeaponSound( WeaponSound_t sound_type ); virtual const WeaponProficiencyInfo_t *GetProficiencyValues();
|
As far as I can tell, GetBulletType is not actually used elsewhere in the SDK. GetBulletSpread can be overloaded to return either a default VECTOR_CONE_NDEGREES vector or another VECTOR_CONE based on the proficiency parameter passed to the function, which makes sense. If you pass in a WEAPON_PROFICIENCY_POOR, you would expect a bigger spread than if you passed in WEAPON_PROFICIENCY_PERFECT. The other functions are pretty straightforward but will be covered when we get to the .cpp file.
| | | virtual bool StartSprinting( void ) { return false; }; virtual bool StopSprinting( void ) { return false; };
virtual float GetDamage( float flDistance, int iLocation ) { return 0.0; };
|
Deprecated functions left over from Team Fortress which are not used anywhere else in the code. Ignore them.
| | | virtual void SetActivity( Activity act, float duration ); inline void SetActivity( Activity eActivity ) { m_Activity = eActivity; } inline Activity GetActivity( void ) { return m_Activity; }
virtual void AddViewKick( void );
virtual char *GetDeathNoticeName( void );
|
Set the weapon's activity, optionally for a specific duration, and get the current activity; add in a kickback from the player's perspective when the weapon fires. GetDeathNoticeName is not derived in any of the weapon files, and is essentially not used. If you use it, it returns this string: "GetDeathNoticeName not implemented on client yet."
| | | CBaseCombatCharacter *GetOwner() const; void SetOwner( CBaseCombatCharacter *owner ); virtual void OnPickedUp( CBaseCombatCharacter *pNewOwner );
|
This code deals with the weapon's owner, setting it, getting it, and figuring out what to do when it has no owner and is picked up.
| | | virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) {}; virtual float CalcViewmodelBob( void ) { return 0.0f; };
|
Calculates and adds the BOB weapon, a devastatingly sarcastic and frustrated tech support worker. No wait, it figures out how the weapon should bob up and down as you run.
| | | virtual void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ); virtual void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName );
virtual bool ShouldShowControlPanels( void ) { return true; }
|
These aren't extended anywhere in the SDK, and as such are not used. I'm kind of curious as to what they were for, but I'll ignore them. There's too much other stuff to think about.
| | | void Lock( float lockTime, CBaseEntity *pLocker ); bool IsLocked( CBaseEntity *pAsker );
|
Exclusively lock a weapon for a certain amount of time. Note that this "lock" seems to depend on your own courtesy - i.e. if you write code that accesses a weapon without first checking IsLocked, you might get in trouble.
| | | public:
const FileWeaponInfo_t &GetWpnData( void ) const; virtual const char *GetViewModel( int viewmodelindex = 0 ) const; virtual const char *GetWorldModel( void ) const; virtual const char *GetAnimPrefix( void ) const; virtual int GetMaxClip1( void ) const; virtual int GetMaxClip2( void ) const; virtual int GetDefaultClip1( void ) const; virtual int GetDefaultClip2( void ) const; virtual int GetWeight( void ) const; virtual bool AllowsAutoSwitchTo( void ) const; virtual bool AllowsAutoSwitchFrom( void ) const; virtual int GetWeaponFlags( void ) const; virtual int GetSlot( void ) const; virtual int GetPosition( void ) const; virtual char const *GetName( void ) const; virtual char const *GetPrintName( void ) const; virtual char const *GetShootSound( int iIndex ) const; virtual bool UsesClipsForAmmo1( void ) const; virtual bool UsesClipsForAmmo2( void ) const; bool IsMeleeWeapon() const;
|
GetWpnData - AHA! Now I know what weapon_parse is for. It contains utility functions to parse weapon.txt files for weapon data and load it into the weapon info database. The other functions return specific subsets of that data such as which model files it uses, the HUD slot and position, sounds, clip sizes, etc.
| | | virtual const unsigned char *GetEncryptionKey( void ) { return NULL; }
|
I'd guess this was added so that people couln't steal cstrike weapon definitions for their own mod, or something. After all, goose and his team put in all that hard work, right? Why let someone else profit from their intellectual property.
| | | virtual int GetPrimaryAmmoType( void ) const { return m_iPrimaryAmmoType; } virtual int GetSecondaryAmmoType( void ) const { return m_iSecondaryAmmoType; } int Clip1() const { return m_iClip1; } int Clip2() const { return m_iClip2; }
int GetPrimaryAmmoCount() { return m_iPrimaryAmmoCount; } void SetPrimaryAmmoCount( int count ) { m_iPrimaryAmmoCount = count; }
int GetSecondaryAmmoCount() { return m_iSecondaryAmmoCount; } void SetSecondaryAmmoCount( int count ) { m_iSecondaryAmmoCount = count; }
virtual CHudTexture const *GetSpriteActive( void ) const; virtual CHudTexture const *GetSpriteInactive( void ) const; virtual CHudTexture const *GetSpriteAmmo( void ) const; virtual CHudTexture const *GetSpriteAmmo2( void ) const; virtual CHudTexture const *GetSpriteCrosshair( void ) const; virtual CHudTexture const *GetSpriteAutoaim( void ) const; virtual CHudTexture const *GetSpriteZoomedCrosshair( void ) const; virtual CHudTexture const *GetSpriteZoomedAutoaim( void ) const;
|
Self-explanatory. A CHudTexture contains information about textures used to draw the HUD (Head's Up Display) as well as methods to draw itself.
| | | public:
#if !defined( CLIENT_DLL )
DECLARE_DATADESC();
|
Another public: domain, starting off with an extrememly difficult-to-nail-down macro. Fortunately, you've got me to do the gruntwork for you. DECLARE_DATADESC is #defined in datamap.h:
| | | #define DECLARE_SIMPLE_DATADESC() \ static datamap_t m_DataMap; \ static datamap_t *GetBaseMap(); \ template <typename T> friend void DataMapAccess(T *, datamap_t **p); \ template <typename T> friend datamap_t *DataMapInit(T *);
#define DECLARE_DATADESC() \ DECLARE_SIMPLE_DATADESC() \ virtual datamap_t *GetDataDescMap( void );
|
So, when our basecombatweapon_shared.h file is compiled, the compiler will resolve DECLARE_DATADESC to:
| | | static datamap_t m_DataMap; static datamap_t *GetBaseMap(); template <typename T> friend void DataMapAccess(T *, datamap_t **p); template <typename T> friend datamap_t *DataMapInit(T *); virtual datamap_t *GetDataDescMap( void );
|
That's the hardest part of deciphering this particular macro. Now all that's left is to figure out what the resolved code actually does. Uh, maybe I spoke too soon about that hardest part thing...
So we're defining a static datamap for the class, as well as a static function to get at it. THEN we have template functions for initializing and accessing the datamap, as well as another function to get the DataDescMap, which is basically the same as the base map. All of which reduces to a somewhat ugly and definitely confusing attemp to recreate .NET-style reflection in c++. You see what Valve wants to be able to do is access information about a class through the class itself - what are the variable types and names, what kind of functions are available, etc - without having the header file. All the DECLARE_DATADESC macro is doing is providing that functionality, albeit in a kludgey way.
| | | virtual void FallInit( void ); virtual void FallThink( void );
|
Still curious about those Think functions? Me too. I'm beginning to.. uh.. consider that they act as some kind of timing control, so that certain actions take place over time, instead of all at once. Don't worry, I may not get to them in this article, but if not they'll be covered in the next one, which will deal with the implementation of FallThink and all the other methods of CBaseCombatWeapon.
| | | bool IsConstrained() { return m_pConstraint != NULL; } bool IsInBadPosition ( void ); bool RepositionWeapon ( void ); void Materialize( void ); void AttemptToMaterialize( void ); virtual void CheckRespawn( void ); CBaseEntity *Respawn ( void );
|
A few more self-evident declarations; we're moving along at a fairly brisk pace, wouldn't you say? I do believe the end (of this file) is in sight.
| | | static int GetAvailableWeaponsInBox( CBaseCombatWeapon **pList, int listMax, const Vector &mins, const Vector &maxs );
|
Ok, this is a seriously freaking cool function. If you read through the definition in the .cpp file it becomes clear what it's purpose it. I don't know why I think this is so cool, I just do. Anyway the idea is, you pass in an empty array of CBaseCombatWeapons, a maximum # of weapons allowed to be added to the list, and the mins and max vectors of the box. Then the function fills your array with weapons for you and hands it back. Remember in the game when you came upon an ammo crate? I think this function is what was used when you opened one up. There would be like, 8 rockets in there, and Valve would call this function with the min and max vectors (read: 3D points) defining the ammo box and the number 3 for listMax, and you'd suddenly have 3 rockets. Then when you close the thing, the replacement rockets (which respawned from the originals as soon as you got them) materialize. How cool is that!! XD
| | | virtual void Delete( void ); void DestroyItem( void ); virtual void Kill( void );
|
Yup.
| | | virtual int CapabilitiesGet( void ) { return 0; } virtual int ObjectCaps( void );
|
Ok, let's suss these out.

The inline definition for CapabilitiesGet is unenlightening at best, but if you look for the declaration of this object you'll see that several derived weapon classes have different declarations.

If you look at weapon_rpg you'll see that CapabilitiesGet looks like this:
| | | int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
|
You can follow bits_CAP_ {...} to BaseCombatCharacter.h where we find the following:
| | |
enum Capability_t { bits_CAP_MOVE_GROUND = 0x00000001, bits_CAP_MOVE_JUMP = 0x00000002, bits_CAP_MOVE_FLY = 0x00000004, bits_CAP_MOVE_CLIMB = 0x00000008, bits_CAP_MOVE_SWIM = 0x00000010, bits_CAP_MOVE_CRAWL = 0x00000020, bits_CAP_MOVE_SHOOT = 0x00000040, bits_CAP_SKIP_NAV_GROUND_CHECK = 0x00000080, bits_CAP_USE = 0x00000100, bits_CAP_AUTO_DOORS = 0x00000400, bits_CAP_OPEN_DOORS = 0x00000800, bits_CAP_TURN_HEAD = 0x00001000, bits_CAP_WEAPON_RANGE_ATTACK1 = 0x00002000, bits_CAP_WEAPON_RANGE_ATTACK2 = 0x00004000, bits_CAP_WEAPON_MELEE_ATTACK1 = 0x00008000, bits_CAP_WEAPON_MELEE_ATTACK2 = 0x00010000, bits_CAP_INNATE_RANGE_ATTACK1 = 0x00020000, bits_CAP_INNATE_RANGE_ATTACK2 = 0x00040000, bits_CAP_INNATE_MELEE_ATTACK1 = 0x00080000, bits_CAP_INNATE_MELEE_ATTACK2 = 0x00100000, bits_CAP_USE_WEAPONS = 0x00200000, bits_CAP_ANIMATEDFACE = 0x00800000, bits_CAP_USE_SHOT_REGULATOR = 0x01000000, bits_CAP_FRIENDLY_DMG_IMMUNE = 0x02000000, bits_CAP_SQUAD = 0x04000000, bits_CAP_DUCK = 0x08000000, bits_CAP_NO_HIT_PLAYER = 0x10000000, bits_CAP_AIM_GUN = 0x20000000, bits_CAP_NO_HIT_SQUADMATES = 0x40000000, bits_CAP_SIMPLE_RADIUS_DAMAGE = 0x80000000, };
|
So CapabilitesGet tells the calling code what the derived weapon is capable of, and the available capabilities are defined in Capability_t. As for ObjectCaps, that refers to class capabilities, e.g. save and restore capabilities. When you save a game, you want to remember how much ammo your rpg had. So you have to have the capability to save, and ObjectCaps returns that information upon request.
| | | virtual bool IsRemoveable() { return m_bRemoveable; } void SetRemoveable( bool bRemoveable ) { m_bRemoveable = bRemoveable; }
|
Indicates whether a weapon can be removed. Used by the GameWeaponManager to clean up all the extra weapons lying around. Not derived anywhere, as far as I can tell.
| | | virtual Activity ActivityOverride( Activity baseAct, bool *pRequired ); virtual acttable_t* ActivityList( void ) { return NULL; } virtual int ActivityListCount( void ) { return 0; }
|
Override simply searches the activity list to see if there is a weapon specific override for a given base activity. ActivityList and ActivityListCount are inline here and return NULL and 0 because this is the base class; in a derived weapon class there would be a real returned value.
| | | virtual bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ); virtual int WeaponRangeAttack1Condition( float flDot, float flDist ); virtual int WeaponRangeAttack2Condition( float flDot, float flDist ); virtual int WeaponMeleeAttack1Condition( float flDot, float flDist ); virtual int WeaponMeleeAttack2Condition( float flDot, float flDist );
|
I had to muck around for a while to figure out what WeaponLOSCondition is for, but basically I think it is used to determine if an NPC's weapon has a Line Of Sight on the player. If you pass in bSetConditions as True, certain conditions are set on the NPC like COND_WEAPON_BLOCKED_BY_FRIEND or COND_WEAPON_SIGHT_OCCLUDED. If the weapon has a LOS, the function returns true. As for the AttackConditions, they just return defined constants like COND_NO_PRIMARY_AMMO, COND_TOO_FAR_TO_ATTACK, and COND_CAN_RANGE_ATTACK1. flDot is a vector dot-product indicating what angle you are looking in relation to the enemy you're trying to attack.
| | | virtual void Operator_FrameUpdate( CBaseCombatCharacter *pOperator ); virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
|
Operator_FrameUpdate advances the animation frame and handle overhead such as event notification to the weapon owner, as well as dispatching the animation frame advance to the viewmodel if necessary. Operator_HandleAnimEvent seems to be a debug method that prints a log message, and shouldn't be used.
| | | void HandleAnimEvent( animevent_t *pEvent );
|
When an animation event occurs, you want to handle it. CBaseCombatWeapon's HandleAnimEvent actually deals with unhandled events, because properly all events should be routed through the character operating it. There shouldn't be an animation event for any weapon lying on the ground. You could override this in a derived class, but I imagine that for something like a team-fortress style turret there is a CBaseCombatCharacter entity registered as the owner of the turret, even though that entity has no model, no physics apply to it, etc. If you look at the definition, you'll see that if there is an owner, and the animation event has been routed through the weapon's HandleAnimEvent method, the result is a call to Operator_HandleAnimEvent, which as I discussed above writes a DevWarning message. The moral of the story: route weapon animation events through their owners.
I don't have a thorough understanding of how the animation code works yet, or precisely what an animation event it, so take the above with a grain of salt.
| | | virtual int UpdateTransmitState( void );
|
This is another sticky wicket; I think it has to do with network play. UpdateTransmitState is used to update the state variable which determines whether an entity is transmitted. So if you have just made a weapon invisible in preparation to delete it, the network code might call UpdateTransmitState to figure out that it doesn't need to transmit information about that weapon. I think.
| | | void InputHideWeapon( inputdata_t &inputdata ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
To fully grok InputHideWeapon, we need to take a look at the inputdata_t struct definition from BaseEntity.h:
| | | struct inputdata_t { CBaseEntity *pActivator; CBaseEntity *pCaller; variant_t value; int nOutputID; };< |
| |