aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/weapon_rpg.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/weapon_rpg.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/hl2/weapon_rpg.cpp')
-rw-r--r--mp/src/game/server/hl2/weapon_rpg.cpp4752
1 files changed, 2376 insertions, 2376 deletions
diff --git a/mp/src/game/server/hl2/weapon_rpg.cpp b/mp/src/game/server/hl2/weapon_rpg.cpp
index 5bc7a1bd..e6278593 100644
--- a/mp/src/game/server/hl2/weapon_rpg.cpp
+++ b/mp/src/game/server/hl2/weapon_rpg.cpp
@@ -1,2376 +1,2376 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "basehlcombatweapon.h"
-#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 "weapon_rpg.h"
-#include "shake.h"
-#include "ai_basenpc.h"
-#include "ai_squad.h"
-#include "te_effect_dispatch.h"
-#include "triggers.h"
-#include "smoke_trail.h"
-#include "collisionutils.h"
-#include "hl2_shareddefs.h"
-#include "rumble_shared.h"
-#include "gamestats.h"
-
-#ifdef PORTAL
- #include "portal_util_shared.h"
-#endif
-
-#ifdef HL2_DLL
- extern int g_interactionPlayerLaunchedRPG;
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define RPG_SPEED 1500
-
-static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15");
-ConVar rpg_missle_use_custom_detonators( "rpg_missle_use_custom_detonators", "1" );
-
-#define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat()
-
-const char *g_pLaserDotThink = "LaserThinkContext";
-
-//-----------------------------------------------------------------------------
-// Laser Dot
-//-----------------------------------------------------------------------------
-class CLaserDot : public CSprite
-{
- DECLARE_CLASS( CLaserDot, CSprite );
-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 );
-
- void LaserThink( void );
-
- int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
-
- void MakeInvisible( void );
-
-protected:
- Vector m_vecSurfaceNormal;
- EHANDLE m_hTargetEnt;
- bool m_bVisibleLaserDot;
- bool m_bIsOn;
-
- DECLARE_DATADESC();
-public:
- CLaserDot *m_pNext;
-};
-
-// 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 ),
- DEFINE_FIELD( m_bCreateDangerSounds, FIELD_BOOLEAN ),
-
- // 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;
- m_bCreateDangerSounds = false;
-}
-
-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 );
- SetDamage( 200.0f );
-
- 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(), CMissile::EXPLOSION_RADIUS,
- 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 )
- {
- // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
- if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) )
- 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() );
-
- if ( pPlayer )
- {
- color32 white = { 255,225,205,64 };
- UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN );
-
- pPlayer->RumbleEffect( RUMBLE_RPG_MISSILE, 0, RUMBLE_FLAG_RESTART );
- }
- }
-
- 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( hl2_episodic.GetBool() )
- {
- if( flBestDist <= ( GetAbsVelocity().Length() * 2.5f ) && FVisible( pBestDot->GetAbsOrigin() ) )
- {
- // Scare targets
- CSoundEnt::InsertSound( SOUND_DANGER, pBestDot->GetAbsOrigin(), CMissile::EXPLOSION_RADIUS, 0.2f, pBestDot, SOUNDENT_CHANNEL_REPEATED_DANGER, NULL );
- }
- }
-
- if ( rpg_missle_use_custom_detonators.GetBool() )
- {
- for ( int i = gm_CustomDetonators.Count() - 1; i >=0; --i )
- {
- CustomDetonator_t &detonator = gm_CustomDetonators[i];
- if ( !detonator.hEntity )
- {
- gm_CustomDetonators.FastRemove( i );
- }
- else
- {
- const Vector &vPos = detonator.hEntity->CollisionProp()->WorldSpaceCenter();
- if ( detonator.halfHeight > 0 )
- {
- if ( fabsf( vPos.z - GetAbsOrigin().z ) < detonator.halfHeight )
- {
- if ( ( GetAbsOrigin().AsVector2D() - vPos.AsVector2D() ).LengthSqr() < detonator.radiusSq )
- {
- Explode();
- return;
- }
- }
- }
- else
- {
- if ( ( GetAbsOrigin() - vPos ).LengthSqr() < detonator.radiusSq )
- {
- Explode();
- return;
- }
- }
- }
- }
- }
-
- //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 );
-
- if( pLaserDot->GetTargetEntity() != NULL && flDist <= 240.0f && hl2_episodic.GetBool() )
- {
- // Prevent the missile circling the Strider like a Halo in ep1_c17_06. If the missile gets within 20
- // feet of a Strider, tighten up the turn speed of the missile so it can break the halo and strike. (sjb 4/27/2006)
- if( pLaserDot->GetTargetEntity()->ClassMatches( "npc_strider" ) )
- {
- flHomingSpeed *= 1.75f;
- }
- }
-
- 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 );
-
-#ifdef HL2_EPISODIC
-
- if ( m_bCreateDangerSounds == true )
- {
- trace_t tr;
- UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
-
- CSoundEnt::InsertSound( SOUND_DANGER, tr.endpos, 100, 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
- }
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// 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;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-CUtlVector<CMissile::CustomDetonator_t> CMissile::gm_CustomDetonators;
-
-void CMissile::AddCustomDetonator( CBaseEntity *pEntity, float radius, float height )
-{
- int i = gm_CustomDetonators.AddToTail();
- gm_CustomDetonators[i].hEntity = pEntity;
- gm_CustomDetonators[i].radiusSq = Square( radius );
- gm_CustomDetonators[i].halfHeight = height * 0.5f;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CMissile::RemoveCustomDetonator( CBaseEntity *pEntity )
-{
- for ( int i = 0; i < gm_CustomDetonators.Count(); i++ )
- {
- if ( gm_CustomDetonators[i].hEntity == pEntity )
- {
- gm_CustomDetonators.FastRemove( i );
- break;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// 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_THINKFUNC( APCSeekThink ),
-
- 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;
- CreateDangerSounds( true );
-
-
- if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
- {
- AddFlag( FL_AIMTARGET );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// 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::APCSeekThink );
- SetNextThink( gpGlobals->curtime );
-}
-
-void CAPCMissile::APCSeekThink( void )
-{
- BaseClass::SeekThink();
-
- bool bFoundDot = false;
-
- //If we can't find a dot to follow around then just send me wherever I'm facing so I can blow up in peace.
- for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
- {
- if ( !pEnt->IsOn() )
- continue;
-
- if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
- continue;
-
- bFoundDot = true;
- }
-
- if ( bFoundDot == false )
- {
- Vector vDir = GetAbsVelocity();
- VectorNormalize ( vDir );
-
- SetAbsVelocity( vDir * 800 );
-
- SetThink( NULL );
- }
-}
-
-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
- {
-#ifdef HL2_EPISODIC
- ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, APC_MISSILE_DAMAGE, 100, true, 20000 );
-#else
- ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), APC_MISSILE_DAMAGE, 100, true, 20000 );
-#endif
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// 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 );
-}
-
-#define RPG_BEAM_SPRITE "effects/laser1_noz.vmt"
-#define RPG_LASER_SPRITE "sprites/redglow1.vmt"
-
-//=============================================================================
-// RPG
-//=============================================================================
-
-BEGIN_DATADESC( CWeaponRPG )
-
- DEFINE_FIELD( m_bInitialStateUpdate,FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bGuiding, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_vecNPCLaserDot, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hMissile, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hLaserMuzzleSprite, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hLaserBeam, FIELD_EHANDLE ),
- DEFINE_FIELD( m_bHideGuiding, FIELD_BOOLEAN ),
-
-END_DATADESC()
-
-IMPLEMENT_SERVERCLASS_ST(CWeaponRPG, DT_WeaponRPG)
-END_SEND_TABLE()
-
-LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
-PRECACHE_WEAPON_REGISTER(weapon_rpg);
-
-acttable_t CWeaponRPG::m_acttable[] =
-{
- { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true },
-
- { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true },
- { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true },
- { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, true },
-
- { ACT_IDLE, ACT_IDLE_RPG, true },
- { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_RPG, true },
- { ACT_WALK, ACT_WALK_RPG, true },
- { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RPG, true },
- { ACT_RUN, ACT_RUN_RPG, true },
- { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true },
- { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponRPG);
-
-//-----------------------------------------------------------------------------
-// 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()
-{
- if ( m_hLaserDot != NULL )
- {
- UTIL_Remove( m_hLaserDot );
- m_hLaserDot = NULL;
- }
-
- if ( m_hLaserMuzzleSprite )
- {
- UTIL_Remove( m_hLaserMuzzleSprite );
- }
-
- if ( m_hLaserBeam )
- {
- UTIL_Remove( m_hLaserBeam );
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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 );
-
- UTIL_PrecacheOther( "rpg_missile" );
-}
-
-
-//-----------------------------------------------------------------------------
-// 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:
-// Input : *pEvent -
-// *pOperator -
-//-----------------------------------------------------------------------------
-void CWeaponRPG::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
-{
- switch( pEvent->event )
- {
- case EVENT_WEAPON_SMG1:
- {
- if ( m_hMissile != NULL )
- return;
-
- Vector muzzlePoint;
- QAngle vecAngles;
-
- muzzlePoint = GetOwner()->Weapon_ShootPosition();
-
- CAI_BaseNPC *npc = pOperator->MyNPCPointer();
- ASSERT( npc != NULL );
-
- Vector vecShootDir = npc->GetActualShootTrajectory( muzzlePoint );
-
- // look for a better launch location
- Vector altLaunchPoint;
- if (GetAttachment( "missile", altLaunchPoint ))
- {
- // check to see if it's relativly free
- trace_t tr;
- AI_TraceHull( altLaunchPoint, altLaunchPoint + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
-
- if( tr.fraction == 1.0)
- {
- muzzlePoint = altLaunchPoint;
- }
- }
-
- VectorAngles( vecShootDir, vecAngles );
-
- m_hMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
- m_hMissile->m_hOwner = this;
-
- // NPCs always get a grace period
- m_hMissile->SetGracePeriod( 0.5 );
-
- pOperator->DoMuzzleFlash();
-
- WeaponSound( SINGLE_NPC );
-
- // Make sure our laserdot is off
- m_bGuiding = false;
-
- if ( m_hLaserDot )
- {
- m_hLaserDot->TurnOff();
- }
- }
- break;
-
- default:
- BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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 )
-{
- // 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;
-
- QAngle vecAngles;
- VectorAngles( vForward, vecAngles );
- m_hMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
-
- m_hMissile->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 )
- {
- m_hMissile->SetGracePeriod( 0.3 );
- }
-
- DecrementAmmo( GetOwner() );
-
- // Register a muzzleflash for the AI
- pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
-
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
- WeaponSound( SINGLE );
-
- pOwner->RumbleEffect( RUMBLE_SHOTGUN_SINGLE, 0, RUMBLE_FLAG_RESTART );
-
- m_iPrimaryAttacks++;
- gamestats->Event_WeaponFired( pOwner, true, GetClassname() );
-
- CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON );
-
- // Check to see if we should trigger any RPG firing triggers
- int iCount = g_hWeaponFireTriggers.Count();
- for ( int i = 0; i < iCount; i++ )
- {
- if ( g_hWeaponFireTriggers[i]->IsTouching( pOwner ) )
- {
- if ( FClassnameIs( g_hWeaponFireTriggers[i], "trigger_rpgfire" ) )
- {
- g_hWeaponFireTriggers[i]->ActivateMultiTrigger( pOwner );
- }
- }
- }
-
- if( hl2_episodic.GetBool() )
- {
- CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
- int nAIs = g_AI_Manager.NumAIs();
-
- string_t iszStriderClassname = AllocPooledString( "npc_strider" );
-
- for ( int i = 0; i < nAIs; i++ )
- {
- if( ppAIs[ i ]->m_iClassname == iszStriderClassname )
- {
- ppAIs[ i ]->DispatchInteraction( g_interactionPlayerLaunchedRPG, NULL, m_hMissile );
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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;
-
- if ( m_hLaserDot == NULL )
- {
- StartGuiding();
-
- //STILL!?
- if ( m_hLaserDot == NULL )
- return;
- }
-
- if ( state )
- {
- m_hLaserDot->TurnOff();
- }
- else
- {
- m_hLaserDot->TurnOn();
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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 || GetIdealActivity() == ACT_VM_RELOAD )
- {
- SuppressGuiding();
- }
- else
- {
- SuppressGuiding( false );
- }
-
- //Player has toggled guidance state
- //Adrian: Players are not allowed to remove the laser guide in single player anymore, bye!
- if ( g_pGameRules->IsMultiplayer() == true )
- {
- if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
- {
- ToggleGuiding();
- }
- }
-
- //Move the laser
- UpdateLaserPosition();
- UpdateLaserEffects();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Vector
-//-----------------------------------------------------------------------------
-Vector CWeaponRPG::GetLaserPosition( void )
-{
- 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);
- return vec3_origin;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: NPC RPG users cheat and directly set the laser pointer's origin
-// Input : &vecTarget -
-//-----------------------------------------------------------------------------
-void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget )
-{
- CreateLaserPointer();
- // Turn the laserdot on
- m_bGuiding = true;
- m_hLaserDot->TurnOn();
-
- Vector muzzlePoint = GetOwner()->Weapon_ShootPosition();
- Vector vecDir = (vecTarget - muzzlePoint);
- VectorNormalize( vecDir );
- vecDir = muzzlePoint + ( vecDir * MAX_TRACE_LENGTH );
- UpdateLaserPosition( muzzlePoint, vecDir );
-
- SetNPCLaserPosition( vecTarget );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget )
-{
- m_vecNPCLaserDot = vecTarget;
- //NDebugOverlay::Box( m_vecNPCLaserDot, -Vector(10,10,10), Vector(10,10,10), 255,0,0, 8, 3 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-const Vector &CWeaponRPG::GetNPCLaserPosition( void )
-{
- return m_vecNPCLaserDot;
-}
-
-//-----------------------------------------------------------------------------
-// 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();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- //Can't have an active missile out
- if ( m_hMissile != NULL )
- return false;
-
- 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;
-
- WeaponSound(SPECIAL1);
-
- CreateLaserPointer();
- StartLaserEffects();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Turn off the guiding laser
-//-----------------------------------------------------------------------------
-void CWeaponRPG::StopGuiding( void )
-{
- m_bGuiding = false;
-
- WeaponSound( SPECIAL2 );
-
- StopLaserEffects();
-
- // Kill the dot completely
- if ( m_hLaserDot != NULL )
- {
- m_hLaserDot->TurnOff();
- UTIL_Remove( m_hLaserDot );
- m_hLaserDot = NULL;
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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 )
-{
- if ( vecMuzzlePos == vec3_origin || vecEndPos == vec3_origin )
- {
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- if ( !pPlayer )
- return;
-
- vecMuzzlePos = pPlayer->Weapon_ShootPosition();
- Vector forward;
-
- if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
- {
- forward = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
- }
- else
- {
- pPlayer->EyeVectors( &forward );
- }
-
- vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
- }
-
- //Move the laser dot, if active
- trace_t tr;
-
- // Trace out for the endpoint
-#ifdef PORTAL
- g_bBulletPortalTrace = true;
- Ray_t rayLaser;
- rayLaser.Init( vecMuzzlePos, vecEndPos );
- UTIL_Portal_TraceRay( rayLaser, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr );
- g_bBulletPortalTrace = false;
-#else
- UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr );
-#endif
-
- // 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 );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::CreateLaserPointer( void )
-{
- if ( m_hLaserDot != NULL )
- return;
-
- m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwnerEntity() );
- m_hLaserDot->TurnOff();
-
- UpdateLaserPosition();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::NotifyRocketDied( void )
-{
- m_hMissile = NULL;
-
- 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;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
-{
- bool bResult = BaseClass::WeaponLOSCondition( ownerPos, targetPos, bSetConditions );
-
- if( bResult )
- {
- CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer();
-
- if( npcOwner )
- {
- trace_t tr;
-
- Vector vecRelativeShootPosition;
- VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition );
- Vector vecMuzzle = ownerPos + vecRelativeShootPosition;
- Vector vecShootDir = npcOwner->GetActualShootTrajectory( vecMuzzle );
-
- // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out.
- AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
-
- if( tr.fraction != 1.0f )
- bResult = false;
- }
- }
-
- return bResult;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flDot -
-// flDist -
-// Output : int
-//-----------------------------------------------------------------------------
-int CWeaponRPG::WeaponRangeAttack1Condition( float flDot, float flDist )
-{
- if ( m_hMissile != NULL )
- return 0;
-
- // Ignore vertical distance when doing our RPG distance calculations
- CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
- if ( pNPC )
- {
- CBaseEntity *pEnemy = pNPC->GetEnemy();
- Vector vecToTarget = (pEnemy->GetAbsOrigin() - pNPC->GetAbsOrigin());
- vecToTarget.z = 0;
- flDist = vecToTarget.Length();
- }
-
- if ( flDist < MIN( m_fMinRange1, m_fMinRange2 ) )
- return COND_TOO_CLOSE_TO_ATTACK;
-
- if ( m_flNextPrimaryAttack > gpGlobals->curtime )
- return 0;
-
- // See if there's anyone in the way!
- CAI_BaseNPC *pOwner = GetOwner()->MyNPCPointer();
- ASSERT( pOwner != NULL );
-
- if( pOwner )
- {
- // Make sure I don't shoot the world!
- trace_t tr;
-
- Vector vecMuzzle = pOwner->Weapon_ShootPosition();
- Vector vecShootDir = pOwner->GetActualShootTrajectory( vecMuzzle );
-
- // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out.
- AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
-
- if( tr.fraction != 1.0 )
- {
- return COND_WEAPON_SIGHT_OCCLUDED;
- }
- }
-
- return COND_CAN_RANGE_ATTACK1;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Start the effects on the viewmodel of the RPG
-//-----------------------------------------------------------------------------
-void CWeaponRPG::StartLaserEffects( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if ( pOwner == NULL )
- return;
-
- CBaseViewModel *pBeamEnt = static_cast<CBaseViewModel *>(pOwner->GetViewModel());
-
- if ( m_hLaserBeam == NULL )
- {
- m_hLaserBeam = CBeam::BeamCreate( RPG_BEAM_SPRITE, 1.0f );
-
- if ( m_hLaserBeam == NULL )
- {
- // We were unable to create the beam
- Assert(0);
- return;
- }
-
- m_hLaserBeam->EntsInit( pBeamEnt, pBeamEnt );
-
- int startAttachment = LookupAttachment( "laser" );
- int endAttachment = LookupAttachment( "laser_end" );
-
- m_hLaserBeam->FollowEntity( pBeamEnt );
- m_hLaserBeam->SetStartAttachment( startAttachment );
- m_hLaserBeam->SetEndAttachment( endAttachment );
- m_hLaserBeam->SetNoise( 0 );
- m_hLaserBeam->SetColor( 255, 0, 0 );
- m_hLaserBeam->SetScrollRate( 0 );
- m_hLaserBeam->SetWidth( 0.5f );
- m_hLaserBeam->SetEndWidth( 0.5f );
- m_hLaserBeam->SetBrightness( 128 );
- m_hLaserBeam->SetBeamFlags( SF_BEAM_SHADEIN );
-#ifdef PORTAL
- m_hLaserBeam->m_bDrawInMainRender = true;
- m_hLaserBeam->m_bDrawInPortalRender = false;
-#endif
- }
- else
- {
- m_hLaserBeam->SetBrightness( 128 );
- }
-
- if ( m_hLaserMuzzleSprite == NULL )
- {
- m_hLaserMuzzleSprite = CSprite::SpriteCreate( RPG_LASER_SPRITE, GetAbsOrigin(), false );
-
- if ( m_hLaserMuzzleSprite == NULL )
- {
- // We were unable to create the sprite
- Assert(0);
- return;
- }
-
-#ifdef PORTAL
- m_hLaserMuzzleSprite->m_bDrawInMainRender = true;
- m_hLaserMuzzleSprite->m_bDrawInPortalRender = false;
-#endif
-
- m_hLaserMuzzleSprite->SetAttachment( pOwner->GetViewModel(), LookupAttachment( "laser" ) );
- m_hLaserMuzzleSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
- m_hLaserMuzzleSprite->SetBrightness( 255, 0.5f );
- m_hLaserMuzzleSprite->SetScale( 0.25f, 0.5f );
- m_hLaserMuzzleSprite->TurnOn();
- }
- else
- {
- m_hLaserMuzzleSprite->TurnOn();
- m_hLaserMuzzleSprite->SetScale( 0.25f, 0.25f );
- m_hLaserMuzzleSprite->SetBrightness( 255 );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Stop the effects on the viewmodel of the RPG
-//-----------------------------------------------------------------------------
-void CWeaponRPG::StopLaserEffects( void )
-{
- if ( m_hLaserBeam != NULL )
- {
- m_hLaserBeam->SetBrightness( 0 );
- }
-
- if ( m_hLaserMuzzleSprite != NULL )
- {
- m_hLaserMuzzleSprite->SetScale( 0.01f );
- m_hLaserMuzzleSprite->SetBrightness( 0, 0.5f );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Pulse all the effects to make them more... well, laser-like
-//-----------------------------------------------------------------------------
-void CWeaponRPG::UpdateLaserEffects( void )
-{
- if ( !m_bGuiding )
- return;
-
- if ( m_hLaserBeam != NULL )
- {
- m_hLaserBeam->SetBrightness( 128 + random->RandomInt( -8, 8 ) );
- }
-
- if ( m_hLaserMuzzleSprite != NULL )
- {
- m_hLaserMuzzleSprite->SetScale( 0.1f + random->RandomFloat( -0.025f, 0.025f ) );
- }
-}
-
-//=============================================================================
-// 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
- DEFINE_THINKFUNC( LaserThink ),
-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;
- g_LaserDotList.Insert( this );
-}
-
-CLaserDot::~CLaserDot( void )
-{
- g_LaserDotList.Remove( this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &origin -
-// Output : CLaserDot
-//-----------------------------------------------------------------------------
-CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
-{
- 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, vec3_origin, vec3_origin );
-
- //Create the graphic
- pLaserDot->SpriteInit( "sprites/redglow1.vmt", origin );
-
- pLaserDot->SetName( AllocPooledString("TEST") );
-
- pLaserDot->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
- pLaserDot->SetScale( 0.5f );
-
- pLaserDot->SetOwnerEntity( pOwner );
-
- pLaserDot->SetContextThink( &CLaserDot::LaserThink, gpGlobals->curtime + 0.1f, g_pLaserDotThink );
- pLaserDot->SetSimulatedEveryTick( true );
-
- if ( !bVisibleDot )
- {
- pLaserDot->MakeInvisible();
- }
-
- return pLaserDot;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CLaserDot::LaserThink( void )
-{
- SetNextThink( gpGlobals->curtime + 0.05f, g_pLaserDotThink );
-
- if ( GetOwnerEntity() == NULL )
- return;
-
- Vector viewDir = GetAbsOrigin() - GetOwnerEntity()->GetAbsOrigin();
- float dist = VectorNormalize( viewDir );
-
- float scale = RemapVal( dist, 32, 1024, 0.01f, 0.5f );
- float scaleOffs = random->RandomFloat( -scale * 0.25f, scale * 0.25f );
-
- scale = clamp( scale + scaleOffs, 0.1f, 32.0f );
-
- SetScale( scale );
-}
-
-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 )
-{
- BaseClass::TurnOff();
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "basehlcombatweapon.h"
+#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 "weapon_rpg.h"
+#include "shake.h"
+#include "ai_basenpc.h"
+#include "ai_squad.h"
+#include "te_effect_dispatch.h"
+#include "triggers.h"
+#include "smoke_trail.h"
+#include "collisionutils.h"
+#include "hl2_shareddefs.h"
+#include "rumble_shared.h"
+#include "gamestats.h"
+
+#ifdef PORTAL
+ #include "portal_util_shared.h"
+#endif
+
+#ifdef HL2_DLL
+ extern int g_interactionPlayerLaunchedRPG;
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define RPG_SPEED 1500
+
+static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15");
+ConVar rpg_missle_use_custom_detonators( "rpg_missle_use_custom_detonators", "1" );
+
+#define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat()
+
+const char *g_pLaserDotThink = "LaserThinkContext";
+
+//-----------------------------------------------------------------------------
+// Laser Dot
+//-----------------------------------------------------------------------------
+class CLaserDot : public CSprite
+{
+ DECLARE_CLASS( CLaserDot, CSprite );
+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 );
+
+ void LaserThink( void );
+
+ int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
+
+ void MakeInvisible( void );
+
+protected:
+ Vector m_vecSurfaceNormal;
+ EHANDLE m_hTargetEnt;
+ bool m_bVisibleLaserDot;
+ bool m_bIsOn;
+
+ DECLARE_DATADESC();
+public:
+ CLaserDot *m_pNext;
+};
+
+// 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 ),
+ DEFINE_FIELD( m_bCreateDangerSounds, FIELD_BOOLEAN ),
+
+ // 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;
+ m_bCreateDangerSounds = false;
+}
+
+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 );
+ SetDamage( 200.0f );
+
+ 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(), CMissile::EXPLOSION_RADIUS,
+ 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 )
+ {
+ // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
+ if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) )
+ 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() );
+
+ if ( pPlayer )
+ {
+ color32 white = { 255,225,205,64 };
+ UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN );
+
+ pPlayer->RumbleEffect( RUMBLE_RPG_MISSILE, 0, RUMBLE_FLAG_RESTART );
+ }
+ }
+
+ 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( hl2_episodic.GetBool() )
+ {
+ if( flBestDist <= ( GetAbsVelocity().Length() * 2.5f ) && FVisible( pBestDot->GetAbsOrigin() ) )
+ {
+ // Scare targets
+ CSoundEnt::InsertSound( SOUND_DANGER, pBestDot->GetAbsOrigin(), CMissile::EXPLOSION_RADIUS, 0.2f, pBestDot, SOUNDENT_CHANNEL_REPEATED_DANGER, NULL );
+ }
+ }
+
+ if ( rpg_missle_use_custom_detonators.GetBool() )
+ {
+ for ( int i = gm_CustomDetonators.Count() - 1; i >=0; --i )
+ {
+ CustomDetonator_t &detonator = gm_CustomDetonators[i];
+ if ( !detonator.hEntity )
+ {
+ gm_CustomDetonators.FastRemove( i );
+ }
+ else
+ {
+ const Vector &vPos = detonator.hEntity->CollisionProp()->WorldSpaceCenter();
+ if ( detonator.halfHeight > 0 )
+ {
+ if ( fabsf( vPos.z - GetAbsOrigin().z ) < detonator.halfHeight )
+ {
+ if ( ( GetAbsOrigin().AsVector2D() - vPos.AsVector2D() ).LengthSqr() < detonator.radiusSq )
+ {
+ Explode();
+ return;
+ }
+ }
+ }
+ else
+ {
+ if ( ( GetAbsOrigin() - vPos ).LengthSqr() < detonator.radiusSq )
+ {
+ Explode();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ //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 );
+
+ if( pLaserDot->GetTargetEntity() != NULL && flDist <= 240.0f && hl2_episodic.GetBool() )
+ {
+ // Prevent the missile circling the Strider like a Halo in ep1_c17_06. If the missile gets within 20
+ // feet of a Strider, tighten up the turn speed of the missile so it can break the halo and strike. (sjb 4/27/2006)
+ if( pLaserDot->GetTargetEntity()->ClassMatches( "npc_strider" ) )
+ {
+ flHomingSpeed *= 1.75f;
+ }
+ }
+
+ 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 );
+
+#ifdef HL2_EPISODIC
+
+ if ( m_bCreateDangerSounds == true )
+ {
+ trace_t tr;
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ CSoundEnt::InsertSound( SOUND_DANGER, tr.endpos, 100, 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// 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;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CUtlVector<CMissile::CustomDetonator_t> CMissile::gm_CustomDetonators;
+
+void CMissile::AddCustomDetonator( CBaseEntity *pEntity, float radius, float height )
+{
+ int i = gm_CustomDetonators.AddToTail();
+ gm_CustomDetonators[i].hEntity = pEntity;
+ gm_CustomDetonators[i].radiusSq = Square( radius );
+ gm_CustomDetonators[i].halfHeight = height * 0.5f;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CMissile::RemoveCustomDetonator( CBaseEntity *pEntity )
+{
+ for ( int i = 0; i < gm_CustomDetonators.Count(); i++ )
+ {
+ if ( gm_CustomDetonators[i].hEntity == pEntity )
+ {
+ gm_CustomDetonators.FastRemove( i );
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// 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_THINKFUNC( APCSeekThink ),
+
+ 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;
+ CreateDangerSounds( true );
+
+
+ if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
+ {
+ AddFlag( FL_AIMTARGET );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// 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::APCSeekThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+void CAPCMissile::APCSeekThink( void )
+{
+ BaseClass::SeekThink();
+
+ bool bFoundDot = false;
+
+ //If we can't find a dot to follow around then just send me wherever I'm facing so I can blow up in peace.
+ for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
+ {
+ if ( !pEnt->IsOn() )
+ continue;
+
+ if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
+ continue;
+
+ bFoundDot = true;
+ }
+
+ if ( bFoundDot == false )
+ {
+ Vector vDir = GetAbsVelocity();
+ VectorNormalize ( vDir );
+
+ SetAbsVelocity( vDir * 800 );
+
+ SetThink( NULL );
+ }
+}
+
+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
+ {
+#ifdef HL2_EPISODIC
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, APC_MISSILE_DAMAGE, 100, true, 20000 );
+#else
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), APC_MISSILE_DAMAGE, 100, true, 20000 );
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// 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 );
+}
+
+#define RPG_BEAM_SPRITE "effects/laser1_noz.vmt"
+#define RPG_LASER_SPRITE "sprites/redglow1.vmt"
+
+//=============================================================================
+// RPG
+//=============================================================================
+
+BEGIN_DATADESC( CWeaponRPG )
+
+ DEFINE_FIELD( m_bInitialStateUpdate,FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bGuiding, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_vecNPCLaserDot, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hMissile, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hLaserMuzzleSprite, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hLaserBeam, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bHideGuiding, FIELD_BOOLEAN ),
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST(CWeaponRPG, DT_WeaponRPG)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
+PRECACHE_WEAPON_REGISTER(weapon_rpg);
+
+acttable_t CWeaponRPG::m_acttable[] =
+{
+ { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true },
+
+ { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true },
+ { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true },
+ { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, true },
+
+ { ACT_IDLE, ACT_IDLE_RPG, true },
+ { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_RPG, true },
+ { ACT_WALK, ACT_WALK_RPG, true },
+ { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RPG, true },
+ { ACT_RUN, ACT_RUN_RPG, true },
+ { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true },
+ { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponRPG);
+
+//-----------------------------------------------------------------------------
+// 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()
+{
+ if ( m_hLaserDot != NULL )
+ {
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+
+ if ( m_hLaserMuzzleSprite )
+ {
+ UTIL_Remove( m_hLaserMuzzleSprite );
+ }
+
+ if ( m_hLaserBeam )
+ {
+ UTIL_Remove( m_hLaserBeam );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 );
+
+ UTIL_PrecacheOther( "rpg_missile" );
+}
+
+
+//-----------------------------------------------------------------------------
+// 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:
+// Input : *pEvent -
+// *pOperator -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
+{
+ switch( pEvent->event )
+ {
+ case EVENT_WEAPON_SMG1:
+ {
+ if ( m_hMissile != NULL )
+ return;
+
+ Vector muzzlePoint;
+ QAngle vecAngles;
+
+ muzzlePoint = GetOwner()->Weapon_ShootPosition();
+
+ CAI_BaseNPC *npc = pOperator->MyNPCPointer();
+ ASSERT( npc != NULL );
+
+ Vector vecShootDir = npc->GetActualShootTrajectory( muzzlePoint );
+
+ // look for a better launch location
+ Vector altLaunchPoint;
+ if (GetAttachment( "missile", altLaunchPoint ))
+ {
+ // check to see if it's relativly free
+ trace_t tr;
+ AI_TraceHull( altLaunchPoint, altLaunchPoint + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
+
+ if( tr.fraction == 1.0)
+ {
+ muzzlePoint = altLaunchPoint;
+ }
+ }
+
+ VectorAngles( vecShootDir, vecAngles );
+
+ m_hMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
+ m_hMissile->m_hOwner = this;
+
+ // NPCs always get a grace period
+ m_hMissile->SetGracePeriod( 0.5 );
+
+ pOperator->DoMuzzleFlash();
+
+ WeaponSound( SINGLE_NPC );
+
+ // Make sure our laserdot is off
+ m_bGuiding = false;
+
+ if ( m_hLaserDot )
+ {
+ m_hLaserDot->TurnOff();
+ }
+ }
+ break;
+
+ default:
+ BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 )
+{
+ // 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;
+
+ QAngle vecAngles;
+ VectorAngles( vForward, vecAngles );
+ m_hMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
+
+ m_hMissile->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 )
+ {
+ m_hMissile->SetGracePeriod( 0.3 );
+ }
+
+ DecrementAmmo( GetOwner() );
+
+ // Register a muzzleflash for the AI
+ pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ WeaponSound( SINGLE );
+
+ pOwner->RumbleEffect( RUMBLE_SHOTGUN_SINGLE, 0, RUMBLE_FLAG_RESTART );
+
+ m_iPrimaryAttacks++;
+ gamestats->Event_WeaponFired( pOwner, true, GetClassname() );
+
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON );
+
+ // Check to see if we should trigger any RPG firing triggers
+ int iCount = g_hWeaponFireTriggers.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ if ( g_hWeaponFireTriggers[i]->IsTouching( pOwner ) )
+ {
+ if ( FClassnameIs( g_hWeaponFireTriggers[i], "trigger_rpgfire" ) )
+ {
+ g_hWeaponFireTriggers[i]->ActivateMultiTrigger( pOwner );
+ }
+ }
+ }
+
+ if( hl2_episodic.GetBool() )
+ {
+ CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
+ int nAIs = g_AI_Manager.NumAIs();
+
+ string_t iszStriderClassname = AllocPooledString( "npc_strider" );
+
+ for ( int i = 0; i < nAIs; i++ )
+ {
+ if( ppAIs[ i ]->m_iClassname == iszStriderClassname )
+ {
+ ppAIs[ i ]->DispatchInteraction( g_interactionPlayerLaunchedRPG, NULL, m_hMissile );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+
+ if ( m_hLaserDot == NULL )
+ {
+ StartGuiding();
+
+ //STILL!?
+ if ( m_hLaserDot == NULL )
+ return;
+ }
+
+ if ( state )
+ {
+ m_hLaserDot->TurnOff();
+ }
+ else
+ {
+ m_hLaserDot->TurnOn();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 || GetIdealActivity() == ACT_VM_RELOAD )
+ {
+ SuppressGuiding();
+ }
+ else
+ {
+ SuppressGuiding( false );
+ }
+
+ //Player has toggled guidance state
+ //Adrian: Players are not allowed to remove the laser guide in single player anymore, bye!
+ if ( g_pGameRules->IsMultiplayer() == true )
+ {
+ if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
+ {
+ ToggleGuiding();
+ }
+ }
+
+ //Move the laser
+ UpdateLaserPosition();
+ UpdateLaserEffects();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Vector
+//-----------------------------------------------------------------------------
+Vector CWeaponRPG::GetLaserPosition( void )
+{
+ 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);
+ return vec3_origin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: NPC RPG users cheat and directly set the laser pointer's origin
+// Input : &vecTarget -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget )
+{
+ CreateLaserPointer();
+ // Turn the laserdot on
+ m_bGuiding = true;
+ m_hLaserDot->TurnOn();
+
+ Vector muzzlePoint = GetOwner()->Weapon_ShootPosition();
+ Vector vecDir = (vecTarget - muzzlePoint);
+ VectorNormalize( vecDir );
+ vecDir = muzzlePoint + ( vecDir * MAX_TRACE_LENGTH );
+ UpdateLaserPosition( muzzlePoint, vecDir );
+
+ SetNPCLaserPosition( vecTarget );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget )
+{
+ m_vecNPCLaserDot = vecTarget;
+ //NDebugOverlay::Box( m_vecNPCLaserDot, -Vector(10,10,10), Vector(10,10,10), 255,0,0, 8, 3 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CWeaponRPG::GetNPCLaserPosition( void )
+{
+ return m_vecNPCLaserDot;
+}
+
+//-----------------------------------------------------------------------------
+// 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();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ //Can't have an active missile out
+ if ( m_hMissile != NULL )
+ return false;
+
+ 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;
+
+ WeaponSound(SPECIAL1);
+
+ CreateLaserPointer();
+ StartLaserEffects();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn off the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StopGuiding( void )
+{
+ m_bGuiding = false;
+
+ WeaponSound( SPECIAL2 );
+
+ StopLaserEffects();
+
+ // Kill the dot completely
+ if ( m_hLaserDot != NULL )
+ {
+ m_hLaserDot->TurnOff();
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 )
+{
+ if ( vecMuzzlePos == vec3_origin || vecEndPos == vec3_origin )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ vecMuzzlePos = pPlayer->Weapon_ShootPosition();
+ Vector forward;
+
+ if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
+ {
+ forward = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
+ }
+ else
+ {
+ pPlayer->EyeVectors( &forward );
+ }
+
+ vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
+ }
+
+ //Move the laser dot, if active
+ trace_t tr;
+
+ // Trace out for the endpoint
+#ifdef PORTAL
+ g_bBulletPortalTrace = true;
+ Ray_t rayLaser;
+ rayLaser.Init( vecMuzzlePos, vecEndPos );
+ UTIL_Portal_TraceRay( rayLaser, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr );
+ g_bBulletPortalTrace = false;
+#else
+ UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr );
+#endif
+
+ // 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 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::CreateLaserPointer( void )
+{
+ if ( m_hLaserDot != NULL )
+ return;
+
+ m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwnerEntity() );
+ m_hLaserDot->TurnOff();
+
+ UpdateLaserPosition();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::NotifyRocketDied( void )
+{
+ m_hMissile = NULL;
+
+ 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;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
+{
+ bool bResult = BaseClass::WeaponLOSCondition( ownerPos, targetPos, bSetConditions );
+
+ if( bResult )
+ {
+ CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer();
+
+ if( npcOwner )
+ {
+ trace_t tr;
+
+ Vector vecRelativeShootPosition;
+ VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition );
+ Vector vecMuzzle = ownerPos + vecRelativeShootPosition;
+ Vector vecShootDir = npcOwner->GetActualShootTrajectory( vecMuzzle );
+
+ // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out.
+ AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
+
+ if( tr.fraction != 1.0f )
+ bResult = false;
+ }
+ }
+
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flDot -
+// flDist -
+// Output : int
+//-----------------------------------------------------------------------------
+int CWeaponRPG::WeaponRangeAttack1Condition( float flDot, float flDist )
+{
+ if ( m_hMissile != NULL )
+ return 0;
+
+ // Ignore vertical distance when doing our RPG distance calculations
+ CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
+ if ( pNPC )
+ {
+ CBaseEntity *pEnemy = pNPC->GetEnemy();
+ Vector vecToTarget = (pEnemy->GetAbsOrigin() - pNPC->GetAbsOrigin());
+ vecToTarget.z = 0;
+ flDist = vecToTarget.Length();
+ }
+
+ if ( flDist < MIN( m_fMinRange1, m_fMinRange2 ) )
+ return COND_TOO_CLOSE_TO_ATTACK;
+
+ if ( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return 0;
+
+ // See if there's anyone in the way!
+ CAI_BaseNPC *pOwner = GetOwner()->MyNPCPointer();
+ ASSERT( pOwner != NULL );
+
+ if( pOwner )
+ {
+ // Make sure I don't shoot the world!
+ trace_t tr;
+
+ Vector vecMuzzle = pOwner->Weapon_ShootPosition();
+ Vector vecShootDir = pOwner->GetActualShootTrajectory( vecMuzzle );
+
+ // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out.
+ AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr );
+
+ if( tr.fraction != 1.0 )
+ {
+ return COND_WEAPON_SIGHT_OCCLUDED;
+ }
+ }
+
+ return COND_CAN_RANGE_ATTACK1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start the effects on the viewmodel of the RPG
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StartLaserEffects( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner == NULL )
+ return;
+
+ CBaseViewModel *pBeamEnt = static_cast<CBaseViewModel *>(pOwner->GetViewModel());
+
+ if ( m_hLaserBeam == NULL )
+ {
+ m_hLaserBeam = CBeam::BeamCreate( RPG_BEAM_SPRITE, 1.0f );
+
+ if ( m_hLaserBeam == NULL )
+ {
+ // We were unable to create the beam
+ Assert(0);
+ return;
+ }
+
+ m_hLaserBeam->EntsInit( pBeamEnt, pBeamEnt );
+
+ int startAttachment = LookupAttachment( "laser" );
+ int endAttachment = LookupAttachment( "laser_end" );
+
+ m_hLaserBeam->FollowEntity( pBeamEnt );
+ m_hLaserBeam->SetStartAttachment( startAttachment );
+ m_hLaserBeam->SetEndAttachment( endAttachment );
+ m_hLaserBeam->SetNoise( 0 );
+ m_hLaserBeam->SetColor( 255, 0, 0 );
+ m_hLaserBeam->SetScrollRate( 0 );
+ m_hLaserBeam->SetWidth( 0.5f );
+ m_hLaserBeam->SetEndWidth( 0.5f );
+ m_hLaserBeam->SetBrightness( 128 );
+ m_hLaserBeam->SetBeamFlags( SF_BEAM_SHADEIN );
+#ifdef PORTAL
+ m_hLaserBeam->m_bDrawInMainRender = true;
+ m_hLaserBeam->m_bDrawInPortalRender = false;
+#endif
+ }
+ else
+ {
+ m_hLaserBeam->SetBrightness( 128 );
+ }
+
+ if ( m_hLaserMuzzleSprite == NULL )
+ {
+ m_hLaserMuzzleSprite = CSprite::SpriteCreate( RPG_LASER_SPRITE, GetAbsOrigin(), false );
+
+ if ( m_hLaserMuzzleSprite == NULL )
+ {
+ // We were unable to create the sprite
+ Assert(0);
+ return;
+ }
+
+#ifdef PORTAL
+ m_hLaserMuzzleSprite->m_bDrawInMainRender = true;
+ m_hLaserMuzzleSprite->m_bDrawInPortalRender = false;
+#endif
+
+ m_hLaserMuzzleSprite->SetAttachment( pOwner->GetViewModel(), LookupAttachment( "laser" ) );
+ m_hLaserMuzzleSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
+ m_hLaserMuzzleSprite->SetBrightness( 255, 0.5f );
+ m_hLaserMuzzleSprite->SetScale( 0.25f, 0.5f );
+ m_hLaserMuzzleSprite->TurnOn();
+ }
+ else
+ {
+ m_hLaserMuzzleSprite->TurnOn();
+ m_hLaserMuzzleSprite->SetScale( 0.25f, 0.25f );
+ m_hLaserMuzzleSprite->SetBrightness( 255 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop the effects on the viewmodel of the RPG
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StopLaserEffects( void )
+{
+ if ( m_hLaserBeam != NULL )
+ {
+ m_hLaserBeam->SetBrightness( 0 );
+ }
+
+ if ( m_hLaserMuzzleSprite != NULL )
+ {
+ m_hLaserMuzzleSprite->SetScale( 0.01f );
+ m_hLaserMuzzleSprite->SetBrightness( 0, 0.5f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pulse all the effects to make them more... well, laser-like
+//-----------------------------------------------------------------------------
+void CWeaponRPG::UpdateLaserEffects( void )
+{
+ if ( !m_bGuiding )
+ return;
+
+ if ( m_hLaserBeam != NULL )
+ {
+ m_hLaserBeam->SetBrightness( 128 + random->RandomInt( -8, 8 ) );
+ }
+
+ if ( m_hLaserMuzzleSprite != NULL )
+ {
+ m_hLaserMuzzleSprite->SetScale( 0.1f + random->RandomFloat( -0.025f, 0.025f ) );
+ }
+}
+
+//=============================================================================
+// 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
+ DEFINE_THINKFUNC( LaserThink ),
+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;
+ g_LaserDotList.Insert( this );
+}
+
+CLaserDot::~CLaserDot( void )
+{
+ g_LaserDotList.Remove( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &origin -
+// Output : CLaserDot
+//-----------------------------------------------------------------------------
+CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
+{
+ 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, vec3_origin, vec3_origin );
+
+ //Create the graphic
+ pLaserDot->SpriteInit( "sprites/redglow1.vmt", origin );
+
+ pLaserDot->SetName( AllocPooledString("TEST") );
+
+ pLaserDot->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
+ pLaserDot->SetScale( 0.5f );
+
+ pLaserDot->SetOwnerEntity( pOwner );
+
+ pLaserDot->SetContextThink( &CLaserDot::LaserThink, gpGlobals->curtime + 0.1f, g_pLaserDotThink );
+ pLaserDot->SetSimulatedEveryTick( true );
+
+ if ( !bVisibleDot )
+ {
+ pLaserDot->MakeInvisible();
+ }
+
+ return pLaserDot;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLaserDot::LaserThink( void )
+{
+ SetNextThink( gpGlobals->curtime + 0.05f, g_pLaserDotThink );
+
+ if ( GetOwnerEntity() == NULL )
+ return;
+
+ Vector viewDir = GetAbsOrigin() - GetOwnerEntity()->GetAbsOrigin();
+ float dist = VectorNormalize( viewDir );
+
+ float scale = RemapVal( dist, 32, 1024, 0.01f, 0.5f );
+ float scaleOffs = random->RandomFloat( -scale * 0.25f, scale * 0.25f );
+
+ scale = clamp( scale + scaleOffs, 0.1f, 32.0f );
+
+ SetScale( scale );
+}
+
+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 )
+{
+ BaseClass::TurnOff();
+}