Difference between revisions of "Authoring your first weapon entity"

From Valve Developer Community
Jump to: navigation, search
(Complete rewrite, I never finished this before. I made to code actually work too.)
Line 7: Line 7:
  
 
== Getting Started ==
 
== Getting Started ==
To get started, create a new .cpp file in the server project and call it weapon_myfirstweapon.cpp.
+
To note, this tutorial expects the reader is using Source SDK 2013 Singleplayer. To get started, create a new .cpp file in the server project and call it weapon_tutorial.cpp.
Type this into your newly created file
+
Type this into your newly created file.
 
<pre>
 
<pre>
 
#include "cbase.h"
 
#include "cbase.h"
 +
#include "basehlcombatweapon.h"
 +
#include "basecombatcharacter.h"
 +
#include "player.h"
 +
#include "gamerules.h"
 +
#include "in_buttons.h"
 +
#include "soundent.h"
 +
#include "game.h"
 +
#include "vstdlib\random.h"
 +
#include "gamestats.h"
 
#include "npcevent.h"
 
#include "npcevent.h"
#include "in_buttons.h"
 
  
#ifdef CLIENT_DLL
+
#include "tier0\memdbgon.h"
#include "c_hl2mp_player.h"
 
#else
 
#include "hl2mp_player.h"
 
#endif
 
 
 
#include "weapon_hl2mpbasehlmpcombatweapon.h"
 
 
 
#ifndef CLIENT_DLL
 
#define CWeaponMyFirstWeapon C_WeaponMyFirstWeapon
 
#endif
 
 
</pre>
 
</pre>
Also to note is this tutorial expects the reader is using the Source SDK Multiplayer, but if the reader wanted to make the weapon for singleplayer, it should be an easy fix. Just adjust the includes.
+
These are all the files we need to include for our weapon to work. If we do not include most these, our weapon just simply would not compile.
  
== Creating The Weapon ==
+
== Creating The Class ==
Now we will create the class that contains all the functions, so we can put the code in later.
+
Now we will define the class and all of our functions, so it is easier on us down the line. Copy this code down into your file.
To start, make a new class, and define it like this
 
 
<pre>
 
<pre>
class CWeaponMyFirstWeapon : public CBaseHL2MPCombatWeapon
+
class CWeaponTutorial : public CBaseHLCombatWeapon
</pre>
+
{
 +
  
Now, open it up with some curly brackets, and type this
 
<pre>
 
 
public:
 
public:
  DECLARE_CLASS(CWeaponMyFirstWeapon, CBaseHL2MPCombatWeapon);
+
DECLARE_CLASS( CWeaponTutorial, CBaseHLCombatWeapon );
 +
        DECLARE_DATADESC( );
 +
        DECLARE_ACTTABLE( );
  
  CWeaponMyFirstWeapon(void);
+
CWeaponTutorial( );
  
  DECLARE_NETWORKCLASS();
+
DECLARE_SERVERCLASS( );
  DECLARE_PREDICTABLE();
+
 
 +
void Precache( );
 +
void ItemPreFrame( );
 +
void ItemBusyFrame( );
 +
void ItemPostFrame( );
 +
void PrimaryAttack( );
 +
void AddViewKick( );
 +
void DryFire( );
 +
virtual bool         Reload( void );
 +
};
 
</pre>
 
</pre>
The code above just starts off the file, makes so it works with the server, and creates the base for the constructor.
+
Here is a quick explanation of what is going on here. If you have read the [[My First Entity]] series of tutorials, you can see that instead of using <code>CLogicalEntity</code> or <code>CBaseAnimating</code> as our <code>BaseClass</code>, we are using <code>CBaseHLCombatWeapon</code>, which contains all of the functions and logic we will be needing to create this weapon. After this, we are simply doing the usual, and declaring our class and data description, and we are also defining an [[acttable]]. This simply tells our weapon which animations to use. Finally, we are simply making our constructor, and making stubs for all of our functions. For more information on what any of these function stubs do, take a quick look at [[Authoring a weapon entity]], but this is not required because later I will be giving a very simple explanation of what these are for and do. After this, type:
 
 
== Creating The Functions ==
 
