Difference between revisions of "Authoring your first weapon entity"

From Valve Developer Community
Jump to: navigation, search
m (Atom moved page Making a weapon entity to Authoring your first weapon entity: Better explains article)
(syntaxhighlight, stub section)
 
(15 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{toc-top}}
+
{{stub}}
  
 
== Overview ==
 
== Overview ==
It should not come as a surprise that the Source engine has a very robust weapon system. This article will cover creating a weapon entity from scratch.
+
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.
Various topics on creating a weapon will be discussed.
+
<br>
 +
For more information on the topic of weapons programming, consider visiting the [[:Category:Weapons_programming|weapons programming]] category.
  
 
== Getting Started ==
 
== Getting Started ==
To get started, create a new .cpp file in the server project and call it weapon_myfirstweapon.
+
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>
+
<syntaxhighlight lang="cpp">
 
#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 "c_hl2mp_player.h"
 
#else
 
#include "hl2mp_player.h"
 
#endif
 
 
#include "weapon_hl2mpbasehlmpcombatweapon.h"
 
  
#ifndef CLIENT_DLL
+
#include "tier0\memdbgon.h"
#define CWeaponMyFirstWeapon C_WeaponMyFirstWeapon
+
</syntaxhighlight>
#endif
+
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.
</pre>
 
Just to note something here, this tutorial expects the reader is using the Source SDK Multiplayer, but if the reader wanted to make the weapon singleplayer, it should be an easy fix.
 
  
 
== Creating The Class ==
 
== 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
+
<syntaxhighlight lang="cpp">
<pre>
+
class CWeaponTutorial : public CBaseHLCombatWeapon
class CWeaponMyFirstWeapon : public CBaseHL2MPCombatWeapon
+
{
</pre>
+
  
Now, open it up with some curly braces, 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();
 
</pre>
 
The code above just starts off the file, makes so it works with the server, and creates the base for the constructor.
 
  
== Creating The Functions ==
+
void Precache( );
After the line that says <code>DEALRE_PREDICTABLE</code>, type these functions in
+
void ItemPreFrame( );
<pre>
+
void ItemBusyFrame( );
void Precache(void);
+
void ItemPostFrame( );
void ItemPreFrame(void);
+
void PrimaryAttack( );
void ItemBusyFrame(void);
+
void AddViewKick( );
void ItemPostFrame(void);
+
void DryFire( );
void PrimaryAttack(void);
+
virtual bool         Reload( void );
void AddViewKick(void);
+
};
 +
</syntaxhighlight>
 +
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_t]]. 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:
 +
<syntaxhighlight lang="cpp">
 +
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;
 +
}
 +
</syntaxhighlight>
 +
All this is doing is simply defining the bullet spread, which is one cone. After this, finally copy this down:
 +
<syntaxhighlight lang="cpp">
 +
private:
 +
float m_flRateOfFire;
 +
</syntaxhighlight>
 +
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.
  
virtual bool Reload(void);
+
== 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.
 +
<syntaxhighlight lang="cpp">
 +
LINK_ENTITY_TO_CLASS( weapon_tutorial, CWeaponTutorial );
 +
PRECACHE_WEAPON_REGISTER( weapon_tutorial );
 +
</syntaxhighlight>
 +
Next, we will define our data description.
 +
<syntaxhighlight lang="cpp">
 +
BEGIN_DATADESC( CWeaponTutorial )
 +
DEFINE_FIELD( m_flRateOfFire, FIELD_FLOAT ),
 +
END_DATADESC( )
 +
</syntaxhighlight>
 +
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_t]], so our weapon knows which animations to use. For this tutorial, we will be using the SMG animations.
 +
<syntaxhighlight lang="cpp">
 +
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 },
 +
};
  
virtual const Vector& GetBulletSpread(void)
+
IMPLEMENT_ACTTABLE( CWeaponTutorial );
 +
</syntaxhighlight>
 +
== 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.
 +
<syntaxhighlight lang="cpp">
 +
CWeaponTutorial::CWeaponTutorial( )
 
{
 
{
    static Vector cone = VECTOR_CONE_1DEGREES;
+
m_fMinRange1 = 24;
    return cone;
+
m_fMaxRange1 = 3000;
 +
m_bFiresUnderwater = true;
 
}
 
}
 +
</syntaxhighlight>
 +
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.
 +
<syntaxhighlight lang="cpp">
 +
void CWeaponTutorial::Precache( )
 +
{
 +
m_flRateOfFire = 0.1f;
  
#ifndef CLIENT_DLL
+
PrecacheModel( "models/weapons/v_smg1.mdl", true );
    DECLARE_ACTTABLE();
+
PrecacheModel( "models/weapons/w_smg1.mdl", true );
#endif
+
BaseClass::Precache( );
</pre>
+
}
All this does is create stubs for the functions we will define later, with the exception of <code>GetBulletSpread</code>.
+
</syntaxhighlight>
To understand all of the functions, see [[Authoring a weapon entity]], but the same things will be described later.
+
After this, we will define our <code>DryFire</code> function. What this function does is also fairly self-explanatory.
 +
<syntaxhighlight lang="cpp">
 +
