This is a multi-part message in MIME format.
--
I'm trying to add a weapon with an alt_fire like that of the flare gun or
irifle. When I only had the main fire, everything worked. I added in the flare,
copying from weapon_flaregun, but sound and collisions didn't work right.
When I tried to fix it, hl2 makes it to the loading screen (before title, after
intro videos), then crashes with "InitFastCopy - missing client class 44". I
suspect it has something to do with client/server problems, but beyond that I'm
clueless.
Can someone tell me what I'm doing wrong? Complete code below:
weapon_smg2.h (server)
======================================================================
#include "basehlcombatweapon.h"
#include "soundenvelope.h"
#include "cbase.h"
#include "player.h"
#include "gamerules.h"
#include "decals.h"
#include "IEffects.h"
#include "engine/IEngineSound.h"
#ifndef WEAPON_SMG2_H
#define WEAPON_SMG2_H
#ifdef _WIN32
#pragma once
#endif
#define SF_FLARE_NO_DLIGHT 0x00000001
#define SF_FLARE_NO_SMOKE 0x00000002
#define SF_FLARE_INFINITE 0x00000004
#define SF_FLARE_START_OFF 0x00000008
#define FLARE_DURATION 30.0f
#define FLARE_DECAY_TIME 10.0f
#define FLARE_BLIND_TIME 6.0f
//---------------------
// Flare
//---------------------
class CFlareGrenade : public CBaseCombatCharacter
{
public:
DECLARE_CLASS( CFlareGrenade, CBaseCombatCharacter );
CFlareGrenade();
~CFlareGrenade();
static CFlareGrenade * GetActiveFlares( void );
CFlareGrenade * GetNextFlare( void ) const { return
m_pNextFlare; }
static CFlareGrenade *Create( Vector vecOrigin, QAngle vecAngles,
CBaseEntity *pOwner, float lifetime );
virtual unsigned int PhysicsSolidMaskForEntity( void ) const;
void Spawn( void );
void Precache( void );
int Restore( IRestore &restore );
void Activate( void );
void StartBurnSound( void );
void Start( float lifeTime );
void Die( float fadeTime );
void Launch( const Vector &direction, float speed );
Class_T Classify( void );
void FlareTouch( CBaseEntity *pOther );
void FlareBurnTouch( CBaseEntity *pOther );
void FlareThink( void );
void InputStart( inputdata_t &inputdata );
void InputDie( inputdata_t &inputdata );
void InputLaunch( inputdata_t &inputdata );
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
static CFlareGrenade *activeFlares;
CBaseEntity *m_pOwner;
int m_nBounces; // how many
times has this flare bounced?
CNetworkVar( float, m_flTimeBurnOut ); // when will the flare burn out?
CNetworkVar( float, m_flScale );
float m_flDuration;
float m_flNextDamage;
CSoundPatch *m_pBurnSound;
bool m_bFading;
CNetworkVar( bool, m_bLight );
CNetworkVar( bool, m_bSmoke );
CNetworkVar( bool, m_bPropFlare );
bool m_bInActiveList;
CFlareGrenade * m_pNextFlare;
void RemoveFromActiveFlares( void );
void AddToActiveFlares( void );
};
#define FLARE_LAUNCH_SPEED 1500
BEGIN_DATADESC( CFlareGrenade )
DEFINE_FIELD( m_pOwner, FIELD_CLASSPTR ),
DEFINE_FIELD( m_nBounces, FIELD_INTEGER ),
DEFINE_FIELD( m_flTimeBurnOut, FIELD_TIME ),
DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "scale" ),
DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
DEFINE_FIELD( m_flNextDamage, FIELD_TIME ),
DEFINE_SOUNDPATCH( m_pBurnSound ),
DEFINE_FIELD( m_bFading, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bLight, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bSmoke, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bPropFlare, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bInActiveList, FIELD_BOOLEAN ),
DEFINE_FIELD( m_pNextFlare, FIELD_CLASSPTR ),
//Input functions
DEFINE_INPUTFUNC( FIELD_FLOAT, "Start", InputStart ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Die", InputDie ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Launch", InputLaunch),
// Function Pointers
DEFINE_FUNCTION( FlareTouch ),
DEFINE_FUNCTION( FlareBurnTouch ),
DEFINE_FUNCTION( FlareThink ),
END_DATADESC()
//Data-tables
IMPLEMENT_SERVERCLASS_ST( CFlareGrenade, DT_FlareGrenade )
SendPropFloat( SENDINFO( m_flTimeBurnOut ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flScale ), 0, SPROP_NOSCALE ),
SendPropInt( SENDINFO( m_bLight ), 1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_bSmoke ), 1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_bPropFlare ), 1, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( flare_grenade, CFlareGrenade );
CFlareGrenade *CFlareGrenade::activeFlares = NULL;
CFlareGrenade *CFlareGrenade::GetActiveFlares( void )
{
return CFlareGrenade::activeFlares;
}
Class_T CFlareGrenade::Classify( void )
{
return CLASS_FLARE;
}
CBaseEntity *CreateFlareGrenade( Vector vOrigin, QAngle Angles, CBaseEntity
*pOwner, float flDuration )
{
CFlareGrenade *pFlare = CFlareGrenade::Create( vOrigin, Angles, pOwner,
flDuration );
if ( pFlare )
{
pFlare->m_bPropFlare = true;
}
return pFlare;
}
void KillFlareGrenade( CBaseEntity *pOwnerEntity, CBaseEntity *pEntity, float
flKillTime )
{
CFlareGrenade *pFlare = dynamic_cast< CFlareGrenade *>( pEntity );
if ( pFlare )
{
float flDieTime = (pFlare->m_flTimeBurnOut -
gpGlobals->curtime) - flKillTime;
if ( flDieTime > 1.0f )
{
pFlare->Die( flDieTime );
pOwnerEntity->SetNextThink( gpGlobals->curtime +
flDieTime + 3.0f );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CFlareGrenade::CFlareGrenade( void )
{
m_flScale = 1.0f;
m_nBounces = 0;
m_bFading = false;
m_bLight = true;
m_bSmoke = true;
m_flNextDamage = gpGlobals->curtime;
m_lifeState = LIFE_ALIVE;
m_iHealth = 100;
m_bPropFlare = false;
m_bInActiveList = false;
m_pNextFlare = NULL;
}
CFlareGrenade::~CFlareGrenade()
{
CSoundEnvelopeController::GetController().SoundDestroy( m_pBurnSound );
m_pBurnSound = NULL;
RemoveFromActiveFlares();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::Precache( void )
{
PrecacheModel("models/weapons/flare.mdl" );
PrecacheScriptSound( "Weapon_FlareGun.Burn" );
// FIXME: needed to precache the fire model. Shouldn't have to do this.
UTIL_PrecacheOther( "_firesmoke" );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &restore -
// Output : int
//-----------------------------------------------------------------------------
int CFlareGrenade::Restore( IRestore &restore )
{
int result = BaseClass::Restore( restore );
if ( m_spawnflags & SF_FLARE_NO_DLIGHT )
{
m_bLight = false;
}
if ( m_spawnflags & SF_FLARE_NO_SMOKE )
{
m_bSmoke = false;
}
return result;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::Spawn( void )
{
Precache();
SetModel( "models/weapons/flare.mdl" );
UTIL_SetSize( this, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ) );
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_SOLID );
SetMoveType( MOVETYPE_NONE );
SetFriction( 0.6f );
SetGravity( UTIL_ScaleForGravity( 400 ) );
m_flTimeBurnOut = gpGlobals->curtime + 30;
AddEffects( EF_NOSHADOW|EF_NORECEIVESHADOW );
if ( m_spawnflags & SF_FLARE_NO_DLIGHT )
{
m_bLight = false;
}
if ( m_spawnflags & SF_FLARE_NO_SMOKE )
{
m_bSmoke = false;
}
if ( m_spawnflags & SF_FLARE_INFINITE )
{
m_flTimeBurnOut = -1.0f;
}
if ( m_spawnflags & SF_FLARE_START_OFF )
{
AddEffects( EF_NODRAW );
}
AddFlag( FL_OBJECT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::Activate( void )
{
BaseClass::Activate();
// Start the burning sound if we're already on
if ( ( m_spawnflags & SF_FLARE_START_OFF ) == false )
{
StartBurnSound();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::StartBurnSound( void )
{
if ( m_pBurnSound == NULL )
{
CPASAttenuationFilter filter( this );
m_pBurnSound =
CSoundEnvelopeController::GetController().SoundCreate(
filter, entindex(), CHAN_WEAPON,
"Weapon_FlareGun.Burn", 3.0f );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : vecOrigin -
// vecAngles -
// *pOwner -
// Output : CFlareGrenade
//-----------------------------------------------------------------------------
CFlareGrenade *CFlareGrenade::Create( Vector vecOrigin, QAngle vecAngles,
CBaseEntity *pOwner, float lifetime )
{
CFlareGrenade *pFlare = (CFlareGrenade *) CreateEntityByName(
"env_flare" );
if ( pFlare == NULL )
return NULL;
UTIL_SetOrigin( pFlare, vecOrigin );
pFlare->SetLocalAngles( vecAngles );
pFlare->Spawn();
pFlare->SetTouch( &CFlareGrenade::FlareTouch );
pFlare->SetThink( &CFlareGrenade::FlareThink );
//Start up the flare
pFlare->Start( lifetime );
//Don't start sparking immediately
pFlare->SetNextThink( gpGlobals->curtime + 0.5f );
//Burn out time
pFlare->m_flTimeBurnOut = gpGlobals->curtime + lifetime;
pFlare->RemoveSolidFlags( FSOLID_NOT_SOLID );
pFlare->AddSolidFlags( FSOLID_NOT_STANDABLE );
pFlare->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
pFlare->SetOwnerEntity( pOwner );
pFlare->m_pOwner = pOwner;
return pFlare;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
unsigned int CFlareGrenade::PhysicsSolidMaskForEntity( void ) const
{
return MASK_NPCSOLID;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::FlareThink( void )
{
float deltaTime = ( m_flTimeBurnOut - gpGlobals->curtime );
if ( !m_bInActiveList && ( ( deltaTime > FLARE_BLIND_TIME ) || (
m_flTimeBurnOut == -1.0f ) ) )
{
AddToActiveFlares();
}
if ( m_flTimeBurnOut != -1.0f )
{
//Fading away
if ( ( deltaTime <= FLARE_DECAY_TIME ) && ( m_bFading == false
) )
{
m_bFading = true;
CSoundEnvelopeController::GetController().SoundChangePitch( m_pBurnSound, 60,
deltaTime );
CSoundEnvelopeController::GetController().SoundFadeOut(
m_pBurnSound, deltaTime );
}
// if flare is no longer bright, remove it from active flare
list
if ( m_bInActiveList && ( deltaTime <= FLARE_BLIND_TIME ) )
{
RemoveFromActiveFlares();
}
//Burned out
if ( m_flTimeBurnOut < gpGlobals->curtime )
{
UTIL_Remove( this );
return;
}
}
//Act differently underwater
if ( GetWaterLevel() > 1 )
{
UTIL_Bubbles( GetAbsOrigin() + Vector( -2, -2, -2 ),
GetAbsOrigin() + Vector( 2, 2, 2 ), 1 );
m_bSmoke = false;
}
else
{
//Shoot sparks
if ( random->RandomInt( 0, 8 ) == 1 )
{
g_pEffects->Sparks( GetAbsOrigin() );
}
}
//Next update
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CFlareGrenade::FlareBurnTouch( CBaseEntity *pOther )
{
if ( pOther && pOther->m_takedamage && ( m_flNextDamage <
gpGlobals->curtime ) )
{
pOther->TakeDamage( CTakeDamageInfo( this, m_pOwner, 1,
(DMG_BULLET|DMG_BURN) ) );
m_flNextDamage = gpGlobals->curtime + 1.0f;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CFlareGrenade::FlareTouch( CBaseEntity *pOther )
{
Assert( pOther );
if ( !pOther->IsSolid() )
return;
if ( ( m_nBounces < 10 ) && ( GetWaterLevel() < 1 ) )
{
// Throw some real chunks here
g_pEffects->Sparks( GetAbsOrigin() );
}
//If the flare hit a person or NPC, do damage here.
if ( pOther && pOther->m_takedamage )
{
/*
The Flare is the iRifle round right now. No damage,
just ignite. (sjb)
//Damage is a function of how fast the flare is flying.
int iDamage = GetAbsVelocity().Length() / 50.0f;
if ( iDamage < 5 )
{
//Clamp minimum damage
iDamage = 5;
}
//Use m_pOwner, not GetOwnerEntity()
pOther->TakeDamage( CTakeDamageInfo( this, m_pOwner, iDamage,
(DMG_BULLET|DMG_BURN) ) );
m_flNextDamage = gpGlobals->curtime + 1.0f;
*/
CBaseAnimating *pAnim;
pAnim = dynamic_cast<CBaseAnimating*>(pOther);
if( pAnim )
{
pAnim->Ignite( 30.0f );
}
Vector vecNewVelocity = GetAbsVelocity();
vecNewVelocity *= 0.1f;
SetAbsVelocity( vecNewVelocity );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
SetGravity(1.0f);
Die( 0.5 );
return;
}
else
{
// hit the world, check the material type here, see if the
flare should stick.
trace_t tr;
tr = CBaseEntity::GetTouchTrace();
//Only do this on the first bounce
if ( m_nBounces == 0 )
{
surfacedata_t *pdata = physprops->GetSurfaceData(
tr.surface.surfaceProps );
if ( pdata != NULL )
{
//Only embed into concrete and wood (jdw: too
obscure for players?)
//if ( ( pdata->gameMaterial == 'C' ) || (
pdata->gameMaterial == 'W' ) )
{
Vector impactDir = ( tr.endpos -
tr.startpos );
VectorNormalize( impactDir );
float surfDot = tr.plane.normal.Dot(
impactDir );
//Do not stick to ceilings or on
shallow impacts
if ( ( tr.plane.normal.z > -0.5f ) && (
surfDot < -0.9f ) )
{
RemoveSolidFlags(
FSOLID_NOT_SOLID );
AddSolidFlags( FSOLID_TRIGGER );
UTIL_SetOrigin( this, tr.endpos
+ ( tr.plane.normal * 2.0f ) );
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
SetTouch(
&CFlareGrenade::FlareBurnTouch );
int index =
decalsystem->GetDecalIndexForName( "SmallScorch" );
if ( index >= 0 )
{
CBroadcastRecipientFilter filter;
te->Decal( filter, 0.0,
&tr.endpos, &tr.startpos, ENTINDEX( tr.m_pEnt ), tr.hitbox, index );
}
CPASAttenuationFilter filter2(
this, "Flare.Touch" );
EmitSound( filter2, entindex(),
"Flare.Touch" );
return;
}
}
}
}
//Scorch decal
if ( GetAbsVelocity().LengthSqr() > (250*250) )
{
int index = decalsystem->GetDecalIndexForName(
"FadingScorch" );
if ( index >= 0 )
{
CBroadcastRecipientFilter filter;
te->Decal( filter, 0.0, &tr.endpos,
&tr.startpos, ENTINDEX( tr.m_pEnt ), tr.hitbox, index );
}
}
// Change our flight characteristics
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
SetGravity( UTIL_ScaleForGravity( 640 ) );
m_nBounces++;
//After the first bounce, smacking into whoever fired the flare
is fair game
SetOwnerEntity( this );
// Slow down
Vector vecNewVelocity = GetAbsVelocity();
vecNewVelocity.x *= 0.8f;
vecNewVelocity.y *= 0.8f;
SetAbsVelocity( vecNewVelocity );
//Stopped?
if ( GetAbsVelocity().Length() < 64.0f )
{
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
RemoveSolidFlags( FSOLID_NOT_SOLID );
AddSolidFlags( FSOLID_TRIGGER );
SetTouch( &CFlareGrenade::FlareBurnTouch );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::Start( float lifeTime )
{
StartBurnSound();
if ( m_pBurnSound != NULL )
{
CSoundEnvelopeController::GetController().Play( m_pBurnSound,
0.0f, 60 );
CSoundEnvelopeController::GetController().SoundChangeVolume(
m_pBurnSound, 0.8f, 2.0f );
CSoundEnvelopeController::GetController().SoundChangePitch(
m_pBurnSound, 100, 2.0f );
}
if ( lifeTime > 0 )
{
m_flTimeBurnOut = gpGlobals->curtime + lifeTime;
}
else
{
m_flTimeBurnOut = -1.0f;
}
RemoveEffects( EF_NODRAW );
SetThink( &CFlareGrenade::FlareThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::Die( float fadeTime )
{
m_flTimeBurnOut = gpGlobals->curtime + fadeTime;
SetThink( &CFlareGrenade::FlareThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlareGrenade::Launch( const Vector &direction, float speed )
{
// Make sure we're visible
if ( m_spawnflags & SF_FLARE_INFINITE )
{
Start( -1 );
}
else
{
Start( 8.0f );
}
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
// Punch our velocity towards our facing
SetAbsVelocity( direction * speed );
SetGravity( 1.0f );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CFlareGrenade::InputStart( inputdata_t &inputdata )
{
Start( inputdata.value.Float() );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CFlareGrenade::InputDie( inputdata_t &inputdata )
{
Die( inputdata.value.Float() );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CFlareGrenade::InputLaunch( inputdata_t &inputdata )
{
Vector direction;
AngleVectors( GetAbsAngles(), &direction );
float speed = inputdata.value.Float();
if ( speed == 0 )
{
speed = FLARE_LAUNCH_SPEED;
}
Launch( direction, speed );
}
//-----------------------------------------------------------------------------
// Purpose: Removes flare from active flare list
//-----------------------------------------------------------------------------
void CFlareGrenade::RemoveFromActiveFlares( void )
{
CFlareGrenade *pFlare;
CFlareGrenade *pPrevFlare;
if ( !m_bInActiveList )
return;
pPrevFlare = NULL;
for( pFlare = CFlareGrenade::activeFlares; pFlare != NULL; pFlare =
pFlare->m_pNextFlare )
{
if ( pFlare == this )
{
if ( pPrevFlare )
{
pPrevFlare->m_pNextFlare = m_pNextFlare;
}
else
{
activeFlares = m_pNextFlare;
}
break;
}
pPrevFlare = pFlare;
}
m_pNextFlare = NULL;
m_bInActiveList = false;
}
//-----------------------------------------------------------------------------
// Purpose: Adds flare to active flare list
//-----------------------------------------------------------------------------
void CFlareGrenade::AddToActiveFlares( void )
{
if ( !m_bInActiveList )
{
m_pNextFlare = CFlareGrenade::activeFlares;
CFlareGrenade::activeFlares = this;
m_bInActiveList = true;
}
}
#endif // WEAPON_SMG2_H
weapon_smg2.cpp (server)
======================================================================
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved.
============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "basehlcombatweapon.h"
#include "NPCevent.h"
#include "basecombatcharacter.h"
#include "AI_BaseNPC.h"
#include "player.h"
#include "game.h"
#include "in_buttons.h"
#include "grenade_ar2.h"
#include "AI_Memory.h"
#include "soundent.h"
#include "rumble_shared.h"
#include "weapon_smg2.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern ConVar sk_plr_dmg_smg2_grenade;
#include "basehlcombatweapon.h"
#include "soundenvelope.h"
//-----------------------------------------------------------------------------
// Actual Gun!
//-----------------------------------------------------------------------------
class CWeaponSMG2 : public CHLSelectFireMachineGun
{
DECLARE_DATADESC();
public:
DECLARE_CLASS( CWeaponSMG2, CHLSelectFireMachineGun );
CWeaponSMG2();
DECLARE_SERVERCLASS();
void Precache( void );
void AddViewKick( void );
void SecondaryAttack( void );
int GetMinBurst() { return 5; }
int GetMaxBurst() { return 12; }
virtual void Equip( CBaseCombatCharacter *pOwner );
bool Reload( void );
float GetFireRate( void ) { return 0.075f; } // fast
int CapabilitiesGet( void ) { return
bits_CAP_WEAPON_RANGE_ATTACK1; }
int WeaponRangeAttack2Condition( float flDot, float flDist
);
Activity GetPrimaryAttackActivity( void );
virtual const Vector& GetBulletSpread( void )
{
static const Vector cone = VECTOR_CONE_5DEGREES;
return cone;
}
const WeaponProficiencyInfo_t *GetProficiencyValues();
void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector
&vecShootOrigin, Vector &vecShootDir );
void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool
bSecondary );
void Operator_HandleAnimEvent( animevent_t *pEvent,
CBaseCombatCharacter *pOperator );
DECLARE_ACTTABLE();
protected:
Vector m_vecTossVelocity;
float m_flNextGrenadeCheck;
};
IMPLEMENT_SERVERCLASS_ST(CWeaponSMG2, DT_WeaponSMG2)
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( weapon_smg2, CWeaponSMG2 );
PRECACHE_WEAPON_REGISTER(weapon_smg2);
BEGIN_DATADESC( CWeaponSMG2 )
DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
END_DATADESC()
acttable_t CWeaponSMG2::m_acttable[] =
{
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG2,
true },
{ ACT_RELOAD, ACT_RELOAD_SMG2,
true },
{ ACT_IDLE, ACT_IDLE_SMG2,
true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG2,
true },
{ ACT_WALK, ACT_WALK_RIFLE,
true },
{ ACT_WALK_AIM, ACT_WALK_AIM_RIFLE,
true },
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_SMG2_RELAXED,
false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_SMG2_STIMULATED,
false },
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG2,
false },//always aims
{ ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED,
false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED,
false },
{ ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE,
false },//always aims
{ ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED,
false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED,
false },
{ ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE,
false },//always aims
// Readiness activities (aiming)
{ ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG2_RELAXED,
false },//never aims
{ ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED,
false },
{ ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG2,
false },//always aims
{ ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED,
false },//never aims
{ ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED,
false },
{ ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE,
false },//always aims
{ ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED,
false },//never aims
{ ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED,
false },
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE,
false },//always aims
//End readiness activities
{ ACT_WALK_AIM, ACT_WALK_AIM_RIFLE,
true },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE,
true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE,
true },
{ ACT_RUN, ACT_RUN_RIFLE,
true },
{ ACT_RUN_AIM, ACT_RUN_AIM_RIFLE,
true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE,
true },
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE,
true },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SMG2, true },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG2_LOW,
true },
{ ACT_COVER_LOW, ACT_COVER_SMG2_LOW,
false },
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG2_LOW,
false },
{ ACT_RELOAD_LOW, ACT_RELOAD_SMG2_LOW,
false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG2,
true },
};
IMPLEMENT_ACTTABLE(CWeaponSMG2);
//=========================================================
CWeaponSMG2::CWeaponSMG2( )
{
m_fMinRange1 = 0;// No minimum range.
m_fMaxRange1 = 1400;
m_bAltFiresUnderwater = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponSMG2::Precache( void )
{
UTIL_PrecacheOther("grenade_ar2"); // Not needed now
PrecacheScriptSound( "Flare.Touch" );
PrecacheScriptSound( "Weapon_FlareGun.Burn" );
UTIL_PrecacheOther( "env_flare" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose: Give this weapon longer range when wielded by an ally NPC.
//-----------------------------------------------------------------------------
void CWeaponSMG2::Equip( CBaseCombatCharacter *pOwner )
{
if( pOwner->Classify() == CLASS_PLAYER_ALLY )
{
m_fMaxRange1 = 3000;
}
else
{
m_fMaxRange1 = 1400;
}
BaseClass::Equip( pOwner );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponSMG2::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector
&vecShootOrigin, Vector &vecShootDir )
{
// FIXME: use the returned number of bullets to account for >10hz
firerate
WeaponSoundRealtime( SINGLE_NPC );
CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE,
pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator,
SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() );
pOperator->FireBullets( 1, vecShootOrigin, vecShootDir,
VECTOR_CONE_PRECALCULATED,
MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 1, entindex(), 0 );
pOperator->DoMuzzleFlash();
m_iClip1 = m_iClip1 - 1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponSMG2::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool
bSecondary )
{
// Ensure we have enough rounds in the clip
m_iClip1++;
Vector vecShootOrigin, vecShootDir;
QAngle angShootDir;
GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin,
angShootDir );
AngleVectors( angShootDir, &vecShootDir );
FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponSMG2::Operator_HandleAnimEvent( animevent_t *pEvent,
CBaseCombatCharacter *pOperator )
{
switch( pEvent->event )
{
case EVENT_WEAPON_SMG2:
{
Vector vecShootOrigin, vecShootDir;
QAngle angDiscard;
// Support old style attachment point firing
if ((pEvent->options == NULL) || (pEvent->options[0] ==
'\0') || (!pOperator->GetAttachment(pEvent->options, vecShootOrigin,
angDiscard)))
{
vecShootOrigin =
pOperator->Weapon_ShootPosition();
}
CAI_BaseNPC *npc = pOperator->MyNPCPointer();
ASSERT( npc != NULL );
vecShootDir = npc->GetActualShootTrajectory(
vecShootOrigin );
FireNPCPrimaryAttack( pOperator, vecShootOrigin,
vecShootDir );
}
break;
/*//FIXME: Re-enable
case EVENT_WEAPON_AR2_GRENADE:
{
CAI_BaseNPC *npc = pOperator->MyNPCPointer();
Vector vecShootOrigin, vecShootDir;
vecShootOrigin = pOperator->Weapon_ShootPosition();
vecShootDir = npc->GetShootEnemyDir( vecShootOrigin );
Vector vecThrow = m_vecTossVelocity;
CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2",
vecShootOrigin, vec3_angle, npc );
pGrenade->SetAbsVelocity( vecThrow );
pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) );
pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY );
pGrenade->m_hOwner = npc;
pGrenade->m_pMyWeaponAR2 = this;
pGrenade->SetDamage(sk_npc_dmg_ar2_grenade.GetFloat());
// FIXME: arrgg ,this is hard coded into the weapon???
m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six
seconds before even looking again to see if a grenade can be thrown.
m_iClip2--;
}
break;
*/
default:
BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Activity
//-----------------------------------------------------------------------------
Activity CWeaponSMG2::GetPrimaryAttackActivity( void )
{
if ( m_nShotsFired < 2 )
return ACT_VM_PRIMARYATTACK;
if ( m_nShotsFired < 3 )
return ACT_VM_RECOIL1;
if ( m_nShotsFired < 4 )
return ACT_VM_RECOIL2;
return ACT_VM_RECOIL3;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CWeaponSMG2::Reload( void )
{
bool fRet;
float fCacheTime = m_flNextSecondaryAttack;
fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
if ( fRet )
{
// Undo whatever the reload process has done to our secondary
// attack timer. We allow you to interrupt reloading to fire
// a grenade.
m_flNextSecondaryAttack = GetOwner()->m_flNextAttack =
fCacheTime;
WeaponSound( RELOAD );
}
return fRet;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponSMG2::AddViewKick( void )
{
#define EASY_DAMPEN 0.5f
#define MAX_VERTICAL_KICK 1.0f //Degrees
#define SLIDE_LIMIT 2.0f //Seconds
//Get the view kick
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK,
m_fFireDuration, SLIDE_LIMIT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponSMG2::SecondaryAttack( void )
{
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
if ( m_iClip1 <= 0 )
{
SendWeaponAnim( ACT_VM_DRYFIRE );
pOwner->m_flNextAttack = gpGlobals->curtime +
SequenceDuration();
return;
}
m_iClip1 = m_iClip1 - 1;
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
pOwner->m_flNextAttack = gpGlobals->curtime + 1;
CFlareGrenade *pFlare = CFlareGrenade::Create(
pOwner->Weapon_ShootPosition(), pOwner->EyeAngles(), pOwner, FLARE_DURATION );
if ( pFlare == NULL )
return;
Vector forward;
pOwner->EyeVectors( &forward );
pFlare->SetAbsVelocity( forward * 1500 );
WeaponSound( SINGLE );
// Register a muzzleflash for the AI.
pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
}
#define COMBINE_MIN_GRENADE_CLEAR_DIST 256
//-----------------------------------------------------------------------------
// Purpose:
// Input : flDot -
// flDist -
// Output : int
//-----------------------------------------------------------------------------
int CWeaponSMG2::WeaponRangeAttack2Condition( float flDot, float flDist )
{
CAI_BaseNPC *npcOwner = GetOwner()->MyNPCPointer();
return COND_NONE;
/*
// --------------------------------------------------------
// Assume things haven't changed too much since last time
// --------------------------------------------------------
if (gpGlobals->curtime < m_flNextGrenadeCheck )
return m_lastGrenadeCondition;
*/
// -----------------------
// If moving, don't check.
// -----------------------
if ( npcOwner->IsMoving())
return COND_NONE;
CBaseEntity *pEnemy = npcOwner->GetEnemy();
if (!pEnemy)
return COND_NONE;
Vector vecEnemyLKP = npcOwner->GetEnemyLKP();
if ( !( pEnemy->GetFlags() & FL_ONGROUND ) && pEnemy->GetWaterLevel()
== 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) )
{
//!!!BUGBUG - we should make this check movetype and make sure
it isn't FLY? Players who jump a lot are unlikely to
// be grenaded.
// don't throw grenades at anything that isn't on the ground!
return COND_NONE;
}
// --------------------------------------
// Get target vector
// --------------------------------------
Vector vecTarget;
if (random->RandomInt(0,1))
{
// magically know where they are
vecTarget = pEnemy->WorldSpaceCenter();
}
else
{
// toss it to where you last saw them
vecTarget = vecEnemyLKP;
}
// vecTarget = m_vecEnemyLKP + (pEnemy->BodyTarget( GetLocalOrigin() )
- pEnemy->GetLocalOrigin());
// estimate position
// vecTarget = vecTarget + pEnemy->m_vecVelocity * 2;
if ( ( vecTarget - npcOwner->GetLocalOrigin() ).Length2D() <=
COMBINE_MIN_GRENADE_CLEAR_DIST )
{
// crap, I don't want to blow myself up
m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full
second.
return (COND_NONE);
}
// ---------------------------------------------------------------------
// Are any friendlies near the intended grenade impact area?
// ---------------------------------------------------------------------
CBaseEntity *pTarget = NULL;
while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, vecTarget,
COMBINE_MIN_GRENADE_CLEAR_DIST ) ) != NULL )
{
//Check to see if the default relationship is hatred, and if so
intensify that
if ( npcOwner->IRelationType( pTarget ) == D_LI )
{
// crap, I might blow my own guy up. Don't throw a
grenade and don't check again for a while.
m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one
full second.
return (COND_WEAPON_BLOCKED_BY_FRIEND);
}
}
// ---------------------------------------------------------------------
// Check that throw is legal and clear
// ---------------------------------------------------------------------
// FIXME: speed is based on difficulty...
Vector vecToss = VecCheckThrow( this, npcOwner->GetLocalOrigin() +
Vector(0,0,60), vecTarget, 600.0, 0.5 );
if ( vecToss != vec3_origin )
{
m_vecTossVelocity = vecToss;
// don't check again for a while.
// JAY: HL1 keeps checking - test?
//m_flNextGrenadeCheck = gpGlobals->curtime;
m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.
return COND_CAN_RANGE_ATTACK2;
}
else
{
// don't check again for a while.
m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full
second.
return COND_WEAPON_SIGHT_OCCLUDED;
}
}
//-----------------------------------------------------------------------------
const WeaponProficiencyInfo_t *CWeaponSMG2::GetProficiencyValues()
{
static WeaponProficiencyInfo_t proficiencyTable[] =
{
{ 7.0, 0.75 },
{ 5.00, 0.75 },
{ 10.0/3.0, 0.75 },
{ 5.0/3.0, 0.75 },
{ 1.00, 1.0 },
};
COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) ==
WEAPON_PROFICIENCY_PERFECT + 1);
return proficiencyTable;
}
c_flaregrenade.cpp
======================================================================
#include "cbase.h"
#include "c_basecombatcharacter.h"
#include "ClientEffectPrecacheSystem.h"
#include "particles_simple.h"
#include "iefx.h"
#include "dlight.h"
#include "view.h"
#include "fx.h"
#include "clientsideeffects.h"
#include "c_pixel_visibility.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class C_FlareGrenade : public CBaseCombatCharacter
{
public:
DECLARE_CLASS( C_FlareGrenade, CBaseCombatCharacter );
DECLARE_CLIENTCLASS();
C_FlareGrenade();
~C_FlareGrenade();
static C_FlareGrenade * GetActiveFlares( void );
C_FlareGrenade * GetNextFlare( void ) const { return
m_pNextFlare; }
static C_FlareGrenade *Create( Vector vecOrigin, QAngle vecAngles,
CBaseEntity *pOwner, float lifetime );
virtual unsigned int PhysicsSolidMaskForEntity( void ) const;
void Spawn( void );
void Precache( void );
int Restore( IRestore &restore );
void Activate( void );
void StartBurnSound( void );
void Start( float lifeTime );
void Die( float fadeTime );
void Launch( const Vector &direction, float speed );
//Class_T Classify( void );
void FlareTouch( CBaseEntity *pOther );
void FlareBurnTouch( CBaseEntity *pOther );
void FlareThink( void );
void InputStart( inputdata_t &inputdata );
void InputDie( inputdata_t &inputdata );
void InputLaunch( inputdata_t &inputdata );
//DECLARE_DATADESC();
static C_FlareGrenade *activeFlares;
CBaseEntity *m_pOwner;
int m_nBounces; // how many
times has this flare bounced?
CNetworkVar( float, m_flTimeBurnOut ); // when will the flare burn out?
CNetworkVar( float, m_flScale );
float m_flDuration;
float m_flNextDamage;
CSoundPatch *m_pBurnSound;
bool m_bFading;
CNetworkVar( bool, m_bLight );
CNetworkVar( bool, m_bSmoke );
CNetworkVar( bool, m_bPropFlare );
bool m_bInActiveList;
C_FlareGrenade * m_pNextFlare;
void RemoveFromActiveFlares( void );
void AddToActiveFlares( void );
};
LINK_ENTITY_TO_CLASS( flare_grenade, C_FlareGrenade )
IMPLEMENT_CLIENTCLASS_DT( C_FlareGrenade, DT_FlareGrenade, CFlareGrenade )
RecvPropInt( RECVINFO( m_nBounces ) ),
RecvPropFloat( RECVINFO( m_flScale ) ),
RecvPropFloat( RECVINFO( m_flDuration ) ),
RecvPropFloat( RECVINFO( m_flTimeBurnOut ) ),
RecvPropFloat( RECVINFO( m_flNextDamage ) ),
RecvPropInt( RECVINFO( m_bFading ) ), // closest to bool I could find
RecvPropInt( RECVINFO( m_bLight ) ),
RecvPropInt( RECVINFO( m_bSmoke ) ),
RecvPropInt( RECVINFO( m_bPropFlare ) ),
RecvPropInt( RECVINFO( m_bInActiveList ) ),
//DEFINE_FIELD( m_pOwner, FIELD_CLASSPTR ),
//DEFINE_FIELD( m_pNextFlare, FIELD_CLASSPTR ),
END_RECV_TABLE()
C_FlareGrenade::C_FlareGrenade( void )
{
m_flScale = 1.0f;
m_nBounces = 0;
m_bFading = false;
m_bLight = true;
m_bSmoke = true;
m_flNextDamage = gpGlobals->curtime;
m_lifeState = LIFE_ALIVE;
m_iHealth = 100;
m_bPropFlare = false;
m_bInActiveList = false;
m_pNextFlare = NULL;
}
C_FlareGrenade::~C_FlareGrenade()
{
m_pBurnSound = NULL;
RemoveFromActiveFlares();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FlareGrenade::Precache( void )
{
PrecacheModel("models/weapons/flare.mdl" );
PrecacheScriptSound( "Weapon_FlareGun.Burn" );
// FIXME: needed to precache the fire model. Shouldn't have to do this.
UTIL_PrecacheOther( "_firesmoke" );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &restore -
// Output : int
//-----------------------------------------------------------------------------
int C_FlareGrenade::Restore( IRestore &restore )
{
int result = BaseClass::Restore( restore );
return result;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FlareGrenade::Spawn( void )
{
Precache();
SetModel( "models/weapons/flare.mdl" );
// UTIL_SetSize( this, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ) );
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_SOLID );
SetMoveType( MOVETYPE_NONE );
SetFriction( 0.6f );
// SetGravity( UTIL_ScaleForGravity( 400 ) );
m_flTimeBurnOut = gpGlobals->curtime + 30;
AddEffects( EF_NOSHADOW|EF_NORECEIVESHADOW );
/* if ( m_spawnflags & SF_FLARE_NO_DLIGHT )
{
m_bLight = false;
}
if ( m_spawnflags & SF_FLARE_NO_SMOKE )
{
m_bSmoke = false;
}
if ( m_spawnflags & SF_FLARE_INFINITE )
{
m_flTimeBurnOut = -1.0f;
}
if ( m_spawnflags & SF_FLARE_START_OFF )
{
AddEffects( EF_NODRAW );
} */
AddFlag( FL_OBJECT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_FlareGrenade::Activate( void )
{
BaseClass::Activate();
}
unsigned int C_FlareGrenade::PhysicsSolidMaskForEntity( void ) const
{
return MASK_NPCSOLID;
}
//-----------------------------------------------------------------------------
// Purpose: Removes flare from active flare list
//-----------------------------------------------------------------------------
void C_FlareGrenade::RemoveFromActiveFlares( void )
{
//C_FlareGrenade *pFlare;
//C_FlareGrenade *pPrevFlare;
//if ( !m_bInActiveList )
// return;
//pPrevFlare = NULL;
//for( pFlare = C_FlareGrenade::activeFlares; pFlare != NULL; pFlare =
pFlare->m_pNextFlare )
//{
// if ( pFlare == this )
// {
// if ( pPrevFlare )
// {
// pPrevFlare->m_pNextFlare = m_pNextFlare;
// }
// else
// {
// activeFlares = m_pNextFlare;
// }
// break;
// }
// pPrevFlare = pFlare;
//}
//m_pNextFlare = NULL;
//m_bInActiveList = false;
}
--
[ winmail.dat of type application/ms-tnef deleted ]
--
_______________________________________________
To unsubscribe, edit your list preferences, or view the list archives, please
visit:
http://list.valvesoftware.com/mailman/listinfo/hlcoders