After the line that says <code>DEALRE_PREDICTABLE</code>, type these functions in
 
 
<pre>
 
<pre>
void Precache(void);
+
virtual const Vector& GetBulletSpread( )
void ItemPreFrame(void);
+
{
void ItemBusyFrame(void);
+
static Vector cone = VECTOR_CONE_1DEGREES, npcCone = VECTOR_CONE_1DEGREES;
void ItemPostFrame(void);
+
if (GetOwner( ) && GetOwner( )->IsNPC( )) //Always handle NPCs first
void PrimaryAttack(void);
+
return npcCone;
void AddViewKick(void);
+
else
 
+
return cone;
Activity GetPrimaryAttackActivity(void);
+
}
 
 
virtual bool Reload(void);
 
 
 
virtual const Vector& GetBulletSpread(void)
 
{
 
    static Vector cone = VECTOR_CONE_1DEGREES;
 
    return cone;
 
}
 
 
 
#ifndef CLIENT_DLL
 
    DECLARE_ACTTABLE();
 
#endif
 
 
</pre>
 
</pre>
All this does is create stubs for the functions we will define later, with the exception of <code>GetBulletSpread</code>.
+
All this is doing is simply defining the bullet spread, which is one cone. After this, finally copy this down:
To understand all of the functions, see [[Authoring a weapon entity]], but the same things will be described later.
 
 
 
== Creating Private Code ==
 
In this section, the reader will learn how to create variables, so they can be networked.
 
To start off the private section, type
 
 
<pre>
 
<pre>
 
private:
 
private:
    CNetworkVar(int, m_iTest);
+
float m_flRateOfFire;
 +
</pre>
 +
All this is doing is defining a float named <code>m_flRateOfFire</code>, which as you guessed, is our rate of fire. We will define this later.
  
private:
+
== Before Writing the Functions ==
    CWeaponMyFirstWeapon(const CWeaponMyFirstWeapon &);
+
Before we write the functions, there are a few simple things we have to do. First, we are going to link our weapon to an entity, so it can be spawned in game, either with the [[give]] command or by any other means, and precache it. Write this code down.
 +
<pre>
 +
LINK_ENTITY_TO_CLASS( weapon_tutorial, CWeaponTutorial );
 +
PRECACHE_WEAPON_REGISTER( weapon_tutorial );
 
</pre>
 
</pre>
Basically, this just creates a variable, with the type of a int, called <code>m_iTest</code>, and then this code creates an alternate constructor. How we will change the value of <code>m_iTest</code> will be discussed later. If your getting an error now just ignore that.
+
Next, we will define our data description.
You can also create this same variable at the top, with a <code>#define</code>.
 
 
<pre>
 
<pre>
#define m_iTest 1
+
BEGIN_DATADESC( CWeaponTutorial )
 +
DEFINE_FIELD( m_flRateOfFire, FIELD_FLOAT ),
 +
END_DATADESC( )
 
</pre>
 
</pre>
Just make sure to delete this instance under the <code>private:</code>
+
If you have read the [[My First Entity]] series, you will know what this is doing. If we were feeling fancy, we could even link this value to a [[ConVar]] so we could define it in game. Finally, we will define the [[acttable]], so our weapon knows which animations to use. For this tutorial, we will be using the SMG animations.
 
 
== Defining The Functions ==
 
Now, go out of the brackets for the <code>CWeaponMyFirstWeapon</code> class.
 
Next, type
 
 
<pre>
 
<pre>
IMPLEMENT_NETWORKCLASS_ALIASED(WeaponMyFirstWeapon, DT_WeaponMyFirstWeapon)
+
acttable_t CWeaponTutorial::m_acttable[] =
 +
{
 +
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true },
 +
{ ACT_RELOAD, ACT_RELOAD_SMG1, true },
 +
{ ACT_IDLE, ACT_IDLE_SMG1, true },
 +
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true },
 +
{ ACT_WALK, ACT_WALK_RIFLE, true },
 +
{ ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true },
 +
};
  