void CWeaponTutorial::DryFire( )
 +
{
 +
WeaponSound( EMPTY );
 +
SendWeaponAnim( ACT_VM_DRYFIRE );
  
== Creating Private Code ==
+
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration( );
In this section, the reader will learn how to create variables, so they can be networked.
+
}
To start off the private section, type
+
</syntaxhighlight>
<pre>
+
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.
private:
+
<syntaxhighlight lang="cpp">
    CNetworkVar(float, m_flRateOfFire);
+
void CWeaponTutorial::ItemPreFrame( )
 +
{
 +
BaseClass::ItemPreFrame( );
 +
}
  
private:
+
void CWeaponTutorial::ItemBusyFrame( )
    CWeaponMyFirstWeapon(const CWeaponMyFirstWeapon &);
+
{
</pre>
+
BaseClass::ItemBusyFrame( );
Basically, this just creates a variable, with the type of a float, called <code>m_flRateOfFire</code>, and creates an alternate constructor.
+
}
To define <code>m_flRateOfFire</code> you can type
 
<pre>
 
m_flRateOfFire = 0.4f; // 60 seconds divided by 150 shots (150 rpm) equals to 0.4
 
</pre>
 
The same can also be done at the top of the file, with a <code>#define</code>.
 
  
== Defining The Functions ==
+
void CWeaponTutorial::ItemPostFrame( )
Now, go out of the braces for the <code>CWeaponMyFirstWeapon</code> class.
+
{
Next, type
+
BaseClass::ItemPostFrame( );
<pre>
+
}
IMPLEMENT_NETWORKCLASS_ALIASED(WeaponMyFirstWeapon, DT_WeaponMyFirstWeapon)
+
</syntaxhighlight>
 
+
<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.
BEGIN_NETWORK_TABLE(CWeaponMyFirstWeapon, DT_WeaponMyFirstWeapon)
+
<syntaxhighlight lang="cpp">
#ifndef CLIENT_DLL
+
void CWeaponTutorial::PrimaryAttack( )
RecvPropFloat(RECVINFO(m_flRateOfFire)),
+
{
#else
+
CBasePlayer *pPlayer = ToBasePlayer( GetOwner( ) ); //This gets the current player holding the weapon
SendPropFloat(SENDINFO(m_flRateOfFire)),
+
Vector vecSrc = pPlayer->Weapon_ShootPosition( );  //This simply just gets the current position of the player.
#endif
+
Vector vecAim = pPlayer->GetAutoaimVector( 0.0 );  //This gets where the player is looking, but also corrected by autoaim.
</pre>
+
pPlayer->FireBullets( 1, vecSrc, vecAim, GetBulletSpread( ), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 1, entindex( ), 0, 30, GetOwner( ), true, true );
This code just defines how things will be networked, and what things will be networked.
+
//This is a lengthy one. All of the args in here are data for our bullet. We could simply use
Moving on, to create the weapons entity, type
+
//BaseClass::PrimaryAttack, but this gives us no control over how our weapon is fired.
<pre>
+
//So instead, we use this and give it all our info for our bullet.
LINK_ENTITY_TO_CLASS(weapon_myfirstweapon, CWeaponMyFirstWeapon);
+
//The next 2 are just where the bullet is being fired from and where it should go.
PRECACHE_WEAPON_REGISTER(weapon_myfirstweapon); //This defines what the weapon script we make later will be called
+
//Next is how far the bullet is fired, and after that is what type of ammo we use.
</pre>
+
//After this is the tracer freq, which really doesnt matter.
Now, to declare the acttable. This is not very important, it just defines what animations to use. For more information, check out the docs on the [[acttable_t|acttable]].
+
//Next is the id of the entity firing the weapon, and the attachment id. These 2 dont really matter.
<pre>
+
//Next is how much damage each bullet should do, which is 30.
#ifndef CLIENT_DLL
+
//Next is what entity is firing the bullet, again.
acttable_t CWeaponMyFirstWeapon::m_acttable[] =
+
//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
 +
}
 +
</syntaxhighlight>
 +
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.
 +
<syntaxhighlight lang="cpp">
 +
void CWeaponTutorial::AddViewKick( )
 +
{
 +
CBasePlayer *pPlayer = ToBasePlayer( GetOwner( ) );
 +
QAngle punch;
 +
punch += QAngle( -0.2, 0.0, 0.0 );
 +
pPlayer->ViewPunch( punch );
 +
}
 +
</syntaxhighlight>
 +
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.
 +
<syntaxhighlight lang="cpp">
 +
bool CWeaponTutorial::Reload( )
 
{
 
{
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false },
+
bool fRet = DefaultReload( GetMaxClip1( ), GetMaxClip2( ), ACT_VM_RELOAD );
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false },
+
if (fRet)
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false },
+
WeaponSound( RELOAD );
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false },
+
return fRet;
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false },
+
}
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false },
+
</syntaxhighlight>
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false },
+
All this does if checks if we must reload, and if we do, play the animation and sound, and if not, do nothing.
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, false },
+
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...
};
 
 
 
IMPLEMENT_ACTTABLE(CWeaponMyFirstWeapon);
 
  
#endif
+
== The Weapon Script ==
</pre>
+
{{stub}}
  
 
[[Category:Weapons programming]]
 
[[Category:Weapons programming]]

Latest revision as of 14:37, 23 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_t. 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_t, 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