mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-23 06:36:54 +00:00
1081 lines
24 KiB
C++
1081 lines
24 KiB
C++
//========= 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
|
|
}
|