mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user