BEGIN_NETWORK_TABLE(CWeaponMyFirstWeapon, DT_WeaponMyFirstWeapon)
+
IMPLEMENT_ACTTABLE( CWeaponTutorial );
#ifndef CLIENT_DLL
 
RecvPropFloat(RECVINFO(m_flRateOfFire)),
 
#else
 
SendPropFloat(SENDINFO(m_flRateOfFire)),
 
#endif
 
 
</pre>
 
</pre>
This code just defines how things will be networked, and what things will be networked.
+
== Programming the Functions ==
Moving on, to create the weapons entity, type
+
Now, we will finally write the code that will make everything come together. To start off, we will write the code for the constructor. What it does is fairly self-explanatory.
 
<pre>
 
<pre>
LINK_ENTITY_TO_CLASS(weapon_myfirstweapon, CWeaponMyFirstWeapon);
+
CWeaponTutorial::CWeaponTutorial( )
PRECACHE_WEAPON_REGISTER(weapon_myfirstweapon); //This defines what the weapon script we make later will be called
+
{
 +
m_fMinRange1 = 24;
 +
m_fMaxRange1 = 3000;
 +
m_bFiresUnderwater = true;
 +
}
 
</pre>
 
</pre>
Now, to declare the acttable. This is not very important, it just defines what animations to use, and in this case we are using the pistol animations. For more information, check out the docs on the [[acttable_t|acttable]].
+
Next, we will move on to the precache class. Since we are using the SMG models, we will precache those. This also sets our rate of fire to 10 rounds per second.
 
<pre>
 
<pre>
#ifndef CLIENT_DLL
+
void CWeaponTutorial::Precache( )
acttable_t CWeaponMyFirstWeapon::m_acttable[] =
 
 
{
 
{
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false },
+
m_flRateOfFire = 0.1f;
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false },
 
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false },
 
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false },
 
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false },
 
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false },
 
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false },
 
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, false },
 
};
 
  
IMPLEMENT_ACTTABLE(CWeaponMyFirstWeapon);
+
PrecacheModel( "models/weapons/v_smg1.mdl", true );
 +
PrecacheModel( "models/weapons/w_smg1.mdl", true );
 +
BaseClass::Precache( );
 +
}
 +
</pre>
 +
After this, we will define our <code>DryFire</code> function. What this function does is also fairly self-explanatory.
 +
<pre>
 +
void CWeaponTutorial::DryFire( )
 +
{
 +
WeaponSound( EMPTY );
 +
SendWeaponAnim( ACT_VM_DRYFIRE );
  
#endif
+
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration( );
 +
}
 
</pre>
 
</pre>
And now, the moment you've all been waiting for, the functions!
+
For this one, I am going to knock out three functions in one block, mainly because they do not do anything and they are just here so I can explain what they do and when you might need them.
 
<pre>
 
<pre>
CWeaponMyFirstWeapon::CWeaponMyFirstWeapon(void)
+
void CWeaponTutorial::ItemPreFrame( )
 
{
 
{
    m_fMinRange1 = 24;
+
BaseClass::ItemPreFrame( );
    m_fMaxRange1 = 1500;
+
}
    m_fMinRange2 = 24;
 
    m_fMaxRange2 = 200;
 
  
    m_bFiresUnderwater = true;
+
void CWeaponTutorial::ItemBusyFrame( )
 +
{
 +
BaseClass::ItemBusyFrame( );
 +
}
  
    m_iTest = 1;
+
void CWeaponTutorial::ItemPostFrame( )
 +
{
 +
BaseClass::ItemPostFrame( );
 
}
 
}
 
</pre>
 
</pre>
This is the constructor. This is called whenever the weapon is first created. As you can see here, it just defines the minimum range, and the maximum range for the weapon. Then, it just says the weapon can be fired underwater, and finally, the value of the <code>m_iTest</code> is set to 1.
+
<code>ItemPreFrame</code> is called before the player movement function. <code>ItemBusyFrame</code> is called with the player movement function. <code>ItemPostFrame</code> is called after the player movement function. Out of these three, <code>ItemPostFrame</code> is the most useful. Now on to the most complicated of these functions, <code>PrimaryAttack</code>. This one requires the most explanation, mainly because we are writing the firing logic ourselves.
Moving on, now we will create the precache function.
 
 
<pre>
 
