Welcome, Guest! Login | Register

An Exploration Of Weapon_RPG: Part 1B [Print this Article]
Posted by: Josiwe
Date posted: Jan 26 2005
User Rating: 4.5 out of 5.0
Number of views: 6324
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


 CODE (C++) 
private:
    typedef CHandle< CBaseCombatCharacter > CBaseCombatCharacterHandle;
    CNetworkVar( CBaseCombatCharacterHandle, m_hOwner );                // Player carrying this weapon


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:

 CODE (C++) 
class CBaseHandle
{
...
...
...
protected:
    // The low NUM_SERIAL_BITS hold the index. If this value is less than MAX_EDICTS, then the entity is networkable.
    // The high NUM_SERIAL_NUM_BITS bits are the serial number.
    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:

 CODE (C++) 
// Use this macro to define a network variable.
#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:

 CODE (C++) 
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.

 CODE (C++) 
protected:

public:

    // Networked fields
    CNetworkVar( int, m_nViewModelIndex );

    // Weapon firing
    CNetworkVar( float, m_flNextPrimaryAttack );                        // soonest time ItemPostFrame will call PrimaryAttack
    CNetworkVar( float, m_flNextSecondaryAttack );                  // soonest time ItemPostFrame will call SecondaryAttack
    CNetworkVar( float, m_flTimeWeaponIdle );                           // soonest time ItemPostFrame will call WeaponIdle


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.

 CODE (C++) 
    // Weapon state
    bool                    m_bInReload;            // Are we in the middle of a reload;
    bool                    m_bFireOnEmpty;         // True when the gun is empty and the player is still holding down the attack key(s)
    // Weapon art
    CNetworkVar( int, m_iViewModelIndex );
    CNetworkVar( int, m_iWorldModelIndex );
    // Sounds
    float                   m_flNextEmptySoundTime;             // delay on empty sound playing


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!):

 CODE (C++) 
    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.

 CODE (C++) 
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.

 CODE (C++) 
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:

 CODE (C++) 
#define IMPLEMENT_NETWORK_VAR_FOR_DERIVED( name ) \
    virtual void NetworkStateChanged_##name() { CHECK_USENETWORKVARS NetworkStateChanged(); }


So now we have the following:

 CODE (C++) 
virtual void NetworkStateChanged_m_nNextThinkTick() { CHECK_USENETWORKVARS NetworkStateChanged(); }


Again in networkvar.h, we can see that CHECK_USENETWORKVARS is defined as:

 CODE (C++) 
#ifdef _DEBUG
    extern bool g_bUseNetworkVars;
    #define CHECK_USENETWORKVARS if(g_bUseNetworkVars)
#else
    #define CHECK_USENETWORKVARS // don't check for g_bUseNetworkVars
#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.

 CODE (C++) 
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.

 CODE (C++) 
    // Weapon data
    CNetworkVar( int, m_iState );               // See WEAPON_* definition
    string_t                m_iszName;              // Classname of this weapon.
    CNetworkVar( int, m_iPrimaryAmmoType );     // "primary" ammo index into the ammo info array
    CNetworkVar( int, m_iSecondaryAmmoType );   // "secondary" ammo index into the ammo info array
    CNetworkVar( int, m_iClip1 );               // number of shots left in the primary weapon clip, -1 it not used
    CNetworkVar( int, m_iClip2 );               // number of shots left in the secondary weapon clip, -1 it not used
    bool                    m_bFiresUnderwater;     // true if this weapon can fire underwater
    float                   m_fMinRange1;           // What's the closest this weapon can be used?
    float                   m_fMinRange2;           // What's the closest this weapon can be used?
    float                   m_fMaxRange1;           // What's the furthest this weapon can be used?
    float                   m_fMaxRange2;           // What's the furthest this weapon can be used?
    bool                    m_bReloadsSingly;       // Tryue if this weapon reloads 1 round at a time
    float                   m_fFireDuration;        // The amount of time that the weapon has sustained firing
    int                     m_iSubType;

    float                   m_flUnlockTime;
    EHANDLE                 m_hLocker;              // Who locked this weapon.


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:

 CODE (C++) 
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.

 CODE (C++) 
    // Server only
#if !defined( CLIENT_DLL )

    // Outputs
    COutputEvent            m_OnPlayerUse;      // Fired when the player uses the weapon.
    COutputEvent            m_OnPlayerPickup;   // Fired when the player picks up the weapon.
    COutputEvent            m_OnNPCPickup;      // Fired when an NPC picks up the weapon.

#else // Client .dll only
    int                     m_iOldState;
    bool                    m_bJustRestored;

    // Allow weapons resource to access m_hWeaponFileInfo directly
    friend class            WeaponsResource;

#endif // End Client .dll only
};

#endif // COMBATWEAPON_SHARED_H


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!"

 QUOTE  
The search for truth is more precious than its possession.  - Albert Einstein

Rate This Article
This article is currently rated: 4.5 out of 5.0 (2 Votes)

You have to register to rate this article.
User Comments

No User Comments

You must register to post a comment. If you have already registered, you must login.

Latest Articles
3rd person View in Multiplayer
Half-Life 2 | Coding | Client Side Tutorials
How to enable it in HL2DM

By: cct | Nov 13 2006

Making a Camera
Half-Life 2 | Level Design
This camera is good for when you join a map, it gives you a view of the map before you join a team

By: slackiller | Mar 05 2006

Making a camera , Part 2
Half-Life 2 | Level Design
these cameras are working monitors that turn on when a button is pushed.

By: slackiller | Mar 04 2006

Storing weapons on ladder
Half-Life 2 | Coding | Snippets
like Raven Sheild or BF2

By: British_Bomber | Dec 24 2005

Implementation of a string lookup table
Half-Life 2 | Coding | Snippets
A string lookup table is a set of functions that is used to convert strings to pre-defined values

By: deathz0rz | Nov 13 2005


Latest Comments
knock knock
General | News
By: MIFUNE | Dec 31 2017
 
knock knock
General | News
By: omega | Dec 22 2016
 
knock knock
General | News
By: MIFUNE | Oct 10 2015
 
New HL HUD Message System
Half-Life | Coding | Shared Tutorials
By: chbrules | Dec 31 2011
 
knock knock
General | News
By: Whistler | Nov 05 2011
 
Particle Engine tutorial part 4
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 18 2010
 
Particle Engine tutorial part 2
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Particle Engine tutorial part 3
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Game Movement Series #2: Analog Jumping and Floating
Half-Life 2 | Coding | Shared Tutorials
By: mars3554 | Oct 26 2009
 
Particle Engine tutorial part 5
Half-Life | Coding | Client Side Tutorials
By: Deadpool | Aug 02 2009
 

Site Info
297 Approved Articless
8 Pending Articles
3940 Registered Members
0 People Online (7 guests)
About - Credits - Contact Us

Wavelength version: 3.0.0.9
Valid XHTML 1.0! Valid CSS!