First of all, this code was implemented in the
official Half-Life SDK released by Valve.
As a final comment, please excuse me if my code contains unnecessary or redundant lines (I'm not really an expert). I hope someone takes the initiative to even make a wiki page with this humble contribution.
First, I added the following lines of code in
weapons.h
:
// NEW MINIGUN
class CMinigun : public CBasePlayerWeapon
{
public:
void Spawn( void );
void Precache( void );
int iItemSlot( void ) { return 3; }
int GetItemInfo(ItemInfo *p);
int AddToPlayer(CBasePlayer *pPlayer);
BOOL Deploy( void );
void PrimaryAttack( void );
void MinigunFire( void );
void Reload( void );
void WeaponIdle( void );
void Holster( int skiplocal = 0 );
int m_iShell;
virtual BOOL UseDecrement( void )
{
#if defined( CLIENT_WEAPONS )
return TRUE;
#else
return FALSE;
#endif
}
private:
unsigned short m_usSpinUpMinigun;
unsigned short m_usFireMinigun;
};
Then, I created an element called
minigun.cpp
in
hldll. All the code it contains is as follows:
// NEW MINIGUN
#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"
enum minigun_e {
MINIGUN_IDLE1 = 0,
MINIGUN_IDLE2,
MINIGUN_SPINUP,
MINIGUN_SPINDOWN,
MINIGUN_FIRE,
MINIGUN_FIRE_EMPTY,
MINIGUN_RELOAD,
MINIGUN_DRAW,
MINIGUN_HOLSTER
};
LINK_ENTITY_TO_CLASS( weapon_minigun, CMinigun );
void CMinigun::Spawn()
{
pev->classname = MAKE_STRING("weapon_minigun");
Precache();
m_iId = WEAPON_MINIGUN;
SET_MODEL(ENT(pev), "models/w_minigun.mdl");
m_iDefaultAmmo = MINIGUN_DEFAULT_GIVE;
FallInit();
}
void CMinigun::Precache( void )
{
PRECACHE_MODEL("models/v_minigun.mdl");
PRECACHE_MODEL("models/w_minigun.mdl");
PRECACHE_MODEL("models/p_minigun.mdl");
PRECACHE_SOUND("weapons/minigun_fire.wav");
PRECACHE_SOUND("weapons/minigun_spinup.wav");
PRECACHE_SOUND("weapons/minigun_spindown.wav");
m_usSpinUpMinigun = PRECACHE_EVENT( 1, "events/minigun1.sc" );
m_usFireMinigun = PRECACHE_EVENT( 1, "events/minigun2.sc" );
m_iShell = PRECACHE_MODEL("models/shell.mdl");
PRECACHE_SOUND("weapons/357_cock1.wav");
}
int CMinigun::GetItemInfo(ItemInfo *p)
{
p->pszName = STRING(pev->classname);
p->pszAmmo1 = "chaingun";
p->iMaxAmmo1 = MINIGUN_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = MINIGUN_MAX_CLIP;
p->iSlot = 2;
p->iPosition = 4;
p->iFlags = 0;
p->iId = m_iId = WEAPON_MINIGUN;
p->iWeight = MINIGUN_WEIGHT;
return 1;
}
int CMinigun::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 CMinigun::Deploy()
{
return DefaultDeploy( "models/v_minigun.mdl", "models/p_minigun.mdl", MINIGUN_DRAW, "mp5" );
}
void CMinigun::Holster( int skiplocal /* = 0 */ )
{
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
SendWeaponAnim( MINIGUN_HOLSTER );
m_fInAttack = 0;
}
void CMinigun::PrimaryAttack( void )
{
if ( m_iClip <= 0 )
{
PlayEmptySound();
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5;
return;
}
if ( m_fInAttack == 0 )
{
PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_usSpinUpMinigun, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 );
m_flTimeWeaponIdle = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.6;
SendWeaponAnim( MINIGUN_SPINUP );
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_spinup.wav", 1, ATTN_NORM);
m_fInAttack = 1;
return;
}
else if ( m_fInAttack == 1 )
{
MinigunFire();
return;
}
}
void CMinigun::MinigunFire()
{
m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
SendWeaponAnim( MINIGUN_FIRE );
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_iClip--;
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
Vector vecShellVelocity = m_pPlayer->pev->velocity + gpGlobals->v_right * RANDOM_FLOAT(50,70) + gpGlobals->v_up * RANDOM_FLOAT(100,150) + gpGlobals->v_forward * 25;
EjectBrass( pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_up * -12 + gpGlobals->v_forward * 32 + gpGlobals->v_right * 6 , vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL );
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_fire.wav", 1, ATTN_NORM);
Vector vecSrc = m_pPlayer->GetGunPosition();
Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
Vector vecDir;
#ifdef CLIENT_DLL
if ( !bIsMultiplayer() )
#else
if ( !g_pGameRules->IsMultiplayer() )
#endif
{
vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES, 8192, BULLET_PLAYER_MINIGUN, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
}
else
{
vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MINIGUN, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
}
int flags;
#if defined( CLIENT_WEAPONS )
flags = FEV_NOTHOST;
#else
flags = 0;
#endif
PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usFireMinigun, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 );
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1;
}
void CMinigun::WeaponIdle( void )
{
if ( m_flTimeWeaponIdle >= 0.6 )
{
if ( m_fInAttack == 1 )
{
m_flTimeWeaponIdle = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.4;
SendWeaponAnim( MINIGUN_SPINDOWN );
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_spindown.wav", 1, ATTN_NORM);
m_fInAttack = 0;
return;
}
}
ResetEmptySound();
if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
int iAnim;
switch ( RANDOM_LONG( 0, 1 ) )
{
case 0:
iAnim = MINIGUN_IDLE1;
break;
default:
case 1:
iAnim = MINIGUN_IDLE2;
break;
}
SendWeaponAnim( iAnim );
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
void CMinigun::Reload( void )
{
if ( m_pPlayer->ammo_9mm <= 0 )
return;
DefaultReload( MINIGUN_MAX_CLIP, MINIGUN_RELOAD, 1.5 );
}
Everything previously written would have been useless if I had not written the following in
ev_hldm.cpp
(
hl_cdll):
// NEW MINIGUN
//======================
// MINIGUN START
//======================
enum minigun_e {
MINIGUN_IDLE1 = 0,
MINIGUN_IDLE2,
MINIGUN_SPINUP,
MINIGUN_SPINDOWN,
MINIGUN_FIRE,
MINIGUN_FIRE_EMPTY,
MINIGUN_RELOAD,
MINIGUN_DRAW,
MINIGUN_HOLSTER
};
void EV_SpinUpMinigun( event_args_t *args )
{
int idx;
vec3_t origin;
vec3_t angles;
vec3_t velocity;
idx = args->entindex;
VectorCopy( args->origin, origin );
VectorCopy( args->angles, angles );
VectorCopy( args->velocity, velocity );
gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/minigun_spinup.wav", 1, ATTN_NORM, 0, PITCH_NORM);
if ( EV_IsLocal( idx ) )
{
gEngfuncs.pEventAPI->EV_WeaponAnimation( MINIGUN_SPINUP, 1 );
}
}
void EV_FireMinigun( event_args_t *args )
{
int idx;
vec3_t origin;
vec3_t angles;
vec3_t velocity;
int empty;
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 );
empty = args->bparam1;
AngleVectors( angles, forward, right, up );
shell = gEngfuncs.pEventAPI->EV_FindModelIndex ("models/shell.mdl");
if ( EV_IsLocal( idx ) )
{
EV_MuzzleFlash();
gEngfuncs.pEventAPI->EV_WeaponAnimation( empty ? MINIGUN_FIRE_EMPTY : MINIGUN_FIRE, 2 );
V_PunchAxis( -2, 0 );
}
EV_GetDefaultShellInfo( args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 20, -12, 4 );
EV_EjectBrass( ShellOrigin, ShellVelocity, angles[ YAW ], shell, TE_BOUNCE_SHELL );
gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/minigun_fire.wav", 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf ) );
EV_GetGunPosition( args, vecSrc, origin );
VectorCopy( forward, vecAiming );
if ( gEngfuncs.GetMaxClients() > 1 )
{
EV_HLDM_FireBullets( idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_MINIGUN, 2, &tracerCount[idx-1], args->fparam1, args->fparam2 );
}
else
{
EV_HLDM_FireBullets( idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_MINIGUN, 2, &tracerCount[idx-1], args->fparam1, args->fparam2 );
}
}
//======================
// MINIGUN END
//======================
For the other details that I did not comment on here, I suggest reviewing
this article written by Penguinboy about weapons programming, I relied entirely on that article to make my new weapon.