diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/weapon_rpg.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-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.cpp | 4752 |
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(); +} |