summaryrefslogtreecommitdiff
path: root/game/shared/tf2/weapon_laserdesignator.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf2/weapon_laserdesignator.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf2/weapon_laserdesignator.cpp')
-rw-r--r--game/shared/tf2/weapon_laserdesignator.cpp394
1 files changed, 394 insertions, 0 deletions
diff --git a/game/shared/tf2/weapon_laserdesignator.cpp b/game/shared/tf2/weapon_laserdesignator.cpp
new file mode 100644
index 0000000..4f9ef46
--- /dev/null
+++ b/game/shared/tf2/weapon_laserdesignator.cpp
@@ -0,0 +1,394 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "NPCEvent.h"
+#include "tf_basecombatweapon.h"
+#include "smoke_trail.h"
+#include "tf_player.h"
+#include "in_buttons.h"
+#include "tf_gamerules.h"
+#include "ammodef.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "effects.h"
+#include "baseviewmodel.h"
+#include "basegrenade_shared.h"
+#include "grenade_limpetmine.h"
+#include "tf_obj_sentrygun.h"
+#include "tf_obj_aerial_sentry_station.h"
+
+
+// Damage CVars
+ConVar weapon_laserdesignator_range( "weapon_laserdesignator_range","1024", FCVAR_NONE, "Laser Designator maximum range" );
+
+// ------------------------------------------------------------------------ //
+// CWeaponLaserDesignator
+// ------------------------------------------------------------------------ //
+class CWeaponLaserDesignator : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponLaserDesignator, CBaseTFCombatWeapon );
+public:
+ virtual void Precache( void );
+ virtual float GetFireRate( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual bool ComputeEMPFireState( void );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+
+ bool ValidDesignationTarget( CBaseEntity *pEntity );
+ bool TargetIsInLock( CBaseTFPlayer *pPlayer, CBaseEntity *pTarget, float *flMaxDot );
+
+ // Designation
+ void StartDesignating( void );
+ void StopDesignating( void );
+ void UpdateBeam( void );
+ void DesignateSentriesToAttack( CBaseEntity *pEntity );
+
+public:
+ // Designation
+ bool m_bDesignating;
+
+ // Beam
+ CBeam *m_pBeam;
+};
+
+LINK_ENTITY_TO_CLASS( weapon_laserdesignator, CWeaponLaserDesignator );
+PRECACHE_WEAPON_REGISTER(weapon_laserdesignator);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::Precache( void )
+{
+ BaseClass::Precache();
+ PrecacheModel( "sprites/laserbeam.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponLaserDesignator::GetFireRate()
+{
+ return 0.2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop thinking and holster
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ StopDesignating();
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::ComputeEMPFireState( void )
+{
+ if (IsOwnerEMPed())
+ {
+ // FIXME: Need a sound
+ //UTIL_EmitSound( pPlayer->pev, CHAN_WEAPON, g_pszEMPGatlingFizzle, 1.0, ATTN_NORM );
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::ItemPostFrame( void )
+{
+ CBasePlayer *pPlayer = GetOwner();
+ if (pPlayer == NULL)
+ return;
+
+ // Are we already welding?
+ if ( m_bDesignating )
+ {
+ if ( pPlayer->m_nButtons & (IN_ATTACK | IN_ATTACK2) )
+ {
+ UpdateBeam();
+ }
+ else
+ {
+ StopDesignating();
+ }
+ }
+ else
+ {
+ if ( (pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ // Start designating
+ PrimaryAttack();
+ UpdateBeam();
+ }
+ }
+
+ // Always idle
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start designating with the laser
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::StartDesignating( void )
+{
+ m_bDesignating = true;
+
+ if ( !m_pBeam )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+ int iIndex = pPlayer->entindex();
+
+ m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 1 );
+ m_pBeam->PointEntInit( vec3_origin, iIndex );
+ m_pBeam->SetEndAttachment( 1 );
+ m_pBeam->SetColor( 255, 32, 32 );
+ m_pBeam->SetBrightness( 255 );
+ m_pBeam->SetNoise( 0 );
+ m_pBeam->SetWidth( 2 );
+ m_pBeam->SetEndWidth( 1 );
+ m_pBeam->SetStartEntity( ENTINDEX(m_hOwner->edict()) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop designating with the laser
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::StopDesignating( void )
+{
+ m_bDesignating = false;
+ if ( m_pBeam )
+ {
+ UTIL_Remove( m_pBeam );
+ m_pBeam = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ WeaponSound(SINGLE);
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+ pPlayer->AddEffects( EF_MUZZLEFLASH );
+
+ StartDesignating();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the beam position and do designator functions
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::UpdateBeam( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ ASSERT( m_pBeam );
+
+ // "Fire" the designator beam
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( pPlayer->GetOrigin() );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+ Vector vecEnd = vecSrc + vecAiming * weapon_laserdesignator_range.GetFloat();
+
+ trace_t tr;
+ TFGameRules()->WeaponTraceLine(vecSrc, vecEnd, MASK_SHOT, pPlayer, DMG_ENERGYBEAM, &tr);
+
+ // Update beam visual
+ m_pBeam->SetStartPos( tr.endpos );
+ m_pBeam->RelinkBeam();
+
+ // Perform designator functions
+ // Did we hit something?
+ CBaseEntity *pEntity = CBaseEntity::Instance( tr.u.ent );
+ if ( pEntity )
+ {
+ // TODO: We dynamic_cast the entity we choose twice. Fix it.
+
+ // If we hit a target we don't care about, try and grab the nearest valid target to our crosshair
+ if ( !ValidDesignationTarget( pEntity ) )
+ {
+ pEntity = NULL;
+
+ // Grab the entity nearest the crosshair
+ float bestdot = AUTOAIM_20DEGREES;
+ float flSize = weapon_laserdesignator_range.GetFloat() * 0.5;
+ Vector vecCenter = vecSrc + vecAiming * flSize;
+
+ // Find a target.
+ CBaseEntity *pList[100];
+ Vector delta( flSize, flSize, flSize );
+ int count = UTIL_EntitiesInBox( pList, 100, vecCenter - delta, vecCenter + delta, FL_CLIENT|FL_NPC|FL_OBJECT );
+ for ( int i = 0; i < count; i++ )
+ {
+ CBaseEntity *pTarget = pList[i];
+ if ( !ValidDesignationTarget(pTarget) )
+ continue;
+ if ( TargetIsInLock( pPlayer, pTarget, &bestdot ) == false )
+ continue;
+ if ( (pTarget->Center() - pPlayer->Center() ).Length() > weapon_laserdesignator_range.GetFloat() )
+ continue;
+
+ // Can shoot at this one
+ pEntity = pTarget;
+ }
+ }
+
+ // Couldn't find anything
+ if ( !pEntity )
+ return;
+
+ // If we hit a player, and it's an enemy, tell my sentryguns to attack it
+ if ( pEntity->IsPlayer() && !pPlayer->InSameTeam(pEntity) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ else if ( pEntity->GetFlags() & FL_NPC )
+ {
+ // If it's an enemy NPC, tell my sentryguns to attack it
+ if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ }
+ else
+ {
+ // Is it a sentrygun? If so, tell it to toggle it's sunken state.
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ {
+ CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun *>(pEntity);
+ if ( pSentry && pSentry->GetBuilder() == pPlayer )
+ {
+ pSentry->ToggleTurtle();
+ }
+ else
+ {
+ // If it's an enemy object, tell my sentryguns to attack it
+ if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ }
+ }
+
+ // Is it a limpet mine? If so, detonate it
+ CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
+ if ( pLimpet && pLimpet->IsLive() && pLimpet->m_hOwner->pev == pPlayer->pev )
+ {
+ pLimpet->Use( pPlayer, pPlayer, USE_ON, 0 );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified entity is a valid one for designation
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::ValidDesignationTarget( CBaseEntity *pEntity )
+{
+ // Ignore the world
+ if ( pEntity->entindex() == 0 )
+ return false;
+
+ // Ignore players on my team
+ if ( pEntity->IsPlayer() && pEntity->InSameTeam(m_hOwner) )
+ return false;
+
+ // Is it a sentrygun? If so, tell it to toggle it's sunken state.
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ return true;
+
+ // My limpet mines are valid
+ CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
+ if ( pLimpet && pLimpet->IsLive() && pLimpet->m_hOwner->pev == m_hOwner->pev )
+ return true;
+
+ // NPCs are valid
+ if ( pEntity->GetFlags() & FL_NPC )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the target entity's close enough to the player's crosshair to lock onto
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::TargetIsInLock( CBaseTFPlayer *pPlayer, CBaseEntity *pTarget, float *flMaxDot )
+{
+ Vector center = pTarget->Center();
+ Vector dir = center - pPlayer->Center();
+ float length = VectorNormalize( dir );
+ Vector forward, right, up;
+ pPlayer->EyeVectors( &forward, &right, &up );
+
+ // Make sure it's in front of the player
+ if ( DotProduct( dir, forward ) < 0.0f )
+ return false;
+
+ float dot = fabs( DotProduct(dir, right ) ) + fabs( DotProduct(dir, up ) );
+
+ // Tweak for distance
+ dot *= 1.0f + 4.0f * ( length / MAX_COORD_RANGE );
+
+ // Outside the dot?
+ if ( dot > *flMaxDot )
+ return false;
+
+ // Open LOS?
+ trace_t tr;
+ UTIL_TraceLine( pPlayer->Center(), center, MASK_SHOT, edict(), COLLISION_GROUP_NONE, &tr );
+ if ( ( tr.fraction != 1.0f ) && ( tr.u.ent != pTarget->edict() ) )
+ return false;
+
+ *flMaxDot = dot;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the player's sentryguns to all attack the specified target
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::DesignateSentriesToAttack( CBaseEntity *pEntity )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ // Tell all this player's sentryguns
+ for ( int i = 0; i < pPlayer->m_aObjects.Size(); i++ )
+ {
+ CBaseObject *pObj = pPlayer->m_aObjects[i];
+ if ( !pObj )
+ continue;
+
+ if ( pObj->IsSentrygun() )
+ {
+ CObjectSentrygun *pSentry = static_cast<CObjectSentrygun *>(pObj);
+ pSentry->DesignateTarget( pEntity );
+ }
+
+ if ( pObj->GetType() == OBJ_AERIAL_SENTRY_STATION )
+ {
+ CObjectAerialSentryStation *pSentryStation = static_cast<CObjectAerialSentryStation *>(pObj);
+ pSentryStation->DesignateTarget( pEntity );
+ }
+ }
+} \ No newline at end of file