diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/shared/hl2mp/weapon_rpg.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/shared/hl2mp/weapon_rpg.cpp')
| -rw-r--r-- | mp/src/game/shared/hl2mp/weapon_rpg.cpp | 2283 |
1 files changed, 2283 insertions, 0 deletions
diff --git a/mp/src/game/shared/hl2mp/weapon_rpg.cpp b/mp/src/game/shared/hl2mp/weapon_rpg.cpp new file mode 100644 index 00000000..89af513d --- /dev/null +++ b/mp/src/game/shared/hl2mp/weapon_rpg.cpp @@ -0,0 +1,2283 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+#include "weapon_rpg.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_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 "soundent.h"
+ #include "player.h"
+ #include "rope.h"
+ #include "vstdlib/random.h"
+ #include "engine/IEngineSound.h"
+ #include "explode.h"
+ #include "util.h"
+ #include "in_buttons.h"
+ #include "shake.h"
+ #include "te_effect_dispatch.h"
+ #include "triggers.h"
+ #include "smoke_trail.h"
+ #include "collisionutils.h"
+ #include "hl2_shareddefs.h"
+#endif
+
+#include "debugoverlay_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define RPG_SPEED 1500
+
+#ifndef CLIENT_DLL
+const char *g_pLaserDotThink = "LaserThinkContext";
+
+static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15");
+#define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat()
+
+#endif
+
+#ifdef CLIENT_DLL
+#define CLaserDot C_LaserDot
+#endif
+
+//-----------------------------------------------------------------------------
+// Laser Dot
+//-----------------------------------------------------------------------------
+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;
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_DATADESC();
+public:
+ CLaserDot *m_pNext;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( LaserDot, DT_LaserDot )
+
+BEGIN_NETWORK_TABLE( CLaserDot, DT_LaserDot )
+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;
+}
+
+BEGIN_DATADESC( CMissile )
+
+ DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hRocketTrail, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_flAugerTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flMarkDeadTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ),
+ DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
+
+ // Function Pointers
+ DEFINE_FUNCTION( MissileTouch ),
+ DEFINE_FUNCTION( AccelerateThink ),
+ DEFINE_FUNCTION( AugerThink ),
+ DEFINE_FUNCTION( IgniteThink ),
+ DEFINE_FUNCTION( SeekThink ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( rpg_missile, CMissile );
+
+class CWeaponRPG;
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CMissile::CMissile()
+{
+ m_hRocketTrail = NULL;
+}
+
+CMissile::~CMissile()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CMissile::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_missile.mdl" );
+ PrecacheModel( "models/weapons/w_missile_launch.mdl" );
+ PrecacheModel( "models/weapons/w_missile_closed.mdl" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CMissile::Spawn( void )
+{
+ Precache();
+
+ SetSolid( SOLID_BBOX );
+ SetModel("models/weapons/w_missile_launch.mdl");
+ UTIL_SetSize( this, -Vector(4,4,4), Vector(4,4,4) );
+
+ SetTouch( &CMissile::MissileTouch );
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetThink( &CMissile::IgniteThink );
+
+ SetNextThink( gpGlobals->curtime + 0.3f );
+
+ m_takedamage = DAMAGE_YES;
+ m_iHealth = m_iMaxHealth = 100;
+ m_bloodColor = DONT_BLEED;
+ m_flGracePeriodEndsAt = 0;
+
+ AddFlag( FL_OBJECT );
+}
+
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CMissile::Event_Killed( const CTakeDamageInfo &info )
+{
+ m_takedamage = DAMAGE_NO;
+
+ ShotDown();
+}
+
+unsigned int CMissile::PhysicsSolidMaskForEntity( void ) const
+{
+ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+int CMissile::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ if ( ( info.GetDamageType() & (DMG_MISSILEDEFENSE | DMG_AIRBOAT) ) == false )
+ return 0;
+
+ bool bIsDamaged;
+ if( m_iHealth <= AugerHealth() )
+ {
+ // This missile is already damaged (i.e., already running AugerThink)
+ bIsDamaged = true;
+ }
+ else
+ {
+ // This missile isn't damaged enough to wobble in flight yet
+ bIsDamaged = false;
+ }
+
+ int nRetVal = BaseClass::OnTakeDamage_Alive( info );
+
+ if( !bIsDamaged )
+ {
+ if ( m_iHealth <= AugerHealth() )
+ {
+ ShotDown();
+ }
+ }
+
+ return nRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops any kind of tracking and shoots dumb
+//-----------------------------------------------------------------------------
+void CMissile::DumbFire( void )
+{
+ SetThink( NULL );
+ SetMoveType( MOVETYPE_FLY );
+
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+
+ EmitSound( "Missile.Ignite" );
+
+ // Smoke trail.
+ CreateSmokeTrail();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::SetGracePeriod( float flGracePeriod )
+{
+ m_flGracePeriodEndsAt = gpGlobals->curtime + flGracePeriod;
+
+ // Go non-solid until the grace period ends
+ AddSolidFlags( FSOLID_NOT_SOLID );
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CMissile::AccelerateThink( void )
+{
+ Vector vecForward;
+
+ // !!!UNDONE - make this work exactly the same as HL1 RPG, lest we have looping sound bugs again!
+ EmitSound( "Missile.Accelerate" );
+
+ // SetEffects( EF_LIGHT );
+
+ AngleVectors( GetLocalAngles(), &vecForward );
+ SetAbsVelocity( vecForward * RPG_SPEED );
+
+ SetThink( &CMissile::SeekThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+#define AUGER_YDEVIANCE 20.0f
+#define AUGER_XDEVIANCEUP 8.0f
+#define AUGER_XDEVIANCEDOWN 1.0f
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CMissile::AugerThink( void )
+{
+ // If we've augered long enough, then just explode
+ if ( m_flAugerTime < gpGlobals->curtime )
+ {
+ Explode();
+ return;
+ }
+
+ if ( m_flMarkDeadTime < gpGlobals->curtime )
+ {
+ m_lifeState = LIFE_DYING;
+ }
+
+ QAngle angles = GetLocalAngles();
+
+ angles.y += random->RandomFloat( -AUGER_YDEVIANCE, AUGER_YDEVIANCE );
+ angles.x += random->RandomFloat( -AUGER_XDEVIANCEDOWN, AUGER_XDEVIANCEUP );
+
+ SetLocalAngles( angles );
+
+ Vector vecForward;
+
+ AngleVectors( GetLocalAngles(), &vecForward );
+
+ SetAbsVelocity( vecForward * 1000.0f );
+
+ SetNextThink( gpGlobals->curtime + 0.05f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Causes the missile to spiral to the ground and explode, due to damage
+//-----------------------------------------------------------------------------
+void CMissile::ShotDown( void )
+{
+ CEffectData data;
+ data.m_vOrigin = GetAbsOrigin();
+
+ DispatchEffect( "RPGShotDown", data );
+
+ if ( m_hRocketTrail != NULL )
+ {
+ m_hRocketTrail->m_bDamaged = true;
+ }
+
+ SetThink( &CMissile::AugerThink );
+ SetNextThink( gpGlobals->curtime );
+ m_flAugerTime = gpGlobals->curtime + 1.5f;
+ m_flMarkDeadTime = gpGlobals->curtime + 0.75;
+
+ // Let the RPG start reloading immediately
+ if ( m_hOwner != NULL )
+ {
+ m_hOwner->NotifyRocketDied();
+ m_hOwner = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// The actual explosion
+//-----------------------------------------------------------------------------
+void CMissile::DoExplosion( void )
+{
+ // Explode
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), GetDamage() * 2,
+ SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::Explode( void )
+{
+ // Don't explode against the skybox. Just pretend that
+ // the missile flies off into the distance.
+ Vector forward;
+
+ GetVectors( &forward, NULL, NULL );
+
+ trace_t tr;
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + forward * 16, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+
+ m_takedamage = DAMAGE_NO;
+ SetSolid( SOLID_NONE );
+ if( tr.fraction == 1.0 || !(tr.surface.flags & SURF_SKY) )
+ {
+ DoExplosion();
+ }
+
+ if( m_hRocketTrail )
+ {
+ m_hRocketTrail->SetLifetime(0.1f);
+ m_hRocketTrail = NULL;
+ }
+
+ if ( m_hOwner != NULL )
+ {
+ m_hOwner->NotifyRocketDied();
+ m_hOwner = NULL;
+ }
+
+ StopSound( "Missile.Ignite" );
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CMissile::MissileTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+
+ // Don't touch triggers (but DO hit weapons)
+ if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON )
+ return;
+
+ Explode();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::CreateSmokeTrail( void )
+{
+ if ( m_hRocketTrail )
+ return;
+
+ // Smoke trail.
+ if ( (m_hRocketTrail = RocketTrail::CreateRocketTrail()) != NULL )
+ {
+ m_hRocketTrail->m_Opacity = 0.2f;
+ m_hRocketTrail->m_SpawnRate = 100;
+ m_hRocketTrail->m_ParticleLifetime = 0.5f;
+ m_hRocketTrail->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
+ m_hRocketTrail->m_EndColor.Init( 0.0, 0.0, 0.0 );
+ m_hRocketTrail->m_StartSize = 8;
+ m_hRocketTrail->m_EndSize = 32;
+ m_hRocketTrail->m_SpawnRadius = 4;
+ m_hRocketTrail->m_MinSpeed = 2;
+ m_hRocketTrail->m_MaxSpeed = 16;
+
+ m_hRocketTrail->SetLifetime( 999 );
+ m_hRocketTrail->FollowEntity( this, "0" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::IgniteThink( void )
+{
+ SetMoveType( MOVETYPE_FLY );
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+
+ //TODO: Play opening sound
+
+ Vector vecForward;
+
+ EmitSound( "Missile.Ignite" );
+
+ AngleVectors( GetLocalAngles(), &vecForward );
+ SetAbsVelocity( vecForward * RPG_SPEED );
+
+ SetThink( &CMissile::SeekThink );
+ SetNextThink( gpGlobals->curtime );
+
+ if ( m_hOwner && m_hOwner->GetOwner() )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( m_hOwner->GetOwner() );
+
+ color32 white = { 255,225,205,64 };
+ UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN );
+ }
+
+ CreateSmokeTrail();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the shooting position
+//-----------------------------------------------------------------------------
+void CMissile::GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition )
+{
+ if ( pLaserDot->GetOwnerEntity() != NULL )
+ {
+ //FIXME: Do we care this isn't exactly the muzzle position?
+ *pShootPosition = pLaserDot->GetOwnerEntity()->WorldSpaceCenter();
+ }
+ else
+ {
+ *pShootPosition = pLaserDot->GetChasePosition();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#define RPG_HOMING_SPEED 0.125f
+
+void CMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
+{
+ *pHomingSpeed = RPG_HOMING_SPEED;
+ if ( pLaserDot->GetTargetEntity() )
+ {
+ *pActualDotPosition = pLaserDot->GetChasePosition();
+ return;
+ }
+
+ Vector vLaserStart;
+ GetShootPosition( pLaserDot, &vLaserStart );
+
+ //Get the laser's vector
+ Vector vLaserDir;
+ VectorSubtract( pLaserDot->GetChasePosition(), vLaserStart, vLaserDir );
+
+ //Find the length of the current laser
+ float flLaserLength = VectorNormalize( vLaserDir );
+
+ //Find the length from the missile to the laser's owner
+ float flMissileLength = GetAbsOrigin().DistTo( vLaserStart );
+
+ //Find the length from the missile to the laser's position
+ Vector vecTargetToMissile;
+ VectorSubtract( GetAbsOrigin(), pLaserDot->GetChasePosition(), vecTargetToMissile );
+ float flTargetLength = VectorNormalize( vecTargetToMissile );
+
+ // See if we should chase the line segment nearest us
+ if ( ( flMissileLength < flLaserLength ) || ( flTargetLength <= 512.0f ) )
+ {
+ *pActualDotPosition = UTIL_PointOnLineNearestPoint( vLaserStart, pLaserDot->GetChasePosition(), GetAbsOrigin() );
+ *pActualDotPosition += ( vLaserDir * 256.0f );
+ }
+ else
+ {
+ // Otherwise chase the dot
+ *pActualDotPosition = pLaserDot->GetChasePosition();
+ }
+
+// NDebugOverlay::Line( pLaserDot->GetChasePosition(), vLaserStart, 0, 255, 0, true, 0.05f );
+// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
+// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::SeekThink( void )
+{
+ CBaseEntity *pBestDot = NULL;
+ float flBestDist = MAX_TRACE_LENGTH;
+ float dotDist;
+
+ // If we have a grace period, go solid when it ends
+ if ( m_flGracePeriodEndsAt )
+ {
+ if ( m_flGracePeriodEndsAt < gpGlobals->curtime )
+ {
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+ m_flGracePeriodEndsAt = 0;
+ }
+ }
+
+ //Search for all dots relevant to us
+ for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
+ {
+ if ( !pEnt->IsOn() )
+ continue;
+
+ if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
+ continue;
+
+ dotDist = (GetAbsOrigin() - pEnt->GetAbsOrigin()).Length();
+
+ //Find closest
+ if ( dotDist < flBestDist )
+ {
+ pBestDot = pEnt;
+ flBestDist = dotDist;
+ }
+ }
+
+ //If we have a dot target
+ if ( pBestDot == NULL )
+ {
+ //Think as soon as possible
+ SetNextThink( gpGlobals->curtime );
+ return;
+ }
+
+ CLaserDot *pLaserDot = (CLaserDot *)pBestDot;
+ Vector targetPos;
+
+ float flHomingSpeed;
+ Vector vecLaserDotPosition;
+ ComputeActualDotPosition( pLaserDot, &targetPos, &flHomingSpeed );
+
+ if ( IsSimulatingOnAlternateTicks() )
+ flHomingSpeed *= 2;
+
+ Vector vTargetDir;
+ VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir );
+ float flDist = VectorNormalize( vTargetDir );
+
+ Vector vDir = GetAbsVelocity();
+ float flSpeed = VectorNormalize( vDir );
+ Vector vNewVelocity = vDir;
+ if ( gpGlobals->frametime > 0.0f )
+ {
+ if ( flSpeed != 0 )
+ {
+ vNewVelocity = ( flHomingSpeed * vTargetDir ) + ( ( 1 - flHomingSpeed ) * vDir );
+
+ // This computation may happen to cancel itself out exactly. If so, slam to targetdir.
+ if ( VectorNormalize( vNewVelocity ) < 1e-3 )
+ {
+ vNewVelocity = (flDist != 0) ? vTargetDir : vDir;
+ }
+ }
+ else
+ {
+ vNewVelocity = vTargetDir;
+ }
+ }
+
+ QAngle finalAngles;
+ VectorAngles( vNewVelocity, finalAngles );
+ SetAbsAngles( finalAngles );
+
+ vNewVelocity *= flSpeed;
+ SetAbsVelocity( vNewVelocity );
+
+ if( GetAbsVelocity() == vec3_origin )
+ {
+ // Strange circumstances have brought this missile to halt. Just blow it up.
+ Explode();
+ return;
+ }
+
+ // Think as soon as possible
+ SetNextThink( gpGlobals->curtime );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : &vecOrigin -
+// &vecAngles -
+// NULL -
+//
+// Output : CMissile
+//-----------------------------------------------------------------------------
+CMissile *CMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner = NULL )
+{
+ //CMissile *pMissile = (CMissile *)CreateEntityByName("rpg_missile" );
+ CMissile *pMissile = (CMissile *) CBaseEntity::Create( "rpg_missile", vecOrigin, vecAngles, CBaseEntity::Instance( pentOwner ) );
+ pMissile->SetOwnerEntity( Instance( pentOwner ) );
+ pMissile->Spawn();
+ pMissile->AddEffects( EF_NOSHADOW );
+
+ Vector vecForward;
+ AngleVectors( vecAngles, &vecForward );
+
+ pMissile->SetAbsVelocity( vecForward * 300 + Vector( 0,0, 128 ) );
+
+ return pMissile;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// This entity is used to create little force boxes that the helicopter
+// should avoid.
+//-----------------------------------------------------------------------------
+class CInfoAPCMissileHint : public CBaseEntity
+{
+ DECLARE_DATADESC();
+
+public:
+ DECLARE_CLASS( CInfoAPCMissileHint, CBaseEntity );
+
+ virtual void Spawn( );
+ virtual void Activate();
+ virtual void UpdateOnRemove();
+
+ static CBaseEntity *FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
+ const Vector &vecCurrentTargetPos, const Vector &vecCurrentTargetVel );
+
+private:
+ EHANDLE m_hTarget;
+
+ typedef CHandle<CInfoAPCMissileHint> APCMissileHintHandle_t;
+ static CUtlVector< APCMissileHintHandle_t > s_APCMissileHints;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// This entity is used to create little force boxes that the helicopters should avoid.
+//
+//-----------------------------------------------------------------------------
+CUtlVector< CInfoAPCMissileHint::APCMissileHintHandle_t > CInfoAPCMissileHint::s_APCMissileHints;
+
+LINK_ENTITY_TO_CLASS( info_apc_missile_hint, CInfoAPCMissileHint );
+
+BEGIN_DATADESC( CInfoAPCMissileHint )
+ DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Spawn, remove
+//-----------------------------------------------------------------------------
+void CInfoAPCMissileHint::Spawn( )
+{
+ SetModel( STRING( GetModelName() ) );
+ SetSolid( SOLID_BSP );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ AddEffects( EF_NODRAW );
+}
+
+void CInfoAPCMissileHint::Activate( )
+{
+ BaseClass::Activate();
+
+ m_hTarget = gEntList.FindEntityByName( NULL, m_target );
+ if ( m_hTarget == NULL )
+ {
+ DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( m_target ) );
+ }
+ else
+ {
+ s_APCMissileHints.AddToTail( this );
+ }
+}
+
+void CInfoAPCMissileHint::UpdateOnRemove( )
+{
+ s_APCMissileHints.FindAndRemove( this );
+ BaseClass::UpdateOnRemove();
+}
+
+
+//-----------------------------------------------------------------------------
+// Where are how should we avoid?
+//-----------------------------------------------------------------------------
+#define HINT_PREDICTION_TIME 3.0f
+
+CBaseEntity *CInfoAPCMissileHint::FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
+ const Vector &vecCurrentEnemyPos, const Vector &vecCurrentEnemyVel )
+{
+ if ( !pTargetName )
+ return NULL;
+
+ float flOOSpeed = pMissile->GetAbsVelocity().Length();
+ if ( flOOSpeed != 0.0f )
+ {
+ flOOSpeed = 1.0f / flOOSpeed;
+ }
+
+ for ( int i = s_APCMissileHints.Count(); --i >= 0; )
+ {
+ CInfoAPCMissileHint *pHint = s_APCMissileHints[i];
+ if ( !pHint->NameMatches( pTargetName ) )
+ continue;
+
+ if ( !pHint->m_hTarget )
+ continue;
+
+ Vector vecMissileToHint, vecMissileToEnemy;
+ VectorSubtract( pHint->m_hTarget->WorldSpaceCenter(), pMissile->GetAbsOrigin(), vecMissileToHint );
+ VectorSubtract( vecCurrentEnemyPos, pMissile->GetAbsOrigin(), vecMissileToEnemy );
+ float flDistMissileToHint = VectorNormalize( vecMissileToHint );
+ VectorNormalize( vecMissileToEnemy );
+ if ( DotProduct( vecMissileToHint, vecMissileToEnemy ) < 0.866f )
+ continue;
+
+ // Determine when the target will be inside the volume.
+ // Project at most 3 seconds in advance
+ Vector vecRayDelta;
+ VectorMultiply( vecCurrentEnemyVel, HINT_PREDICTION_TIME, vecRayDelta );
+
+ BoxTraceInfo_t trace;
+ if ( !IntersectRayWithOBB( vecCurrentEnemyPos, vecRayDelta, pHint->CollisionProp()->CollisionToWorldTransform(),
+ pHint->CollisionProp()->OBBMins(), pHint->CollisionProp()->OBBMaxs(), 0.0f, &trace ))
+ {
+ continue;
+ }
+
+ // Determine the amount of time it would take the missile to reach the target
+ // If we can reach the target within the time it takes for the enemy to reach the
+ float tSqr = flDistMissileToHint * flOOSpeed / HINT_PREDICTION_TIME;
+ if ( (tSqr < (trace.t1 * trace.t1)) || (tSqr > (trace.t2 * trace.t2)) )
+ continue;
+
+ return pHint->m_hTarget;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// a list of missiles to search quickly
+//-----------------------------------------------------------------------------
+CEntityClassList<CAPCMissile> g_APCMissileList;
+template <> CAPCMissile *CEntityClassList<CAPCMissile>::m_pClassList = NULL;
+CAPCMissile *GetAPCMissileList()
+{
+ return g_APCMissileList.m_pClassList;
+}
+
+//-----------------------------------------------------------------------------
+// Finds apc missiles in cone
+//-----------------------------------------------------------------------------
+CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle )
+{
+ float flCosAngle = cos( DEG2RAD( flAngle ) );
+ for( CAPCMissile *pEnt = GetAPCMissileList(); pEnt != NULL; pEnt = pEnt->m_pNext )
+ {
+ if ( !pEnt->IsSolid() )
+ continue;
+
+ Vector vecDelta;
+ VectorSubtract( pEnt->GetAbsOrigin(), vecOrigin, vecDelta );
+ VectorNormalize( vecDelta );
+ float flDot = DotProduct( vecDelta, vecDirection );
+ if ( flDot > flCosAngle )
+ return pEnt;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Specialized version of the missile
+//
+//-----------------------------------------------------------------------------
+#define MAX_HOMING_DISTANCE 2250.0f
+#define MIN_HOMING_DISTANCE 1250.0f
+#define MAX_NEAR_HOMING_DISTANCE 1750.0f
+#define MIN_NEAR_HOMING_DISTANCE 1000.0f
+#define DOWNWARD_BLEND_TIME_START 0.2f
+#define MIN_HEIGHT_DIFFERENCE 250.0f
+#define MAX_HEIGHT_DIFFERENCE 550.0f
+#define CORRECTION_TIME 0.2f
+#define APC_LAUNCH_HOMING_SPEED 0.1f
+#define APC_HOMING_SPEED 0.025f
+#define HOMING_SPEED_ACCEL 0.01f
+
+BEGIN_DATADESC( CAPCMissile )
+
+ DEFINE_FIELD( m_flReachedTargetTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flIgnitionTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bGuidingDisabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hSpecificTarget, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_strHint, FIELD_STRING ),
+ DEFINE_FIELD( m_flLastHomingSpeed, FIELD_FLOAT ),
+// DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
+
+ DEFINE_THINKFUNC( BeginSeekThink ),
+ DEFINE_THINKFUNC( AugerStartThink ),
+ DEFINE_THINKFUNC( ExplodeThink ),
+
+ DEFINE_FUNCTION( APCMissileTouch ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( apc_missile, CAPCMissile );
+
+CAPCMissile *CAPCMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, CBaseEntity *pOwner )
+{
+ CAPCMissile *pMissile = (CAPCMissile *)CBaseEntity::Create( "apc_missile", vecOrigin, vecAngles, pOwner );
+ pMissile->SetOwnerEntity( pOwner );
+ pMissile->Spawn();
+ pMissile->SetAbsVelocity( vecVelocity );
+ pMissile->AddFlag( FL_NOTARGET );
+ pMissile->AddEffects( EF_NOSHADOW );
+ return pMissile;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CAPCMissile::CAPCMissile()
+{
+ g_APCMissileList.Insert( this );
+}
+
+CAPCMissile::~CAPCMissile()
+{
+ g_APCMissileList.Remove( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Shared initialization code
+//-----------------------------------------------------------------------------
+void CAPCMissile::Init()
+{
+ SetMoveType( MOVETYPE_FLY );
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+ CreateSmokeTrail();
+ SetTouch( &CAPCMissile::APCMissileTouch );
+ m_flLastHomingSpeed = APC_HOMING_SPEED;
+}
+
+
+//-----------------------------------------------------------------------------
+// For hitting a specific target
+//-----------------------------------------------------------------------------
+void CAPCMissile::AimAtSpecificTarget( CBaseEntity *pTarget )
+{
+ m_hSpecificTarget = pTarget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CAPCMissile::APCMissileTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() && !pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
+ return;
+
+ Explode();
+}
+
+
+//-----------------------------------------------------------------------------
+// Specialized version of the missile
+//-----------------------------------------------------------------------------
+void CAPCMissile::IgniteDelay( void )
+{
+ m_flIgnitionTime = gpGlobals->curtime + 0.3f;
+
+ SetThink( &CAPCMissile::BeginSeekThink );
+ SetNextThink( m_flIgnitionTime );
+ Init();
+ AddSolidFlags( FSOLID_NOT_SOLID );
+}
+
+void CAPCMissile::AugerDelay( float flDelay )
+{
+ m_flIgnitionTime = gpGlobals->curtime;
+ SetThink( &CAPCMissile::AugerStartThink );
+ SetNextThink( gpGlobals->curtime + flDelay );
+ Init();
+ DisableGuiding();
+}
+
+void CAPCMissile::AugerStartThink()
+{
+ if ( m_hRocketTrail != NULL )
+ {
+ m_hRocketTrail->m_bDamaged = true;
+ }
+ m_flAugerTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
+ SetThink( &CAPCMissile::AugerThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+void CAPCMissile::ExplodeDelay( float flDelay )
+{
+ m_flIgnitionTime = gpGlobals->curtime;
+ SetThink( &CAPCMissile::ExplodeThink );
+ SetNextThink( gpGlobals->curtime + flDelay );
+ Init();
+ DisableGuiding();
+}
+
+
+void CAPCMissile::BeginSeekThink( void )
+{
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+ SetThink( &CAPCMissile::SeekThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+void CAPCMissile::ExplodeThink()
+{
+ DoExplosion();
+}
+
+//-----------------------------------------------------------------------------
+// Health lost at which augering starts
+//-----------------------------------------------------------------------------
+int CAPCMissile::AugerHealth()
+{
+ return m_iMaxHealth - 25;
+}
+
+
+//-----------------------------------------------------------------------------
+// Health lost at which augering starts
+//-----------------------------------------------------------------------------
+void CAPCMissile::DisableGuiding()
+{
+ m_bGuidingDisabled = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Guidance hints
+//-----------------------------------------------------------------------------
+void CAPCMissile::SetGuidanceHint( const char *pHintName )
+{
+ m_strHint = MAKE_STRING( pHintName );
+}
+
+
+//-----------------------------------------------------------------------------
+// The actual explosion
+//-----------------------------------------------------------------------------
+void CAPCMissile::DoExplosion( void )
+{
+ if ( GetWaterLevel() != 0 )
+ {
+ CEffectData data;
+ data.m_vOrigin = WorldSpaceCenter();
+ data.m_flMagnitude = 128;
+ data.m_flScale = 128;
+ data.m_fFlags = 0;
+ DispatchEffect( "WaterSurfaceExplosion", data );
+ }
+ else
+ {
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(),
+ APC_MISSILE_DAMAGE, 100, true, 20000 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCMissile::ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition )
+{
+ Vector vecTarget = pTarget->BodyTarget( vecShootPosition, false );
+ float flShotSpeed = GetAbsVelocity().Length();
+ if ( flShotSpeed == 0 )
+ {
+ *pLeadPosition = vecTarget;
+ return;
+ }
+
+ Vector vecVelocity = pTarget->GetSmoothedVelocity();
+ vecVelocity.z = 0.0f;
+ float flTargetSpeed = VectorNormalize( vecVelocity );
+ Vector vecDelta;
+ VectorSubtract( vecShootPosition, vecTarget, vecDelta );
+ float flTargetToShooter = VectorNormalize( vecDelta );
+ float flCosTheta = DotProduct( vecDelta, vecVelocity );
+
+ // Law of cosines... z^2 = x^2 + y^2 - 2xy cos Theta
+ // where z = flShooterToPredictedTargetPosition = flShotSpeed * predicted time
+ // x = flTargetSpeed * predicted time
+ // y = flTargetToShooter
+ // solve for predicted time using at^2 + bt + c = 0, t = (-b +/- sqrt( b^2 - 4ac )) / 2a
+ float a = flTargetSpeed * flTargetSpeed - flShotSpeed * flShotSpeed;
+ float b = -2.0f * flTargetToShooter * flCosTheta * flTargetSpeed;
+ float c = flTargetToShooter * flTargetToShooter;
+
+ float flDiscrim = b*b - 4*a*c;
+ if (flDiscrim < 0)
+ {
+ *pLeadPosition = vecTarget;
+ return;
+ }
+
+ flDiscrim = sqrt(flDiscrim);
+ float t = (-b + flDiscrim) / (2.0f * a);
+ float t2 = (-b - flDiscrim) / (2.0f * a);
+ if ( t < t2 )
+ {
+ t = t2;
+ }
+
+ if ( t <= 0.0f )
+ {
+ *pLeadPosition = vecTarget;
+ return;
+ }
+
+ VectorMA( vecTarget, flTargetSpeed * t, vecVelocity, *pLeadPosition );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
+{
+ if ( m_bGuidingDisabled )
+ {
+ *pActualDotPosition = GetAbsOrigin();
+ *pHomingSpeed = 0.0f;
+ m_flLastHomingSpeed = *pHomingSpeed;
+ return;
+ }
+
+ if ( ( m_strHint != NULL_STRING ) && (!m_hSpecificTarget) )
+ {
+ Vector vecOrigin, vecVelocity;
+ CBaseEntity *pTarget = pLaserDot->GetTargetEntity();
+ if ( pTarget )
+ {
+ vecOrigin = pTarget->BodyTarget( GetAbsOrigin(), false );
+ vecVelocity = pTarget->GetSmoothedVelocity();
+ }
+ else
+ {
+ vecOrigin = pLaserDot->GetChasePosition();
+ vecVelocity = vec3_origin;
+ }
+
+ m_hSpecificTarget = CInfoAPCMissileHint::FindAimTarget( this, STRING( m_strHint ), vecOrigin, vecVelocity );
+ }
+
+ CBaseEntity *pLaserTarget = m_hSpecificTarget ? m_hSpecificTarget.Get() : pLaserDot->GetTargetEntity();
+ if ( !pLaserTarget )
+ {
+ BaseClass::ComputeActualDotPosition( pLaserDot, pActualDotPosition, pHomingSpeed );
+ m_flLastHomingSpeed = *pHomingSpeed;
+ return;
+ }
+
+ if ( pLaserTarget->ClassMatches( "npc_bullseye" ) )
+ {
+ if ( m_flLastHomingSpeed != RPG_HOMING_SPEED )
+ {
+ if (m_flLastHomingSpeed > RPG_HOMING_SPEED)
+ {
+ m_flLastHomingSpeed -= HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
+ if ( m_flLastHomingSpeed < RPG_HOMING_SPEED )
+ {
+ m_flLastHomingSpeed = RPG_HOMING_SPEED;
+ }
+ }
+ else
+ {
+ m_flLastHomingSpeed += HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
+ if ( m_flLastHomingSpeed > RPG_HOMING_SPEED )
+ {
+ m_flLastHomingSpeed = RPG_HOMING_SPEED;
+ }
+ }
+ }
+ *pHomingSpeed = m_flLastHomingSpeed;
+ *pActualDotPosition = pLaserTarget->WorldSpaceCenter();
+ return;
+ }
+
+ Vector vLaserStart;
+ GetShootPosition( pLaserDot, &vLaserStart );
+ *pHomingSpeed = APC_LAUNCH_HOMING_SPEED;
+
+ //Get the laser's vector
+ Vector vecTargetPosition = pLaserTarget->BodyTarget( GetAbsOrigin(), false );
+
+ // Compute leading position
+ Vector vecLeadPosition;
+ ComputeLeadingPosition( GetAbsOrigin(), pLaserTarget, &vecLeadPosition );
+
+ Vector vecTargetToMissile, vecTargetToShooter;
+ VectorSubtract( GetAbsOrigin(), vecTargetPosition, vecTargetToMissile );
+ VectorSubtract( vLaserStart, vecTargetPosition, vecTargetToShooter );
+
+ *pActualDotPosition = vecLeadPosition;
+
+ float flMinHomingDistance = MIN_HOMING_DISTANCE;
+ float flMaxHomingDistance = MAX_HOMING_DISTANCE;
+ float flBlendTime = gpGlobals->curtime - m_flIgnitionTime;
+ if ( flBlendTime > DOWNWARD_BLEND_TIME_START )
+ {
+ if ( m_flReachedTargetTime != 0.0f )
+ {
+ *pHomingSpeed = APC_HOMING_SPEED;
+ float flDeltaTime = clamp( gpGlobals->curtime - m_flReachedTargetTime, 0.0f, CORRECTION_TIME );
+ *pHomingSpeed = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, 0.2f, *pHomingSpeed );
+ flMinHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MIN_NEAR_HOMING_DISTANCE, flMinHomingDistance );
+ flMaxHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MAX_NEAR_HOMING_DISTANCE, flMaxHomingDistance );
+ }
+ else
+ {
+ flMinHomingDistance = MIN_NEAR_HOMING_DISTANCE;
+ flMaxHomingDistance = MAX_NEAR_HOMING_DISTANCE;
+ Vector vecDelta;
+ VectorSubtract( GetAbsOrigin(), *pActualDotPosition, vecDelta );
+ if ( vecDelta.z > MIN_HEIGHT_DIFFERENCE )
+ {
+ float flClampedHeight = clamp( vecDelta.z, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE );
+ float flHeightAdjustFactor = SimpleSplineRemapVal( flClampedHeight, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE, 0.0f, 1.0f );
+
+ vecDelta.z = 0.0f;
+ float flDist = VectorNormalize( vecDelta );
+
+ float flForwardOffset = 2000.0f;
+ if ( flDist > flForwardOffset )
+ {
+ Vector vecNewPosition;
+ VectorMA( GetAbsOrigin(), -flForwardOffset, vecDelta, vecNewPosition );
+ vecNewPosition.z = pActualDotPosition->z;
+
+ VectorLerp( *pActualDotPosition, vecNewPosition, flHeightAdjustFactor, *pActualDotPosition );
+ }
+ }
+ else
+ {
+ m_flReachedTargetTime = gpGlobals->curtime;
+ }
+ }
+
+ // Allows for players right at the edge of rocket range to be threatened
+ if ( flBlendTime > 0.6f )
+ {
+ float flTargetLength = GetAbsOrigin().DistTo( pLaserTarget->WorldSpaceCenter() );
+ flTargetLength = clamp( flTargetLength, flMinHomingDistance, flMaxHomingDistance );
+ *pHomingSpeed = SimpleSplineRemapVal( flTargetLength, flMaxHomingDistance, flMinHomingDistance, *pHomingSpeed, 0.01f );
+ }
+ }
+
+ float flDot = DotProduct2D( vecTargetToShooter.AsVector2D(), vecTargetToMissile.AsVector2D() );
+ if ( ( flDot < 0 ) || m_bGuidingDisabled )
+ {
+ *pHomingSpeed = 0.0f;
+ }
+
+ m_flLastHomingSpeed = *pHomingSpeed;
+
+// NDebugOverlay::Line( vecLeadPosition, GetAbsOrigin(), 0, 255, 0, true, 0.05f );
+// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
+// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
+}
+
+#endif
+
+#define RPG_BEAM_SPRITE "effects/laser1.vmt"
+#define RPG_BEAM_SPRITE_NOZ "effects/laser1_noz.vmt"
+#define RPG_LASER_SPRITE "sprites/redglow1"
+
+//=============================================================================
+// RPG
+//=============================================================================
+
+LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
+PRECACHE_WEAPON_REGISTER(weapon_rpg);
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRPG, DT_WeaponRPG )
+
+#ifdef CLIENT_DLL
+void RecvProxy_MissileDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ CWeaponRPG *pRPG = ((CWeaponRPG*)pStruct);
+
+ RecvProxy_IntToEHandle( pData, pStruct, pOut );
+
+ CBaseEntity *pNewMissile = pRPG->GetMissile();
+
+ if ( pNewMissile == NULL )
+ {
+ if ( pRPG->GetOwner() && pRPG->GetOwner()->GetActiveWeapon() == pRPG )
+ {
+ pRPG->NotifyRocketDied();
+ }
+ }
+}
+
+#endif
+
+BEGIN_NETWORK_TABLE( CWeaponRPG, DT_WeaponRPG )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bInitialStateUpdate ) ),
+ RecvPropBool( RECVINFO( m_bGuiding ) ),
+ RecvPropBool( RECVINFO( m_bHideGuiding ) ),
+ RecvPropEHandle( RECVINFO( m_hMissile ), RecvProxy_MissileDied ),
+ RecvPropVector( RECVINFO( m_vecLaserDot ) ),
+#else
+ SendPropBool( SENDINFO( m_bInitialStateUpdate ) ),
+ SendPropBool( SENDINFO( m_bGuiding ) ),
+ SendPropBool( SENDINFO( m_bHideGuiding ) ),
+ SendPropEHandle( SENDINFO( m_hMissile ) ),
+ SendPropVector( SENDINFO( m_vecLaserDot ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+
+BEGIN_PREDICTION_DATA( CWeaponRPG )
+ DEFINE_PRED_FIELD( m_bInitialStateUpdate, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bHideGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+
+#endif
+
+#ifndef CLIENT_DLL
+acttable_t CWeaponRPG::m_acttable[] =
+{
+ { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_RPG, false },
+ { ACT_HL2MP_RUN, ACT_HL2MP_RUN_RPG, false },
+ { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_RPG, false },
+ { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_RPG, false },
+ { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG, false },
+ { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_RPG, false },
+ { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_RPG, false },
+ { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, false },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponRPG);
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponRPG::CWeaponRPG()
+{
+ m_bReloadsSingly = true;
+ m_bInitialStateUpdate= false;
+ m_bHideGuiding = false;
+ m_bGuiding = false;
+
+ m_fMinRange1 = m_fMinRange2 = 40*12;
+ m_fMaxRange1 = m_fMaxRange2 = 500*12;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponRPG::~CWeaponRPG()
+{
+#ifndef CLIENT_DLL
+ if ( m_hLaserDot != NULL )
+ {
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "Missile.Ignite" );
+ PrecacheScriptSound( "Missile.Accelerate" );
+
+ // Laser dot...
+ PrecacheModel( "sprites/redglow1.vmt" );
+ PrecacheModel( RPG_LASER_SPRITE );
+ PrecacheModel( RPG_BEAM_SPRITE );
+ PrecacheModel( RPG_BEAM_SPRITE_NOZ );
+
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther( "rpg_missile" );
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Activate( void )
+{
+ BaseClass::Activate();
+
+ // Restore the laser pointer after transition
+ if ( m_bGuiding )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->GetActiveWeapon() == this )
+ {
+ StartGuiding();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::HasAnyAmmo( void )
+{
+ if ( m_hMissile != NULL )
+ return true;
+
+ return BaseClass::HasAnyAmmo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::WeaponShouldBeLowered( void )
+{
+ // Lower us if we're out of ammo
+ if ( !HasAnyAmmo() )
+ return true;
+
+ return BaseClass::WeaponShouldBeLowered();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::PrimaryAttack( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if (!pPlayer)
+ return;
+
+ // Can't have an active missile out
+ if ( m_hMissile != NULL )
+ return;
+
+ // Can't be reloading
+ if ( GetActivity() == ACT_VM_RELOAD )
+ return;
+
+ Vector vecOrigin;
+ Vector vecForward;
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ Vector vForward, vRight, vUp;
+
+ pOwner->EyeVectors( &vForward, &vRight, &vUp );
+
+ Vector muzzlePoint = pOwner->Weapon_ShootPosition() + vForward * 12.0f + vRight * 6.0f + vUp * -3.0f;
+
+#ifndef CLIENT_DLL
+ QAngle vecAngles;
+ VectorAngles( vForward, vecAngles );
+
+ CMissile *pMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
+ pMissile->m_hOwner = this;
+
+ // If the shot is clear to the player, give the missile a grace period
+ trace_t tr;
+ Vector vecEye = pOwner->EyePosition();
+ UTIL_TraceLine( vecEye, vecEye + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1.0 )
+ {
+ pMissile->SetGracePeriod( 0.3 );
+ }
+
+ pMissile->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
+
+ m_hMissile = pMissile;
+#endif
+
+ DecrementAmmo( GetOwner() );
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ WeaponSound( SINGLE );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOwner -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::DecrementAmmo( CBaseCombatCharacter *pOwner )
+{
+ // Take away our primary ammo type
+ pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::SuppressGuiding( bool state )
+{
+ m_bHideGuiding = state;
+
+#ifndef CLIENT_DLL
+
+ if ( m_hLaserDot == NULL )
+ {
+ StartGuiding();
+
+ //STILL!?
+ if ( m_hLaserDot == NULL )
+ return;
+ }
+
+ if ( state )
+ {
+ m_hLaserDot->TurnOff();
+ }
+ else
+ {
+ m_hLaserDot->TurnOn();
+ }
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override this if we're guiding a missile currently
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Lower( void )
+{
+ if ( m_hMissile != NULL )
+ return false;
+
+ return BaseClass::Lower();
+}
+
+//-----------------------------------------------------------------------------
+// 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_bInitialStateUpdate ) && ( GetActivity() != ACT_VM_DRAW ) )
+ {
+ StartGuiding();
+ m_bInitialStateUpdate = false;
+ }
+
+ // Supress our guiding effects if we're lowered
+ if ( GetIdealActivity() == ACT_VM_IDLE_LOWERED )
+ {
+ SuppressGuiding();
+ }
+ else
+ {
+ SuppressGuiding( false );
+ }
+
+ //Move the laser
+ UpdateLaserPosition();
+
+ if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 && m_hMissile == NULL )
+ {
+ StopGuiding();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Vector
+//-----------------------------------------------------------------------------
+Vector CWeaponRPG::GetLaserPosition( void )
+{
+#ifndef CLIENT_DLL
+ CreateLaserPointer();
+
+ if ( m_hLaserDot != NULL )
+ return m_hLaserDot->GetAbsOrigin();
+
+ //FIXME: The laser dot sprite is not active, this code should not be allowed!
+ assert(0);
+#endif
+ return vec3_origin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: NPC RPG users cheat and directly set the laser pointer's origin
+// Input : &vecTarget -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget )
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CWeaponRPG::GetNPCLaserPosition( void )
+{
+ return vec3_origin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true if the rocket is being guided, false if it's dumb
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::IsGuiding( void )
+{
+ return m_bGuiding;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Deploy( void )
+{
+ m_bInitialStateUpdate = true;
+
+ return BaseClass::Deploy();
+}
+
+bool CWeaponRPG::CanHolster( void )
+{
+ //Can't have an active missile out
+ if ( m_hMissile != NULL )
+ return false;
+
+ return BaseClass::CanHolster();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ StopGuiding();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn on the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StartGuiding( void )
+{
+ // Don't start back up if we're overriding this
+ if ( m_bHideGuiding )
+ return;
+
+ m_bGuiding = true;
+
+#ifndef CLIENT_DLL
+ WeaponSound(SPECIAL1);
+
+ CreateLaserPointer();
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn off the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StopGuiding( void )
+{
+ m_bGuiding = false;
+
+#ifndef CLIENT_DLL
+
+ WeaponSound( SPECIAL2 );
+
+ // Kill the dot completely
+ if ( m_hLaserDot != NULL )
+ {
+ m_hLaserDot->TurnOff();
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+#else
+ if ( m_pBeam )
+ {
+ //Tell it to die right away and let the beam code free it.
+ m_pBeam->brightness = 0.0f;
+ m_pBeam->flags &= ~FBEAM_FOREVER;
+ m_pBeam->die = gpGlobals->curtime - 0.1;
+ m_pBeam = NULL;
+ }
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::ToggleGuiding( void )
+{
+ if ( IsGuiding() )
+ {
+ StopGuiding();
+ }
+ else
+ {
+ StartGuiding();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Drop( const Vector &vecVelocity )
+{
+ StopGuiding();
+
+ BaseClass::Drop( vecVelocity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::UpdateLaserPosition( Vector vecMuzzlePos, Vector vecEndPos )
+{
+
+#ifndef CLIENT_DLL
+ if ( vecMuzzlePos == vec3_origin || vecEndPos == vec3_origin )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ vecMuzzlePos = pPlayer->Weapon_ShootPosition();
+ Vector forward;
+ pPlayer->EyeVectors( &forward );
+ vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
+ }
+
+ //Move the laser dot, if active
+ trace_t tr;
+
+ // Trace out for the endpoint
+ UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), GetOwner(), COLLISION_GROUP_NONE, &tr );
+
+ // Move the laser sprite
+ if ( m_hLaserDot != NULL )
+ {
+ Vector laserPos = tr.endpos;
+ m_hLaserDot->SetLaserPosition( laserPos, tr.plane.normal );
+
+ if ( tr.DidHitNonWorldEntity() )
+ {
+ CBaseEntity *pHit = tr.m_pEnt;
+
+ if ( ( pHit != NULL ) && ( pHit->m_takedamage ) )
+ {
+ m_hLaserDot->SetTargetEntity( pHit );
+ }
+ else
+ {
+ m_hLaserDot->SetTargetEntity( NULL );
+ }
+ }
+ else
+ {
+ m_hLaserDot->SetTargetEntity( NULL );
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::CreateLaserPointer( void )
+{
+#ifndef CLIENT_DLL
+ if ( m_hLaserDot != NULL )
+ return;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return;
+
+ m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwner() );
+ m_hLaserDot->TurnOff();
+
+ UpdateLaserPosition();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::NotifyRocketDied( void )
+{
+ m_hMissile = NULL;
+
+ if ( GetActivity() == ACT_VM_RELOAD )
+ return;
+
+ Reload();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Reload( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return false;
+
+ if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return false;
+
+ WeaponSound( RELOAD );
+
+ SendWeaponAnim( ACT_VM_RELOAD );
+
+ return true;
+}
+
+#ifdef CLIENT_DLL
+
+#define RPG_MUZZLE_ATTACHMENT 1
+#define RPG_GUIDE_ATTACHMENT 2
+#define RPG_GUIDE_TARGET_ATTACHMENT 3
+
+#define RPG_GUIDE_ATTACHMENT_3RD 4
+#define RPG_GUIDE_TARGET_ATTACHMENT_3RD 5
+
+#define RPG_LASER_BEAM_LENGTH 128
+
+extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the attachment point on either the world or viewmodel
+// This should really be worked into the CBaseCombatWeapon class!
+//-----------------------------------------------------------------------------
+void CWeaponRPG::GetWeaponAttachment( int attachmentId, Vector &outVector, Vector *dir /*= NULL*/ )
+{
+ QAngle angles;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ pOwner->GetViewModel()->GetAttachment( attachmentId, outVector, angles );
+ ::FormatViewModelAttachment( outVector, true );
+ }
+ }
+ else
+ {
+ // We offset the IDs to make them correct for our world model
+ BaseClass::GetAttachment( attachmentId, outVector, angles );
+ }
+
+ // Supply the direction, if requested
+ if ( dir != NULL )
+ {
+ AngleVectors( angles, dir, NULL, NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup our laser beam
+//-----------------------------------------------------------------------------
+void CWeaponRPG::InitBeam( void )
+{
+ if ( m_pBeam != NULL )
+ return;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return;
+
+
+ BeamInfo_t beamInfo;
+
+ CBaseEntity *pEntity = NULL;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ pEntity = pOwner->GetViewModel();
+ }
+ }
+ else
+ {
+ pEntity = this;
+ }
+
+ beamInfo.m_pStartEnt = pEntity;
+ beamInfo.m_pEndEnt = pEntity;
+ beamInfo.m_nType = TE_BEAMPOINTS;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = vec3_origin;
+
+ beamInfo.m_pszModelName = ( ShouldDrawUsingViewModel() ) ? RPG_BEAM_SPRITE_NOZ : RPG_BEAM_SPRITE;
+
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ beamInfo.m_flWidth = 2.0f;
+ beamInfo.m_flEndWidth = 2.0f;
+ beamInfo.m_nStartAttachment = RPG_GUIDE_ATTACHMENT;
+ beamInfo.m_nEndAttachment = RPG_GUIDE_TARGET_ATTACHMENT;
+ }
+ else
+ {
+ beamInfo.m_flWidth = 1.0f;
+ beamInfo.m_flEndWidth = 1.0f;
+ beamInfo.m_nStartAttachment = RPG_GUIDE_ATTACHMENT_3RD;
+ beamInfo.m_nEndAttachment = RPG_GUIDE_TARGET_ATTACHMENT_3RD;
+ }
+
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 0;
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 1.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 0.0;
+ beamInfo.m_flBlue = 0.0;
+ beamInfo.m_nSegments = 4;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = (FBEAM_FOREVER|FBEAM_SHADEOUT);
+
+ m_pBeam = beams->CreateBeamEntPoint( beamInfo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw effects for our weapon
+//-----------------------------------------------------------------------------
+void CWeaponRPG::DrawEffects( void )
+{
+ // Must be guiding and not hidden
+ if ( !m_bGuiding || m_bHideGuiding )
+ {
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->brightness = 0;
+ }
+
+ return;
+ }
+
+ // Setup our sprite
+ if ( m_hSpriteMaterial == NULL )
+ {
+ m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+
+ // Setup our beam
+ if ( m_hBeamMaterial == NULL )
+ {
+ m_hBeamMaterial.Init( RPG_BEAM_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+
+ color32 color={255,255,255,255};
+ Vector vecAttachment, vecDir;
+ QAngle angles;
+
+ float scale = 8.0f + random->RandomFloat( -2.0f, 2.0f );
+
+ int attachmentID = ( ShouldDrawUsingViewModel() ) ? RPG_GUIDE_ATTACHMENT : RPG_GUIDE_ATTACHMENT_3RD;
+
+ GetWeaponAttachment( attachmentID, vecAttachment, &vecDir );
+
+ // Draw the sprite
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_hSpriteMaterial, this );
+ DrawSprite( vecAttachment, scale, scale, color );
+
+ // Get the beam's run
+ trace_t tr;
+ UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * RPG_LASER_BEAM_LENGTH ), MASK_SHOT, GetOwner(), COLLISION_GROUP_NONE, &tr );
+
+ InitBeam();
+
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->fadeLength = RPG_LASER_BEAM_LENGTH * tr.fraction;
+ m_pBeam->brightness = random->RandomInt( 128, 200 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called on third-person weapon drawing
+//-----------------------------------------------------------------------------
+int CWeaponRPG::DrawModel( int flags )
+{
+ // Only render these on the transparent pass
+ if ( flags & STUDIO_TRANSPARENCY )
+ {
+ DrawEffects();
+ return 1;
+ }
+
+ // Draw the model as normal
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called after first-person viewmodel is drawn
+//-----------------------------------------------------------------------------
+void CWeaponRPG::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
+{
+ // Draw our laser effects
+ DrawEffects();
+
+ BaseClass::ViewModelDrawn( pBaseViewModel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to determine sorting of model when drawn
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::IsTranslucent( void )
+{
+ // Must be guiding and not hidden
+ if ( m_bGuiding && !m_bHideGuiding )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turns off effects when leaving the PVS
+//-----------------------------------------------------------------------------
+void CWeaponRPG::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit(state);
+
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->brightness = 0.0f;
+ }
+ }
+}
+
+#endif //CLIENT_DLL
+
+
+//=============================================================================
+// Laser Dot
+//=============================================================================
+
+LINK_ENTITY_TO_CLASS( env_laserdot, 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( "env_laserdot", 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(4,4,4), Vector(4,4,4) );
+
+ 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;
+ if ( m_bVisibleLaserDot )
+ {
+ //BaseClass::TurnOn();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLaserDot::TurnOff( void )
+{
+ m_bIsOn = false;
+ 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_HL2MP_Player *pOwner = ToHL2MPPlayer( 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->GetAnimEyeAngles();
+ 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
|