dllsm16.h
#ifndef TOD_M16_H
#define TOD_M16_H
class CM16 : public CBasePlayerWeapon
{
public:
virtual void Spawn( void );
virtual void Precache( void );
virtual int iItemSlot( void ) { return M16_SLOT; }
virtual int GetItemInfo(ItemInfo *p);
virtual int AddToPlayer( CBasePlayer *pPlayer );
virtual void PrimaryAttack( void );
virtual BOOL Deploy( void );
virtual void Reload( void );
virtual void WeaponIdle( void );
virtual BOOL UseDecrement( void ) { return TRUE; }
private:
int m_iShell;
unsigned short m_event;
};
class CM16AmmoClip : public CBasePlayerAmmo
{
virtual void Spawn( void );
virtual void Precache( void );
virtual BOOL AddAmmo( CBaseEntity *pOther ) ;
};
Notice that we have two classes here, CM16 and CM16AmmoClip. The firstenum m16a1_e
{
M16_IDLE_SIL = 0,
M16_IDLE,
M16_FIRE1,
M16_FIRE2,
M16_FIRE3,
M16_RELOAD,
M16_DEPLOY,
};
#endif
These are your models animations and must match those in your model. Thedllsm16.cpp
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "soundent.h"
#include "gamerules.h"
#include "m16.h"
These are the standard includes for all weapons, plus I have added anLINK_ENTITY_TO_CLASS( weapon_m16, CM16 );
LINK_ENTITY_TO_CLASS( ammo_m16, CM16AmmoClip );
These link the weapon and ammo clip classes to entities that can bevoid CM16::Spawn( )
{
pev->classname = MAKE_STRING("weapon_m16"); // hack to allow for old names
Precache( );
SET_MODEL(ENT(pev), M16_MODEL_WORLD);
m_iId = WEAPON_M16;
m_iDefaultAmmo = M16_DEFAULT_AMMO;
FallInit(); // get ready to fall down.
}
This is our spawn function. Notice that I tend to use defines for most thingsvoid CM16::Precache( void )
{
PRECACHE_MODEL(M16_MODEL_1STPERSON);
PRECACHE_MODEL(M16_MODEL_3RDPERSON);
PRECACHE_MODEL(M16_MODEL_WORLD);
m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell
PRECACHE_SOUND (M16_SOUND_FIRE1);
PRECACHE_SOUND (M16_SOUND_FIRE2);
m_event = PRECACHE_EVENT( 1, "events/m16.sc" );
}
This is where we load any of the models or sounds that our weapon uses. Allint CM16::GetItemInfo(ItemInfo *p)
{
p->pszName = STRING(pev->classname);
p->pszAmmo1 = "ammo_m16"; // The type of ammo it uses
p->iMaxAmmo1 = M16_MAX_AMMO; // Max ammo the player can carry
p->pszAmmo2 = NULL; // No secondary ammo
p->iMaxAmmo2 = -1;
p->iMaxClip = M16_DEFAULT_AMMO; // The clip size
p->iSlot = M16_SLOT - 1; // The number in the HUD
p->iPosition = M16_POSITION; // The position in a HUD slot
p->iFlags = ITEM_FLAG_NOAUTOSWITCHEMPTY | ITEM_FLAG_SELECTONEMPTY;
p->iId = m_iId = WEAPON_M16; // The weapon id
p->iWeight = M16_WEIGHT; // for autoswitching
return 1;
}
Here we set the information for the weapon. The secondary ammo has beenint CM16::AddToPlayer( CBasePlayer *pPlayer )
{
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
{
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
WRITE_BYTE( m_iId );
MESSAGE_END();
return TRUE;
}
return FALSE;
}
BOOL CM16::Deploy( )
{
return DefaultDeploy( M16_MODEL_1STPERSON, M16_MODEL_3RDPERSON,
M16_DEPLOY, "mp5" );
}
There isn't anything you will want to change in AddToPlayer. The only interestingvoid CM16::PrimaryAttack()
{
// don't fire underwater
if (m_pPlayer->pev->waterlevel == 3)
{
PlayEmptySound( );
m_flNextPrimaryAttack = 0.15;
return;
}
// don't fire if empty
if (m_iClip <= 0)
{
PlayEmptySound();
m_flNextPrimaryAttack = 0.15;
return;
}
// Weapon sound
m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;
// one less round in the clip
m_iClip--;
// add a muzzle flash
m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
// player "shoot" animation
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
// fire off a round
Vector vecSrc(m_pPlayer->GetGunPosition());
Vector vecAim(m_pPlayer->GetAutoaimVector(AUTOAIM_2DEGREES));
Vector vecAcc(VECTOR_CONE_6DEGREES);
Vector vecDir(m_pPlayer->FireBulletsPlayer( 1, // how many shots
vecSrc,
vecAim,
vecAcc, // accuracy
8192, // max range
BULLET_PLAYER_M16, // bullet type
0, // tracer frequency
0, // damage
m_pPlayer->pev,
m_pPlayer->random_seed ));
// Fire off the client side event
PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_event, 0.0,
(float *)&g_vecZero, (float *)&g_vecZero,
vecDir.x, vecDir.y, 0, 0, (m_iClip ? 0 : 1), 0 );
// Add a delay before the player can fire the next shot
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + M16_FIRE_DELAY;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() +
UTIL_SharedRandomFloat(m_pPlayer->random_seed,
M16_FIRE_DELAY + 1, M16_FIRE_DELAY + 2);
}
This is the meat of the weapon, it fires off one round when the player pressesvoid CM16::Reload( void )
{
DefaultReload( M16_DEFAULT_AMMO, M16_RELOAD, M16_RELOAD_TIME );
}
void CM16::WeaponIdle( void )
{
ResetEmptySound( );
m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase())
return;
SendWeaponAnim( M16_IDLE );
m_flTimeWeaponIdle = UTIL_SharedRandomFloat(m_pPlayer->random_seed, 10, 15);
}
void CM16AmmoClip::Spawn( void )
{
Precache( );
SET_MODEL(ENT(pev), "models/w_9mmARclip.mdl");
CBasePlayerAmmo::Spawn( );
}
void CM16AmmoClip::Precache( void )
{
PRECACHE_MODEL ("models/w_9mmARclip.mdl");
PRECACHE_SOUND("items/9mmclip1.wav");
}
BOOL CM16AmmoClip::AddAmmo( CBaseEntity *pOther )
{
int bResult = (pOther->GiveAmmo(M16_DEFAULT_AMMO, "ammo_m16",
M16_MAX_AMMO) != -1);
if (bResult)
{
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
}
return bResult;
}
These methods round out our weapon. Now lets get on to weapons.h and adddllsweapons.h
#define WEAPON_HANDGRENADE 12
#define WEAPON_TRIPMINE 13
#define WEAPON_SATCHEL 14
#define WEAPON_SNARK 15
#define WEAPON_M16 16
This must be a unique value.
// weapon weight factors (for auto-switching) (-1 = noswitch)
#define CROWBAR_WEIGHT 0
#define GLOCK_WEIGHT 10
#define PYTHON_WEIGHT 15
#define MP5_WEIGHT 15
#define M16_WEIGHT 15
Our M16 will have the same switching weight as the MP5
#define M16_MODEL_1STPERSON "models/v_m16a1.mdl"
#define M16_MODEL_3RDPERSON "models/p_m16a1.mdl"
#define M16_MODEL_WORLD "models/w_m16a1.mdl"
#define M16_SOUND_FIRE1 "weapons/m16a1_fire-1.wav"
#define M16_SOUND_FIRE2 "weapons/m16a1_fire-2.wav"
#define M16_SOUND_VOLUME 0.85
#define M16_FIRE_DELAY 0.085 // was: .15 For comparison, glock's is 0.2
#define M16_RELOAD_TIME 2.0
#define M16_DEFAULT_AMMO 30
#define M16_MAX_AMMO 180
#define M16_SLOT 2
#define M16_POSITION 1
Add this in with the other defines in weapons.h and adjust the slot and// bullet types
typedef enum
{
BULLET_NONE = 0,
BULLET_PLAYER_9MM, // glock
BULLET_PLAYER_MP5, // mp5
BULLET_PLAYER_357, // python
BULLET_PLAYER_BUCKSHOT, // shotgun
BULLET_PLAYER_CROWBAR, // crowbar swipe
BULLET_PLAYER_M16,
BULLET_MONSTER_9MM,
BULLET_MONSTER_MP5,
BULLET_MONSTER_12MM,
} Bullet;
This adds our new bullet type to the game. Now lets set up the damage thisdllscombat.cpp
else switch(iBulletType)
{
default:
case BULLET_PLAYER_9MM:
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg9MM,
vecDir, &tr, DMG_BULLET);
break;
case BULLET_PLAYER_MP5:
case BULLET_PLAYER_M16:
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgMP5,
vecDir, &tr, DMG_BULLET);
break;
This gives the M16 the same damage as the MP5. If you want it to have it's owndllsfunc_break.cpp
const char *CBreakable::pSpawnObjects[] =
{
NULL, // 0
"item_battery", // 1
"item_healthkit", // 2
"weapon_9mmhandgun",// 3
"ammo_9mmclip", // 4
"weapon_9mmAR", // 5
"ammo_9mmAR", // 6
"ammo_ARgrenades", // 7
"weapon_shotgun", // 8
"ammo_buckshot", // 9
"weapon_crossbow", // 10
"ammo_crossbow", // 11
"weapon_357", // 12
"ammo_357", // 13
"weapon_rpg", // 14
"ammo_rpgclip", // 15
"ammo_gaussclip", // 16
"weapon_handgrenade",// 17
"weapon_tripmine", // 18
"weapon_satchel", // 19
"weapon_snark", // 20
"weapon_hornetgun", // 21
"weapon_m16",
"ammo_m16",
};
And now, move on to weapons.cpp to precache our weapon;
dllsweapons.cpp
// called by worldspawn
void W_Precache(void)
{
memset(CBasePlayerItem::ItemInfoArray, 0,
sizeof(CBasePlayerItem::ItemInfoArray));
memset(CBasePlayerItem::AmmoInfoArray, 0,
sizeof(CBasePlayerItem::AmmoInfoArray));
giAmmoIndex = 0;
// custom items...
// m16
UTIL_PrecacheOtherWeapon( "weapon_m16" );
UTIL_PrecacheOther( "ammo_m16" );
Now, if you want to automatically give your weapon to a player in a game,dllsmultiplay_gamerules.cpp
void CHalfLifeMultiplay :: PlayerSpawn( CBasePlayer *pPlayer )
{
BOOL addDefault;
CBaseEntity *pWeaponEntity = NULL;
pPlayer->pev->weapons |= (1<Touch( pPlayer );
addDefault = FALSE;
}
if ( addDefault )
{
pPlayer->GiveNamedItem( "weapon_crowbar" );
pPlayer->GiveNamedItem( "weapon_9mmhandgun" );
pPlayer->GiveAmmo( 68, "9mm", _9MM_MAX_CARRY );// 4 full reloads
pPlayer->GiveNamedItem( "weapon_m16" );
pPlayer->GiveAmmo( M16_MAX_AMMO, "ammo_m16", M16_MAX_AMMO );
}
}
cl_dllev_hldm.cpp
#include "m16.h";
Next we will add the following line near the top.
cl_dllev_hldm.cpp
void EV_TripmineFire( struct event_args_s *args );
void EV_SnarkFire( struct event_args_s *args );
void EV_FireM16( struct event_args_s *args );
Then add the following code at the bottom;
//======================
// M16 START
//======================
void EV_FireM16( event_args_t *args )
{
int idx;
vec3_t origin;
vec3_t angles;
vec3_t velocity;
vec3_t ShellVelocity;
vec3_t ShellOrigin;
int shell;
vec3_t vecSrc, vecAiming;
vec3_t up, right, forward;
float flSpread = 0.01;
idx = args->entindex;
VectorCopy( args->origin, origin );
VectorCopy( args->angles, angles );
VectorCopy( args->velocity, velocity );
AngleVectors( angles, forward, right, up );
if ( EV_IsLocal( idx ) )
{
// Add muzzle flash to current weapon model
EV_MuzzleFlash();
gEngfuncs.pEventAPI->EV_WeaponAnimation(M16_FIRE1 +
gEngfuncs.pfnRandomLong(0,2), 2);
// This gives it a bit of kick, adjust the numbers to your liking
V_PunchAxis( 0, gEngfuncs.pfnRandomFloat( -2, 2 ) );
}
// Eject shells, adjust the last three numbers to move the start point that the
// shells eject from to fit your model
shell = gEngfuncs.pEventAPI->EV_FindModelIndex ("models/shell.mdl");
EV_GetDefaultShellInfo( args, origin, velocity, ShellVelocity,
ShellOrigin, forward, right, up, 12, -10, 7 );
EV_EjectBrass(ShellOrigin,
ShellVelocity,
angles[ YAW ],
shell,
TE_BOUNCE_SHELL);
// Play the fire sound
switch( gEngfuncs.pfnRandomLong( 0, 1 ) )
{
case 0:
gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON,
"weapons/m16a1_fire-1.wav",
1, ATTN_NORM, 0,
94 + gEngfuncs.pfnRandomLong( 0, 0xf ) );
break;
case 1:
gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON,
"weapons/m16a1_fire-2.wav",
1, ATTN_NORM, 0,
94 + gEngfuncs.pfnRandomLong( 0, 0xf ) );
break;
}
// Fire off the bullets client side
EV_GetGunPosition( args, vecSrc, origin );
VectorCopy( forward, vecAiming );
EV_HLDM_FireBullets( idx,
forward,
right,
up,
1,
vecSrc,
vecAiming,
8192,
BULLET_PLAYER_M16,
0,
0,
args->fparam1, // These are the accuracy passed from
args->fparam2 ); // PLAYBACK_EVENT_FULL on the server
}
Most of this is self explanatory. Notice the similarities between thecl_dllhlhl_events.cpp
void EV_HornetGunFire( struct event_args_s *args );
void EV_SnarkFire( struct event_args_s *args );
void EV_FireM16( struct event_args_s *args );
Then further down;
void Game_HookEvents( void )
{
gEngfuncs.pfnHookEvent( "events/python.sc", EV_FirePython );
gEngfuncs.pfnHookEvent( "events/gauss.sc", EV_FireGauss );
gEngfuncs.pfnHookEvent( "events/gaussspin.sc", EV_SpinGauss );
gEngfuncs.pfnHookEvent( "events/crossbow1.sc", EV_FireCrossbow );
gEngfuncs.pfnHookEvent( "events/crossbow2.sc", EV_FireCrossbow2 );
gEngfuncs.pfnHookEvent( "events/shotgun1.sc", EV_FireShotGunSingle );
gEngfuncs.pfnHookEvent( "events/shotgun2.sc", EV_FireShotGunDouble );
gEngfuncs.pfnHookEvent( "events/egon_fire.sc", EV_EgonFire );
gEngfuncs.pfnHookEvent( "events/egon_stop.sc", EV_EgonStop );
gEngfuncs.pfnHookEvent( "events/firehornet.sc", EV_HornetGunFire );
gEngfuncs.pfnHookEvent( "events/snarkfire.sc", EV_SnarkFire );
gEngfuncs.pfnHookEvent( "events/rpg.sc", EV_FireRpg );
gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire );
gEngfuncs.pfnHookEvent( "events/m16.sc", EV_FireM16 );
Tired yet? Okay, we're almost done. Open up hl_weapons.cpp and add ancl_dllhlhl_weapons.cpp
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "usercmd.h"
#include "entity_state.h"
#include "demo_api.h"
#include "pm_defs.h"
#include "event_api.h"
#include "r_efx.h"
#include "../hud_iface.h"
#include "../com_weapons.h"
#include "../demo.h"
#include "m16.h"
Then add a global for your weapon near the top with the rest of theCTripmine g_Tripmine;
CSqueak g_Snark;
CM16 g_M16;
Add a HUD_PrepEntity for the weapon in HUD_InitClientWeapons().
HUD_PrepEntity( &g_Tripmine , &player );
HUD_PrepEntity( &g_Snark , &player );
HUD_PrepEntity( &g_M16 , &player );
Lastly, add your weapon to the switch statement in HUD_WeaponsPostThink()
switch ( from->client.m_iId )
{
case WEAPON_CROWBAR:
pWeapon = &g_Crowbar;
break;
case WEAPON_GLOCK:
pWeapon = &g_Glock;
break;
case WEAPON_M16:
pWeapon = &g_M16;
break;
Whew, that's it! You should now have a functioning weapon. Compile theYou must log in to post a comment. You can login or register a new account.