<pre>
void CWeaponMyFirstWeapon::Precache(void)
+
void CWeaponTutorial::PrimaryAttack( )
 
{
 
{
BaseClass::Precache();
+
CBasePlayer *pPlayer = ToBasePlayer( GetOwner( ) ); //This gets the current player holding the weapon
 +
Vector vecSrc = pPlayer->Weapon_ShootPosition( );  //This simply just gets the current position of the player.
 +
Vector vecAim = pPlayer->GetAutoaimVector( 0.0 );  //This gets where the player is looking, but also corrected by autoaim.
 +
pPlayer->FireBullets( 1, vecSrc, vecAim, GetBulletSpread( ), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 1, entindex( ), 0, 30, GetOwner( ), true, true );
 +
//This is a lengthy one. All of the args in here are data for our bullet. We could simply use
 +
//BaseClass::PrimaryAttack, but this gives us no control over how our weapon is fired.
 +
//So instead, we use this and give it all our info for our bullet.
 +
//The next 2 are just where the bullet is being fired from and where it should go.
 +
//Next is how far the bullet is fired, and after that is what type of ammo we use.
 +
//After this is the tracer freq, which really doesnt matter.
 +
//Next is the id of the entity firing the weapon, and the attachment id. These 2 dont really matter.
 +
//Next is how much damage each bullet should do, which is 30.
 +
//Next is what entity is firing the bullet, again.
 +
//The final 2 define wether our first shot should be accurate, and wether this is the primary attack.
 +
WeaponSound( SINGLE ); //This makes our weapon emit the single show sound.
 +
SendWeaponAnim( ACT_VM_PRIMARYATTACK ); //This sends the animation for us shooting.
 +
m_flNextPrimaryAttack = gpGlobals->curtime + m_flRateOfFire; //This defines when our next attack should be
 +
AddViewKick( ); //Don't forget to add our viewkick
 
}
 
}
 
</pre>
 
</pre>
So, this is called even before the item is created. Basically, it just caches all the needed resources. Now, on to the next function!
+
After this, we only have 2 more functions to go, so congratulations, you made it! We will now do the View Kick function, which is fairly simple.
 
<pre>
 
<pre>
void CWeaponMyFirstWeapon::ItemPreFrame(void) {
+
void CWeaponTutorial::AddViewKick( )
BaseClass::ItemPreFrame();
+
{
 +
CBasePlayer *pPlayer = ToBasePlayer( GetOwner( ) );
 +
QAngle punch;
 +
punch += QAngle( -0.2, 0.0, 0.0 );
 +
pPlayer->ViewPunch( punch );
 +
}
 +
</pre>
 +
This just simply creates recoil straight up by adding .2 to our QAngle every time we fire. Now, for the final, and one of the simplest functions, reload.
 +
<pre>
 +
bool CWeaponTutorial::Reload( )
 +
{
 +
bool fRet = DefaultReload( GetMaxClip1( ), GetMaxClip2( ), ACT_VM_RELOAD );
 +
if (fRet)
 +
WeaponSound( RELOAD );
 +
return fRet;
 
}
 
}
 
</pre>
 
</pre>
This is called before the has moved, or in other words it is executed before the player movement function.
+
All this does if checks if we must reload, and if we do, play the animation and sound, and if not, do nothing.
 +
Now, you can compile this and place the dll in your game bin folder. This won't work quite yet, because we have to do one more thing, and that thing is...
  
 +
== The Weapon Script ==
 +
This unfinished currently, but if anyone would be willing to add this section in greater detail before I add it, that would be fairly appreciated. Thank you for reading.
  
 
[[Category:Weapons programming]]
 
[[Category:Weapons programming]]

Revision as of 21:59, 22 October 2018

Overview

It should not come as a surprise that the Source engine has a very robust (although sometimes frustrating) weapons system. This article will cover creating a weapon entity from scratch.
For more information on the topic of weapons programming, consider visiting the weapons programming category.

