Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1081 lines
24 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: RPG
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "in_buttons.h"
#include "hl1mp_basecombatweapon_shared.h"
#include "hl1mp_weapon_rpg.h"
#ifdef CLIENT_DLL
#include "hl1/c_hl1mp_player.h"
#include "model_types.h"
#include "beamdraw.h"
#include "fx_line.h"
#include "view.h"
#else
#include "basecombatcharacter.h"
#include "movie_explosion.h"
#include "hl1mp_player.h"
#include "rope.h"
#include "soundent.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "explode.h"
#include "util.h"
#include "te_effect_dispatch.h"
#include "shake.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern ConVar sk_plr_dmg_rpg;
void TE_BeamFollow( IRecipientFilter& filter, float delay,
int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth,
float fadeLength,float r, float g, float b, float a );
#define RPG_LASER_SPRITE "sprites/redglow_mp1"
class CLaserDot : public CBaseEntity
{
DECLARE_CLASS( CLaserDot, CBaseEntity );
public:
CLaserDot( void );
~CLaserDot( void );
static CLaserDot *Create( const Vector &origin, CBaseEntity *pOwner = NULL, bool bVisibleDot = true );
void SetTargetEntity( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; }
CBaseEntity *GetTargetEntity( void ) { return m_hTargetEnt; }
void SetLaserPosition( const Vector &origin, const Vector &normal );
Vector GetChasePosition();
void TurnOn( void );
void TurnOff( void );
bool IsOn() const { return m_bIsOn; }
void Toggle( void );
int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
void MakeInvisible( void );
#ifdef CLIENT_DLL
virtual bool IsTransparent( void ) { return true; }
virtual RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TRANSLUCENT_ENTITY; }
virtual int DrawModel( int flags );
virtual void OnDataChanged( DataUpdateType_t updateType );
virtual bool ShouldDraw( void ) { return (IsEffectActive(EF_NODRAW)==false); }
CMaterialReference m_hSpriteMaterial;
#endif
protected:
Vector m_vecSurfaceNormal;
EHANDLE m_hTargetEnt;
bool m_bVisibleLaserDot;
// bool m_bIsOn;
CNetworkVar( bool, m_bIsOn );
DECLARE_NETWORKCLASS();
DECLARE_DATADESC();
public:
CLaserDot *m_pNext;
};
#ifndef CLIENT_DLL
//=============================================================================
// RPG Rocket
//=============================================================================
BEGIN_DATADESC( CRpgRocket )
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( m_vecAbsVelocity, FIELD_VECTOR ),
DEFINE_FIELD( m_flIgniteTime, FIELD_TIME ),
//DEFINE_FIELD( m_iTrail, FIELD_INTEGER ),
// Function Pointers
DEFINE_ENTITYFUNC( RocketTouch ),
DEFINE_THINKFUNC( IgniteThink ),
DEFINE_THINKFUNC( SeekThink ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket );
IMPLEMENT_SERVERCLASS_ST(CRpgRocket, DT_RpgRocket)
END_SEND_TABLE()
CRpgRocket::CRpgRocket()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//
//
//-----------------------------------------------------------------------------
void CRpgRocket::Precache( void )
{
PrecacheModel( "models/rpgrocket.mdl" );
PrecacheModel( "sprites/animglow01.vmt" );
PrecacheScriptSound( "Weapon_RPG.RocketIgnite" );
m_iTrail = PrecacheModel("sprites/smoke.vmt");
}
//-----------------------------------------------------------------------------
// Purpose:
//
//
//-----------------------------------------------------------------------------
void CRpgRocket::Spawn( void )
{
Precache();
SetSolid( SOLID_BBOX );
SetModel("models/rpgrocket.mdl");
UTIL_SetSize( this, -Vector(0,0,0), Vector(0,0,0) );
SetTouch( &CRpgRocket::RocketTouch );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
SetThink( &CRpgRocket::IgniteThink );
SetNextThink( gpGlobals->curtime + 0.4f );
QAngle angAngs;
Vector vecFwd;
angAngs = GetAbsAngles();
angAngs.x -= 30;
AngleVectors( angAngs, &vecFwd );
SetAbsVelocity( vecFwd * 250 );
SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for rockets
m_flDamage = sk_plr_dmg_rpg.GetFloat();
m_DmgRadius = m_flDamage * 2.5;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pOther -
//-----------------------------------------------------------------------------
void CRpgRocket::RocketTouch( CBaseEntity *pOther )
{
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
return;
if ( m_hOwner != NULL )
{
m_hOwner->NotifyRocketDied();
}
StopSound( "Weapon_RPG.RocketIgnite" );
ExplodeTouch( pOther );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRpgRocket::IgniteThink( void )
{
SetMoveType( MOVETYPE_FLY );
AddEffects( EF_DIMLIGHT );
EmitSound( "Weapon_RPG.RocketIgnite" );
SetThink( &CRpgRocket::SeekThink );
SetNextThink( gpGlobals->curtime + 0.1f );
CBroadcastRecipientFilter filter;
TE_BeamFollow( filter, 0.0,
entindex(),
m_iTrail,
0,
4,
5,
5,
0,
224,
224,
255,
255 );
m_flIgniteTime = gpGlobals->curtime;
}
#define RPG_HOMING_SPEED 0.125f
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRpgRocket::SeekThink( void )
{
CBaseEntity *pOther = NULL;
Vector vecTarget;
Vector vecFwd;
Vector vecDir;
float flDist, flMax, flDot;
trace_t tr;
AngleVectors( GetAbsAngles(), &vecFwd );
vecTarget = vecFwd;
flMax = 4096;
// Examine all entities within a reasonable radius
while ( (pOther = gEntList.FindEntityByClassname( pOther, "laser_spot" ) ) != NULL)
{
CLaserDot *pDot = dynamic_cast<CLaserDot*>(pOther);
// if ( pDot->IsActive() )
if ( pDot->IsOn() )
{
UTIL_TraceLine( GetAbsOrigin(), pDot->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction >= 0.90 )
{
vecDir = pDot->GetAbsOrigin() - GetAbsOrigin();
flDist = VectorLength( vecDir );
VectorNormalize( vecDir );
flDot = DotProduct( vecFwd, vecDir );
if ( (flDot > 0) && (flDist * (1 - flDot) < flMax) )
{
flMax = flDist * (1 - flDot);
vecTarget = vecDir;
}
}
}
}
QAngle vecAng;
VectorAngles( vecTarget, vecAng );
SetAbsAngles( vecAng );
// this acceleration and turning math is totally wrong, but it seems to respond well so don't change it.
float flSpeed = GetAbsVelocity().Length();
if ( gpGlobals->curtime - m_flIgniteTime < 1.0 )
{
SetAbsVelocity( GetAbsVelocity() * 0.2 + vecTarget * (flSpeed * 0.8 + 400) );
if ( GetWaterLevel() == 3 )
{
// go slow underwater
if ( GetAbsVelocity().Length() > 300 )
{
Vector vecVel = GetAbsVelocity();
VectorNormalize( vecVel );
SetAbsVelocity( vecVel * 300 );
}
UTIL_BubbleTrail( GetAbsOrigin() - GetAbsVelocity() * 0.1, GetAbsOrigin(), 4 );
}
else
{
if ( GetAbsVelocity().Length() > 2000 )
{
Vector vecVel = GetAbsVelocity();
VectorNormalize( vecVel );
SetAbsVelocity( vecVel * 2000 );
}
}
}
else
{
if ( IsEffectActive( EF_DIMLIGHT ) )
{
ClearEffects();
}
SetAbsVelocity( GetAbsVelocity() * 0.2 + vecTarget * flSpeed * 0.798 );
if ( GetWaterLevel() == 0 && GetAbsVelocity().Length() < 1500 )
{
Detonate();
}
}
SetNextThink( gpGlobals->curtime + 0.1f );
}
void CRpgRocket::Detonate( void )
{
StopSound( "Weapon_RPG.RocketIgnite" );
BaseClass::Detonate();
}
//-----------------------------------------------------------------------------
// Purpose:
//
// Input : &vecOrigin -
// &vecAngles -
// NULL -
//
// Output : CRpgRocket
//-----------------------------------------------------------------------------
CRpgRocket *CRpgRocket::Create( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner )
{
CRpgRocket *pRocket = (CRpgRocket *)CreateEntityByName( "rpg_rocket" );
UTIL_SetOrigin( pRocket, vecOrigin );
pRocket->SetAbsAngles( angAngles );
pRocket->Spawn();
pRocket->SetOwnerEntity( pentOwner );
return pRocket;
}
#endif // endif #ifndef CLIENT_DLL
//=============================================================================
// Laser Dot
//=============================================================================
IMPLEMENT_NETWORKCLASS_ALIASED( LaserDot, DT_LaserDot )
BEGIN_NETWORK_TABLE( CLaserDot, DT_LaserDot )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bIsOn ) ),
#else
SendPropBool( SENDINFO( m_bIsOn ) ),
#endif
END_NETWORK_TABLE()
#ifndef CLIENT_DLL
// a list of laser dots to search quickly
CEntityClassList<CLaserDot> g_LaserDotList;
template <> CLaserDot *CEntityClassList<CLaserDot>::m_pClassList = NULL;
CLaserDot *GetLaserDotList()
{
return g_LaserDotList.m_pClassList;
}
#endif
LINK_ENTITY_TO_CLASS( laser_spot, CLaserDot );
BEGIN_DATADESC( CLaserDot )
DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ),
DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
DEFINE_FIELD( m_bVisibleLaserDot, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ),
//DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ), // don't save - regenerated by constructor
END_DATADESC()
//-----------------------------------------------------------------------------
// Finds missiles in cone
//-----------------------------------------------------------------------------
CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
{
return CLaserDot::Create( origin, pOwner, bVisibleDot );
}
void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget )
{
CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
pDot->SetTargetEntity( pTarget );
}
void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable )
{
CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
if ( bEnable )
{
pDot->TurnOn();
}
else
{
pDot->TurnOff();
}
}
CLaserDot::CLaserDot( void )
{
m_hTargetEnt = NULL;
m_bIsOn = true;
#ifndef CLIENT_DLL
g_LaserDotList.Insert( this );
#endif
}
CLaserDot::~CLaserDot( void )
{
#ifndef CLIENT_DLL
g_LaserDotList.Remove( this );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &origin -
// Output : CLaserDot
//-----------------------------------------------------------------------------
CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
{
#ifndef CLIENT_DLL
CLaserDot *pLaserDot = (CLaserDot *) CBaseEntity::Create( "laser_spot", origin, QAngle(0,0,0) );
if ( pLaserDot == NULL )
return NULL;
pLaserDot->m_bVisibleLaserDot = bVisibleDot;
pLaserDot->SetMoveType( MOVETYPE_NONE );
pLaserDot->AddSolidFlags( FSOLID_NOT_SOLID );
pLaserDot->AddEffects( EF_NOSHADOW );
UTIL_SetSize( pLaserDot, -Vector(6,6,6), Vector(6,6,6) );
pLaserDot->SetOwnerEntity( pOwner );
pLaserDot->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
if ( !bVisibleDot )
{
pLaserDot->MakeInvisible();
}
return pLaserDot;
#else
return NULL;
#endif
}
void CLaserDot::SetLaserPosition( const Vector &origin, const Vector &normal )
{
SetAbsOrigin( origin );
m_vecSurfaceNormal = normal;
}
Vector CLaserDot::GetChasePosition()
{
return GetAbsOrigin() - m_vecSurfaceNormal * 10;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLaserDot::TurnOn( void )
{
m_bIsOn = true;
RemoveEffects(EF_NODRAW);
if ( m_bVisibleLaserDot )
{
//BaseClass::TurnOn();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLaserDot::TurnOff( void )
{
m_bIsOn = false;
AddEffects(EF_NODRAW);
if ( m_bVisibleLaserDot )
{
//BaseClass::TurnOff();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLaserDot::MakeInvisible( void )
{
}
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Draw our sprite
//-----------------------------------------------------------------------------
int CLaserDot::DrawModel( int flags )
{
color32 color={255,255,255,255};
Vector vecAttachment, vecDir;
QAngle angles;
float scale;
Vector endPos;
C_HL1MP_Player *pOwner = ToHL1MPPlayer( GetOwnerEntity() );
if ( pOwner != NULL && pOwner->IsDormant() == false )
{
// Always draw the dot in front of our faces when in first-person
if ( pOwner->IsLocalPlayer() )
{
// Take our view position and orientation
vecAttachment = CurrentViewOrigin();
vecDir = CurrentViewForward();
}
else
{
// Take the eye position and direction
vecAttachment = pOwner->EyePosition();
QAngle angles = pOwner->EyeAngles();
AngleVectors( angles, &vecDir );
}
trace_t tr;
UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
// Backup off the hit plane
endPos = tr.endpos + ( tr.plane.normal * 4.0f );
}
else
{
// Just use our position if we can't predict it otherwise
endPos = GetAbsOrigin();
}
// Randomly flutter
scale = 16.0f + random->RandomFloat( -4.0f, 4.0f );
// Draw our laser dot in space
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( m_hSpriteMaterial, this );
DrawSprite( endPos, scale, scale, color );
return 1;
}
//-----------------------------------------------------------------------------
// Purpose: Setup our sprite reference
//-----------------------------------------------------------------------------
void CLaserDot::OnDataChanged( DataUpdateType_t updateType )
{
if ( updateType == DATA_UPDATE_CREATED )
{
m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
}
}
#endif //CLIENT_DLL
//=============================================================================
// RPG Weapon
//=============================================================================
LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
PRECACHE_WEAPON_REGISTER( weapon_rpg );
//IMPLEMENT_SERVERCLASS_ST( CWeaponRPG, DT_WeaponRPG )
//END_SEND_TABLE()
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRPG, DT_WeaponRPG )
BEGIN_DATADESC( CWeaponRPG )
DEFINE_FIELD( m_bIntialStateUpdate, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bGuiding, FIELD_BOOLEAN ),
#ifndef CLIENT_DLL
DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
#endif
DEFINE_FIELD( m_hMissile, FIELD_EHANDLE ),
DEFINE_FIELD( m_bLaserDotSuspended, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flLaserDotReviveTime, FIELD_TIME ),
END_DATADESC()
BEGIN_NETWORK_TABLE( CWeaponRPG, DT_WeaponRPG )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bIntialStateUpdate ) ),
RecvPropBool( RECVINFO( m_bGuiding ) ),
RecvPropBool( RECVINFO( m_bLaserDotSuspended ) ),
// RecvPropEHandle( RECVINFO( m_hMissile ), RecvProxy_MissileDied ),
// RecvPropVector( RECVINFO( m_vecLaserDot ) ),
#else
SendPropBool( SENDINFO( m_bIntialStateUpdate ) ),
SendPropBool( SENDINFO( m_bGuiding ) ),
SendPropBool( SENDINFO( m_bLaserDotSuspended ) ),
// SendPropEHandle( SENDINFO( m_hMissile ) ),
// SendPropVector( SENDINFO( m_vecLaserDot ) ),
#endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponRPG )
#ifdef CLIENT_DLL
DEFINE_PRED_FIELD( m_bIntialStateUpdate, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_bLaserDotSuspended, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flLaserDotReviveTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
#endif
END_PREDICTION_DATA()
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponRPG::CWeaponRPG( void )
{
m_bReloadsSingly = false;
m_bFiresUnderwater = true;
m_bGuiding = true;
m_bIntialStateUpdate = false;
m_bLaserDotSuspended = false;
}
CWeaponRPG::~CWeaponRPG()
{
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
UTIL_Remove( m_hLaserDot );
m_hLaserDot = NULL;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponRPG::ItemPostFrame( void )
{
BaseClass::ItemPostFrame();
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
//If we're pulling the weapon out for the first time, wait to draw the laser
if ( m_bIntialStateUpdate )
{
if ( GetActivity() != ACT_VM_DRAW )
{
if ( IsGuiding() && !m_bLaserDotSuspended )
{
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOn();
}
#endif
}
m_bIntialStateUpdate = false;
}
else
{
return;
}
}
//Player has toggled guidance state
if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
{
if ( IsGuiding() )
{
StopGuiding();
}
else
{
StartGuiding();
}
}
//Move the laser
UpdateSpot();
}
void CWeaponRPG::Drop( const Vector &vecVelocity )
{
StopGuiding();
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
UTIL_Remove( m_hLaserDot );
m_hLaserDot = NULL;
}
#endif
BaseClass::Drop( vecVelocity );
}
int CWeaponRPG::GetDefaultClip1( void ) const
{
if ( g_pGameRules->IsMultiplayer() )
{
// more default ammo in multiplay.
return BaseClass::GetDefaultClip1() * 2;
}
else
{
return BaseClass::GetDefaultClip1();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponRPG::Precache( void )
{
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "laser_spot" );
UTIL_PrecacheOther( "rpg_rocket" );
#endif
// PrecacheModel( RPG_LASER_SPRITE );
PrecacheModel( "sprites/redglow_mp1.vmt" );
BaseClass::Precache();
}
bool CWeaponRPG::Deploy( void )
{
m_bIntialStateUpdate = true;
m_bLaserDotSuspended = false;
CreateLaserPointer();
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOff();
}
#endif
if ( m_iClip1 <= 0 )
{
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_RPG_DRAW_UNLOADED, (char*)GetAnimPrefix() );
}
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponRPG::PrimaryAttack( void )
{
// Can't have an active missile out
if ( m_hMissile != NULL )
return;
// Can't be reloading
if ( GetActivity() == ACT_VM_RELOAD )
return;
if ( m_iClip1 <= 0 )
{
if ( !m_bFireOnEmpty )
{
Reload();
}
else
{
WeaponSound( EMPTY );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
}
}
Vector vecOrigin;
Vector vecForward;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
WeaponSound( SINGLE );
#ifndef CLIENT_DLL
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 400, 0.2 );
#endif
pOwner->DoMuzzleFlash();
#ifndef CLIENT_DLL
// Register a muzzleflash for the AI
pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
#endif
Vector vForward, vRight, vUp;
pOwner->EyeVectors( &vForward, &vRight, &vUp );
Vector muzzlePoint = pOwner->Weapon_ShootPosition() + vForward * 16.0f + vRight * 8.0f + vUp * -8.0f;
#ifndef CLIENT_DLL
QAngle vecAngles;
VectorAngles( vForward, vecAngles );
CRpgRocket * pMissile = CRpgRocket::Create( muzzlePoint, vecAngles, pOwner );
pMissile->m_hOwner = this;
pMissile->SetAbsVelocity( pMissile->GetAbsVelocity() + vForward * DotProduct( pOwner->GetAbsVelocity(), vForward ) );
m_hMissile = pMissile;
#endif
pOwner->ViewPunch( QAngle( -5, 0, 0 ) );
m_iClip1--;
m_flNextPrimaryAttack = gpGlobals->curtime + 1.5;
SetWeaponIdleTime( 1.5 );
UpdateSpot();
}
void CWeaponRPG::WeaponIdle( void )
{
CBaseCombatCharacter *pOwner = GetOwner();
if ( pOwner == NULL )
return;
if ( !HasWeaponIdleTimeElapsed() )
return;
int iAnim;
float flRand = random->RandomFloat( 0, 1 );
if ( flRand <= 0.75 || IsGuiding() )
{
if ( m_iClip1 <= 0 )
iAnim = ACT_RPG_IDLE_UNLOADED;
else
iAnim = ACT_VM_IDLE;
}
else
{
if ( m_iClip1 <= 0 )
iAnim = ACT_RPG_FIDGET_UNLOADED;
else
iAnim = ACT_VM_FIDGET;
}
SendWeaponAnim( iAnim );
}
void CWeaponRPG::NotifyRocketDied( void )
{
m_hMissile = NULL;
// Can't be reloading
if ( GetActivity() == ACT_VM_RELOAD )
return;
// Reload();
}
bool CWeaponRPG::Reload( void )
{
CBaseCombatCharacter *pOwner = GetOwner();
#if 0
if ( pOwner == NULL )
return false;
if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
return false;
WeaponSound( RELOAD );
SendWeaponAnim( ACT_VM_RELOAD );
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOff();
}
#endif
m_bLaserDotSuspended = true;
m_flLaserDotReviveTime = gpGlobals->curtime + 2.1;
m_flNextPrimaryAttack = gpGlobals->curtime + 2.1;
m_flNextSecondaryAttack = gpGlobals->curtime + 2.1;
return true;
#endif
// Can't be reloading
if ( GetActivity() == ACT_VM_RELOAD )
return false;
if ( pOwner == NULL )
return false;
if ( m_iClip1 > 0 )
return false;
if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
return false;
// because the RPG waits to autoreload when no missiles are active while the LTD is on, the
// weapons code is constantly calling into this function, but is often denied because
// a) missiles are in flight, but the LTD is on
// or
// b) player is totally out of ammo and has nothing to switch to, and should be allowed to
// shine the designator around
//
// Set the next attack time into the future so that WeaponIdle will get called more often
// than reload, allowing the RPG LTD to be updated
if ( ( m_hMissile != NULL ) && IsGuiding() )
{
// no reloading when there are active missiles tracking the designator.
// ward off future autoreload attempts by setting next attack time into the future for a bit.
return false;
}
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOff();
}
#endif
m_bLaserDotSuspended = true;
m_flLaserDotReviveTime = gpGlobals->curtime + 2.1;
m_flNextSecondaryAttack = gpGlobals->curtime + 2.1;
return DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
}
bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
{
// can't put away while guiding a missile.
if ( IsGuiding() && ( m_hMissile != NULL ) )
return false;
// StopGuiding();
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOff();
UTIL_Remove( m_hLaserDot );
m_hLaserDot = NULL;
}
#endif
m_bLaserDotSuspended = false;
return BaseClass::Holster( pSwitchingTo );
}
void CWeaponRPG::UpdateSpot( void )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
CreateLaserPointer();
#ifndef CLIENT_DLL
if ( m_hLaserDot == NULL )
return;
#endif
if ( IsGuiding() && m_bLaserDotSuspended && ( m_flLaserDotReviveTime <= gpGlobals->curtime ) )
{
#ifndef CLIENT_DLL
m_hLaserDot->TurnOn();
#endif
m_bLaserDotSuspended = false;
}
//Move the laser dot, if active
trace_t tr;
Vector muzzlePos = pPlayer->Weapon_ShootPosition();
Vector forward;
AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, &forward );
Vector endPos = muzzlePos + ( forward * MAX_TRACE_LENGTH );
// Trace out for the endpoint
UTIL_TraceLine( muzzlePos, endPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
// Move the laser sprite
Vector laserPos = tr.endpos + ( tr.plane.normal * 2.0f );
#ifndef CLIENT_DLL
m_hLaserDot->SetLaserPosition( laserPos, tr.plane.normal );
#endif
}
void CWeaponRPG::CreateLaserPointer( void )
{
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
return;
m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwner() );
if ( !IsGuiding() )
{
if ( m_hLaserDot )
{
m_hLaserDot->TurnOff();
}
}
#endif
}
bool CWeaponRPG::IsGuiding( void )
{
return m_bGuiding;
}
void CWeaponRPG::StartGuiding( void )
{
m_bGuiding = true;
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOn();
}
#endif
UpdateSpot();
}
void CWeaponRPG::StopGuiding( void )
{
m_bGuiding = false;
#ifndef CLIENT_DLL
if ( m_hLaserDot != NULL )
{
m_hLaserDot->TurnOff();
}
#endif
}