Posted by: Josiwe
Date posted: Jan 25 2005 User Rating: 4.5 out of 5.0 | Number of views: 4484 Number of comments: 0 | 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. |
This is a continuation of my article exploring the CBaseCombatWeapon class header, part of a larger series of articles about the RPG weapon class. If you have not read Part 1A, please read it first. Thanks,
- Josiwe
| | | private: typedef CHandle< CBaseCombatCharacter > CBaseCombatCharacterHandle; CNetworkVar( CBaseCombatCharacterHandle, m_hOwner );
|
At long last, some private variables. First off we're telling the compiler that every time we use CBaseCombatCharacterHandle, it's really a CHandle of type CBaseCombatCharacter. If you look at ehandle.h, you'll see that CHandle is a public CBaseHandle. This is an important class to understand. A Handle is a unique identifier for an entity. In the implementation of CBaseHandle we find the following code:
| | | class CBaseHandle { ... ... ... protected: unsigned long m_Index; };
|
The bits in the unsigned long m_Index are split - part of them represent the index in an entity list, and the other part represents a serial number for the entity. Together they form a unique identifier, or handle. CHandle is a templatized version of CBaseHandle, which allows you to create an entity list or handle for any type of entity you like, provided it inherits from CBaseEntity. So our typedef tells the compiler that CBaseCombatCharacterHandle is a CHandle of type CBaseCombatCharacter. If you followed all of that, give yourself a pat on the back, because it took me like 40 minutes of head-scratching and nose-picking to wrap my brain around it.
CNetworkVar is a macro in networkvar.h that looks like this:
| | | #define CNetworkVar( type, name ) \ NETWORK_VAR_START( type, name ) \ NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged )
|
There's about 10 more layers of macros down, so I'll spare you the details of my journey into the depths of Valve's macro madness and just tell you what the line resolves to:
| | | class NetworkVar_CBaseCombatCharacterHandle; friend class NetworkVar_m_hOwner; typedef ThisClass MakeANetworkVar_m_hOwner; class NetworkVar_m_hOwner { public: template <typename T> friend int ServerClassInit(T *); public: static inline void NetworkStateChanged( void *ptr ) { ((ThisClass*)(((char*)ptr) - ( (int)&((ThisClass*)0)->m_hOwner)))->NetworkStateChanged (); \ } }; CNetworkVarBase< CBaseCombatCharacterHandle, NetworkVar_m_hOwner > m_hOwner;
|
Comment from Omega: "CNetworkVar makes a variable shared between the server and client dll. Meaning, if the client has access to the entity, it can read it on the client as it's automatically replicated (provided you setup the Send_ and Recv tables)."
CNetworkVar in is used here to create a network variable for our Cbasecombatweapon's owner.
| | | protected:
public:
CNetworkVar( int, m_nViewModelIndex );
CNetworkVar( float, m_flNextPrimaryAttack ); CNetworkVar( float, m_flNextSecondaryAttack ); CNetworkVar( float, m_flTimeWeaponIdle );
|
Here we're creating network variables to let the other players (in a multiplayer game) know about the soonest time certain events could occur, which I guess helps with prediction. Also we create a network variable for the... viewmodel index? I fail to see how that could be useful, but whatever. Let's move on.
UPDATE: Um, duh. Read Valve's documentation on Entity Networking for a far more lucid explanation of what the hell is happening here than I am able to provide.
| | | bool m_bInReload; bool m_bFireOnEmpty; CNetworkVar( int, m_iViewModelIndex ); CNetworkVar( int, m_iWorldModelIndex ); float m_flNextEmptySoundTime;
|
Ahh, blissfully simple bools. How do I love thee? Let me count the ways. (Hint: there's only 2 ways.)
Since the comments on the bools are pretty staightforward, let's move on. We're getting close to the end (YAY!):
| | | Activity GetIdealActivity( void ) { return m_IdealActivity; } int GetIdealSequence( void ) { return m_nIdealSequence; }
bool SetIdealActivity( Activity ideal ); void MaintainIdealActivity( void );
|
I'm still a bit unclear on the difference between an Activity and a Sequence; but it's clear what these functions do.
| | | private: Activity m_Activity; int m_nIdealSequence; Activity m_IdealActivity;
bool m_bRemoveable;
int m_iPrimaryAmmoCount; int m_iSecondaryAmmoCount;
|
Some easy private variables. We've seen these in a few of the inline definitions, so here's where they are declared, for those curious.
| | | public:
IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_nNextThinkTick );
|
Another macro. This one isn't too bad. The first layer is resolved by the macro definition in networkvar.h:
| | | #define IMPLEMENT_NETWORK_VAR_FOR_DERIVED( name ) \ virtual void NetworkStateChanged_##name() { CHECK_USENETWORKVARS NetworkStateChanged(); }
|
So now we have the following:
| | | virtual void NetworkStateChanged_m_nNextThinkTick() { CHECK_USENETWORKVARS NetworkStateChanged(); }
|
Again in networkvar.h, we can see that CHECK_USENETWORKVARS is defined as:
| | | #ifdef _DEBUG extern bool g_bUseNetworkVars; #define CHECK_USENETWORKVARS if(g_bUseNetworkVars) #else #define CHECK_USENETWORKVARS #endif
|
Since we can assume that we don't need to use Valve's debug code to edit their networking frame (unless you're crazy), we can safely ignore this second level of macro-y goodness. Which means that IMPLEMENT_NETWORK_VAR_FOR_DERIVED sets up a function that notifies other network entities when m_nNextThinkTick changes.
| | | int WeaponState() const { return m_iState; }
|
Basically a read-only property. "But Master Josiwe," you ask, "where is m_iState defined?" Oh grasshopper. You have much to learn yet about patience.
| | | CNetworkVar( int, m_iState ); string_t m_iszName; CNetworkVar( int, m_iPrimaryAmmoType ); CNetworkVar( int, m_iSecondaryAmmoType ); CNetworkVar( int, m_iClip1 ); CNetworkVar( int, m_iClip2 ); bool m_bFiresUnderwater; float m_fMinRange1; float m_fMinRange2; float m_fMaxRange1; float m_fMaxRange2; bool m_bReloadsSingly; float m_fFireDuration; int m_iSubType;
float m_flUnlockTime; EHANDLE m_hLocker;
|
Here we have a load of public members and network variables, with helpful green comments. It's not easy, being green. But green can be big like an ocean, or important like a mountain, or tall like a tree. So read those comments, and don't post a newbie question in the forum like "what does m_bFiresUnderwater do? i'm done learning c++ but this one is tricky"
We'll skip the next 3 lines of code which are client only for reasons previously mentioned, which takes us here:
| | | IPhysicsConstraint *GetConstraint() { return m_pConstraint; }
private:
WEAPON_FILE_INFO_HANDLE m_hWeaponFileInfo; IPhysicsConstraint *m_pConstraint;
|
IPhysicsConstraint is an interface or code-contract that basecombatweapon agrees to implement. GetConstraint obviously returns a pointer to this interface object - what this means is that something else in the game can call myBaseCombatWeapon.GetConstraint->SomePhysicsMethod and any weapon derived from basecombatweapon guarantees that it has implemented it. m_hWeaponFileInfo is a pointer to a handle for the info class containing properties saved in the weapon.txt file.
| | | #if !defined( CLIENT_DLL )
COutputEvent m_OnPlayerUse; COutputEvent m_OnPlayerPickup; COutputEvent m_OnNPCPickup;
#else int m_iOldState; bool m_bJustRestored;
friend class WeaponsResource;
#endif };
#endif
|
Finally, finally, we define 3 output events that the server will fire when our weapon is picked up or used. See Valve's documentation about logical entities for more on outputs.
Wow. I'll tell you, when I started this article I thought it was going to be easy. Ha! Ha ha, with the maniacle laughing and the erratic blinking! Anyway, I hope you've enjoyed my sartorial wit and perhaps learned a thing or 62 about basecombatweapon. Buckle up though, because next time we'll be looking at some of the more interesting (by which I mean confusing) definitions in basecombatweapon_shared.cpp.
For those curious, after that I'll be taking a look at the non-shared, server side version of basecombatweapon. There may be some interesting and useful things in the client that I'll digress into, but I'm not interested in the rendering stuff. It's out of my league. The main focus of these articles isn't understanding how the bone-animation works, but how the game rules are used to make the rpg behave the way it does. What's up with the laser? And the mizzizzle class??? If you want to know, check back in a couple weeks for the next installment of "Josiwe's manic adventure into the SDK!"
| | | | The search for truth is more precious than its possession. - Albert Einstein | |
|
User Comments
No User Comments
You must register to post a comment. If you have already registered, you must login.
|
297 Approved Articless
6 Pending Articles
3940 Registered Members
0 People Online (16 guests)
|
|