Getting Started

To note, this tutorial expects the reader is using Source SDK 2013 Singleplayer. To get started, create a new .cpp file in the server project and call it weapon_tutorial.cpp. Type this into your newly created file.

#include "cbase.h"
#include "basehlcombatweapon.h"
#include "basecombatcharacter.h"
#include "player.h"
#include "gamerules.h"
#include "in_buttons.h"
#include "soundent.h"
#include "game.h"
#include "vstdlib\random.h"
#include "gamestats.h"
#include "npcevent.h"

#include "tier0\memdbgon.h"

These are all the files we need to include for our weapon to work. If we do not include most these, our weapon just simply would not compile.

Creating The Class

Now we will define the class and all of our functions, so it is easier on us down the line. Copy this code down into your file.

class CWeaponTutorial : public CBaseHLCombatWeapon
{
	

public:
	DECLARE_CLASS( CWeaponTutorial, CBaseHLCombatWeapon );
        DECLARE_DATADESC( );
        DECLARE_ACTTABLE( );

	CWeaponTutorial( );

	DECLARE_SERVERCLASS( );

	void			Precache( );
	void			ItemPreFrame( );
	void			ItemBusyFrame( );
	void			ItemPostFrame( );
	void			PrimaryAttack( );
	void			AddViewKick( );
	void			DryFire( );
	virtual bool	        Reload( void );
};

Here is a quick explanation of what is going on here. If you have read the My First Entity series of tutorials, you can see that instead of using CLogicalEntity or CBaseAnimating as our BaseClass, we are using CBaseHLCombatWeapon, which contains all of the functions and logic we will be needing to create this weapon. After this, we are simply doing the usual, and declaring our class and data description, and we are also defining an acttable. This simply tells our weapon which animations to use. Finally, we are simply making our constructor, and making stubs for all of our functions. For more information on what any of these function stubs do, take a quick look at Authoring a weapon entity, but this is not required because later I will be giving a very simple explanation of what these are for and do. After this, type:

virtual const Vector& GetBulletSpread( )
	{
		static Vector cone = VECTOR_CONE_1DEGREES, npcCone = VECTOR_CONE_1DEGREES;
		if (GetOwner( ) && GetOwner( )->IsNPC( )) //Always handle NPCs first
			return npcCone;
		else
			return cone;
	}

All this is doing is simply defining the bullet spread, which is one cone. After this, finally copy this down:

private:
	float m_flRateOfFire;

All this is doing is defining a float named m_flRateOfFire, which as you guessed, is our rate of fire. We will define this later.

Before Writing the Functions

Before we write the functions, there are a few simple things we have to do. First, we are going to link our weapon to an entity, so it can be spawned in game, either with the give command or by any other means, and precache it. Write this code down.

LINK_ENTITY_TO_CLASS( weapon_tutorial, CWeaponTutorial );
PRECACHE_WEAPON_REGISTER( weapon_tutorial );

Next, we will define our data description.

BEGIN_DATADESC( CWeaponTutorial )
	DEFINE_FIELD( m_flRateOfFire, FIELD_FLOAT ),
END_DATADESC( )

If you have read the My First Entity series, you will know what this is doing. If we were feeling fancy, we could even link this value to a ConVar so we could define it in game. Finally, we will define the acttable, so our weapon knows which animations to use. For this tutorial, we will be using the SMG animations.

acttable_t	CWeaponTutorial::m_acttable[] =
{
	{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true },
	{ ACT_RELOAD, ACT_RELOAD_SMG1, true },
	{ ACT_IDLE, ACT_IDLE_SMG1, true },
	{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true },
	{ ACT_WALK, ACT_WALK_RIFLE, true },
	{ ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true },
};

IMPLEMENT_ACTTABLE( CWeaponTutorial );

Programming the Functions

Now, we will finally write the code that will make everything come together. To start off, we will write the code for the constructor. What it does is fairly self-explanatory.

CWeaponTutorial::CWeaponTutorial( )
{
	m_fMinRange1 = 24;
	m_fMaxRange1 = 3000;
	m_bFiresUnderwater = true;
}

Next, we will move on to the precache class. Since we are using the SMG models, we will precache those. This also sets our rate of fire to 10 rounds per second.

void CWeaponTutorial::Precache( )
{
	m_flRateOfFire = 0.1f;

	PrecacheModel( "models/weapons/v_smg1.mdl", true );
	PrecacheModel( "models/weapons/w_smg1.mdl", true );
	BaseClass::Precache( );
}

After this, we will define our DryFire function. What this function does is also fairly self-explanatory.

void CWeaponTutorial::DryFire( )
{
	WeaponSound( EMPTY );
	SendWeaponAnim( ACT_VM_DRYFIRE );

	m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration( );
}

For this one, I am going to knock out three functions in one block, mainly because they do not do anything and they are just here so I can explain what they do and when you might need them.

void CWeaponTutorial::ItemPreFrame( )
{
	BaseClass::ItemPreFrame( );
}

void CWeaponTutorial::ItemBusyFrame( )
{
	BaseClass::ItemBusyFrame( );
}

void CWeaponTutorial::ItemPostFrame( )
{
	BaseClass::ItemPostFrame( );
}

ItemPreFrame is called before the player movement function. ItemBusyFrame is called with the player movement function. ItemPostFrame is called after the player movement function. Out of these three, ItemPostFrame is the most useful. Now on to the most complicated of these functions, PrimaryAttack. This one requires the most explanation, mainly because we are writing the firing logic ourselves.

void CWeaponTutorial::PrimaryAttack( )
{
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner( ) ); //This gets the current player holding the weapon
	Vector vecSrc = pPlayer->Weapon_ShootPosition( );   //This simply just gets the current position of the player.
	Vector vecAim = pPlayer->GetAutoaimVector( 0.0 );   //This gets where the player is looking, but also corrected by autoaim.
	pPlayer->FireBullets( 1, vecSrc, vecAim, GetBulletSpread( ), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 1, entindex( ), 0, 30, GetOwner( ), true, true );
	//This is a lengthy one. All of the args in here are data for our bullet. We could simply use
	//BaseClass::PrimaryAttack, but this gives us no control over how our weapon is fired.
	//So instead, we use this and give it all our info for our bullet.
	//The next 2 are just where the bullet is being fired from and where it should go.
	//Next is how far the bullet is fired, and after that is what type of ammo we use.
	//After this is the tracer freq, which really doesnt matter.
	//Next is the id of the entity firing the weapon, and the attachment id. These 2 dont really matter.
	//Next is how much damage each bullet should do, which is 30.
	//Next is what entity is firing the bullet, again.
	//The final 2 define wether our first shot should be accurate, and wether this is the primary attack.
	WeaponSound( SINGLE ); //This makes our weapon emit the single show sound.
	SendWeaponAnim( ACT_VM_PRIMARYATTACK ); //This sends the animation for us shooting.
	m_flNextPrimaryAttack = gpGlobals->curtime + m_flRateOfFire; //This defines when our next attack should be
	AddViewKick( ); //Don't forget to add our viewkick
}

After this, we only have 2 more functions to go, so congratulations, you made it! We will now do the View Kick function, which is fairly simple.

void CWeaponTutorial::AddViewKick( )
{
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner( ) );
	QAngle punch;
	punch += QAngle( -0.2, 0.0, 0.0 );
	pPlayer->ViewPunch( punch );
}

This just simply creates recoil straight up by adding .2 to our QAngle every time we fire. Now, for the final, and one of the simplest functions, reload.

bool CWeaponTutorial::Reload( )
{
	bool fRet = DefaultReload( GetMaxClip1( ), GetMaxClip2( ), ACT_VM_RELOAD );
	if (fRet)
		WeaponSound( RELOAD );
	return fRet;
}

All this does if checks if we must reload, and if we do, play the animation and sound, and if not, do nothing. Now, you can compile this and place the dll in your game bin folder. This won't work quite yet, because we have to do one more thing, and that thing is...

The Weapon Script

This unfinished currently, but if anyone would be willing to add this section in greater detail before I add it, that would be fairly appreciated. Thank you for reading.