Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

688 lines
20 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "basetfplayer_shared.h"
#include "in_buttons.h"
#include "tf_shareddefs.h"
#include "basegrenade_shared.h"
#include "basetfcombatweapon_shared.h"
#include "weapon_limpetmine.h"
#include "IEffects.h"
#include "engine/IEngineSound.h"
#include "weapon_grenade_rocket.h"
#include "beam_shared.h"
#include "tf_gamerules.h"
#if defined( CLIENT_DLL )
#else
#include "grenade_limpetmine.h"
#include "tf_obj_sentrygun.h"
#include "ai_network.h"
#include "tf_team.h"
#endif
// Damage CVars
ConVar weapon_laserdesignator_range( "weapon_laserdesignator_range","2048", FCVAR_REPLICATED, "Laser Designator maximum range" );
ConVar weapon_limpetmine_max_deployed( "weapon_limpetmine_max_deployed","0", FCVAR_REPLICATED, "Maximum number of Limpet Mines that can be deployed at a single time" );
ConVar weapon_limpetmine_max_distance_off_trace( "weapon_limpetmine_max_distance_off_trace","25", FCVAR_REPLICATED, "Maximum distance off of the trace that a limpet can be." );
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponLimpetmine, DT_WeaponLimpetmine )
BEGIN_NETWORK_TABLE( CWeaponLimpetmine, DT_WeaponLimpetmine )
#if !defined( CLIENT_DLL )
SendPropInt( SENDINFO( m_iDeployedLimpets ), 5, SPROP_UNSIGNED ),
SendPropEHandle( SENDINFO( m_hDesignatedEntity ) ),
#else
RecvPropInt( RECVINFO( m_iDeployedLimpets ) ),
RecvPropEHandle( RECVINFO(m_hDesignatedEntity) ),
#endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponLimpetmine )
DEFINE_PRED_FIELD( m_iDeployedLimpets, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_hDesignatedEntity, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( weapon_limpetmine, CWeaponLimpetmine );
PRECACHE_WEAPON_REGISTER(weapon_limpetmine);
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CWeaponLimpetmine::CWeaponLimpetmine( void )
{
SetPredictionEligible( true );
m_hDesignatedEntity = NULL;
m_flNextBuzzTime = 0;
m_iDeployedLimpets = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::Precache( void )
{
BaseClass::Precache();
#ifndef CLIENT_DLL
UTIL_PrecacheOther( "grenade_limpetmine" );
#endif
PrecacheScriptSound( "WeaponLimpetmine.Deny" );
PrecacheModel( "sprites/laserbeam.vmt" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::UpdateOnRemove( void )
{
#ifndef CLIENT_DLL
RemoveDeployedLimpets();
if ( m_hLaserDesignation )
{
UTIL_Remove( m_hLaserDesignation );
}
#endif
// Chain at end to mimic destructor unwind order
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::CleanupOnActStart( void )
{
#ifndef CLIENT_DLL
RemoveDeployedLimpets();
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CWeaponLimpetmine::GetFireRate( void )
{
return 0.5;
}
//-----------------------------------------------------------------------------
// Purpose: Limpetmine considers itself as having ammo at all times, because it contains the designator
//-----------------------------------------------------------------------------
bool CWeaponLimpetmine::HasAnyAmmo( void )
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Stop thinking and holster
//-----------------------------------------------------------------------------
bool CWeaponLimpetmine::Holster( CBaseCombatWeapon *pSwitchingTo )
{
StopDesignating();
return BaseClass::Holster(pSwitchingTo);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::ItemPostFrame( void )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
return;
#ifndef CLIENT_DLL
// If we don't have a laser designator yet, create one
if ( !m_hLaserDesignation )
{
m_hLaserDesignation = CEnvLaserDesignation::Create( pPlayer );
}
#endif
// Is the player trying to designate?
if ( pPlayer->m_nButtons & IN_ATTACK )
{
if ( m_bDesignating )
{
#ifndef CLIENT_DLL
ActivateBeam();
#endif
}
else if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
{
StartDesignating();
#ifndef CLIENT_DLL
ActivateBeam();
#endif
}
}
else if ( m_bDesignating )
{
StopDesignating();
}
else if ( (pPlayer->m_nButtons & IN_ATTACK2) && !m_flStartedLaunchingAt && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
{
m_flStartedLaunchingAt = gpGlobals->curtime;
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
else if ( (pPlayer->m_afButtonReleased & IN_ATTACK2) && (m_flNextPrimaryAttack <= gpGlobals->curtime) && m_flStartedLaunchingAt )
{
m_flNextPrimaryAttack = gpGlobals->curtime;
PrimaryAttack();
m_flStartedLaunchingAt = 0;
}
UpdateBeamTarget();
// Always idle
WeaponIdle();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::PrimaryAttack( void )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
if ( !pPlayer )
return;
// Don't allow anymore limpets to be placed if we've reached the max
if ( m_iDeployedLimpets >= weapon_limpetmine_max_deployed.GetInt() )
{
#ifdef CLIENT_DLL
// We should flash the limpet mine count on the weapon at this point
CLocalPlayerFilter filter;
EmitSound( filter, entindex(), "WeaponLimpetmine.Deny" );
m_flNextBuzzTime = gpGlobals->curtime + 0.5f; // only buzz every so often.
#endif
return;
}
if ( IsOwnerEMPed() )
return;
trace_t tr;
// Get an aim vector. Don't use GetAimVector() because we don't want autoaiming.
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
Vector vecAiming;
pPlayer->EyeVectors( &vecAiming );
// Calculate launch velocity (3 seconds for max distance)
float flThrowTime = MIN( (gpGlobals->curtime - m_flStartedLaunchingAt), 3.0 );
float flSpeed = 600 + (300 * flThrowTime);
vecAiming *= flSpeed;
WeaponSound( WPN_DOUBLE );
#ifndef CLIENT_DLL
ThrowLimpet( pPlayer, vecSrc, vecAiming );
#endif
SendWeaponAnim( ACT_VM_SECONDARYATTACK );
m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
m_flNextSecondaryAttack = gpGlobals->curtime + GetFireRate();
pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
}
#if !defined( CLIENT_DLL )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::ThrowLimpet( CBasePlayer *pPlayer, Vector vecSrc, Vector vecAiming )
{
CLimpetMine *pLimpet = CLimpetMine::Create(vecSrc, vecAiming, pPlayer);
pLimpet->SetLauncher( this );
// Increase the limpet count
m_iDeployedLimpets += 1;
}
//-----------------------------------------------------------------------------
// Purpose: Detonates all deployed limpets for this weapon
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::DetonateDeployedLimpets( void )
{
CBaseEntity *pEntity = NULL;
while ((pEntity = gEntList.FindEntityByClassname( pEntity, "grenade_limpetmine" )) != NULL)
{
CLimpetMine* pLimpet = (CLimpetMine*)pEntity;
if ( pLimpet->IsLive() && pLimpet->GetThrower() == GetOwner() )
{
pLimpet->Use( GetOwner(), this, USE_ON, 0 );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Quietly removes all deployed limpets for this weapon
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::RemoveDeployedLimpets( void )
{
CBaseEntity *pEntity = NULL;
while ((pEntity = gEntList.FindEntityByClassname( pEntity, "grenade_limpetmine" )) != NULL)
{
CLimpetMine* pLimpet = (CLimpetMine*)pEntity;
if ( pLimpet->GetThrower() == GetOwner() )
{
pLimpet->Use( GetOwner(), this, USE_SET, 0 );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::ActivateBeam( void )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
if ( !pPlayer )
return;
Assert( m_pBeam );
if( m_hDesignatedEntity != NULL )
{
CBaseEntity* pEntity = m_hDesignatedEntity.Get();
// 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();
return;
}
else
{
// If it's an enemy object, tell my sentryguns to attack it
if ( !pPlayer->InSameTeam( pEntity ) )
{
DesignateSentriesToAttack( pEntity );
return;
}
}
}
// Is it a limpet mine? If so, detonate it
CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
if ( pLimpet && pLimpet->IsLive() && pLimpet->GetThrower() == pPlayer )
{
pLimpet->Use( pPlayer, this, USE_ON, 0 );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the specified entity is a valid one for designation
//-----------------------------------------------------------------------------
bool CWeaponLimpetmine::ValidDesignationTarget( CBaseEntity *pEntity )
{
if( !pEntity )
return false;
// Ignore the world
if ((!pEntity) || ( pEntity->entindex() == 0 ))
return false;
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
Assert(pPlayer);
if(!pPlayer)
return false;
// Ignore players on my team
if ( pEntity->IsPlayer() )
{
if( pEntity->InSameTeam(GetOwner()) )
return false;
else
return true;
}
// can either target my own sentry guns to turtle, or enemy buildings to target for sentries:
if ( pEntity->Classify() == CLASS_MILITARY )
{
// Is it a sentry gun I built?
if ( dynamic_cast<CObjectSentrygun *>(pEntity) && ((CObjectSentrygun*)pEntity)->GetBuilder() == pPlayer )
{
return true;
}
// Is it an enemy object?
else if ( !pPlayer->InSameTeam( pEntity ) )
{
return true;
}
}
// My limpet mines are valid
CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
if ( pLimpet && pLimpet->IsLive() && pLimpet->GetThrower() == GetOwner() )
return true;
// NPCs are valid
if ( pEntity->GetFlags() & FL_NPC )
return true;
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Set the player's sentryguns to all attack the specified target
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::DesignateSentriesToAttack( CBaseEntity *pEntity )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
if ( !pPlayer )
return;
// Tell all this player's sentryguns
for ( int i = 0; i < pPlayer->GetObjectCount(); i++ )
{
CBaseObject *pObj = pPlayer->GetObject(i);
if ( !pObj )
continue;
if ( pObj->IsSentrygun() )
{
CObjectSentrygun *pSentry = static_cast<CObjectSentrygun *>(pObj);
pSentry->DesignateTarget( pEntity );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseEntity* CWeaponLimpetmine::GetDesignatedEntity( CBaseTFPlayer *pPlayer )
{
// Construct the start and end vectors.
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
Vector vecAiming;
pPlayer->EyeVectors( &vecAiming );
Vector vecEnd = vecSrc + vecAiming * weapon_laserdesignator_range.GetFloat();
float fBestDistanceSq = FLT_MAX;
CBaseEntity* pBestEntity = NULL;
float fMaxDist = weapon_limpetmine_max_distance_off_trace.GetFloat();
float fMaxDistSq = fMaxDist * fMaxDist;
// Check all limpets against a cylinder:
if( CLimpetMine::allLimpets )
{
for ( CLimpetMine *pLimpet = CLimpetMine::allLimpets ;pLimpet; pLimpet = pLimpet->nextLimpet)
{
// Find the closest limpet to the center of the cylinder:
if( pLimpet->IsLive() && pLimpet->GetThrower() != NULL && pLimpet->GetThrower() == pPlayer )
{
Vector vecNearestPoint = PointOnLineNearestPoint( vecSrc, vecEnd, pLimpet->GetAbsOrigin() );
float fDistSq = ( pLimpet->GetAbsOrigin() - vecNearestPoint ).LengthSqr();
if (( fDistSq > fMaxDistSq ) || ( fDistSq >= fBestDistanceSq ))
continue;
if (TFGameRules()->IsBlockedByEnemyShields( vecSrc, pLimpet->GetAbsOrigin(), GetOwner()->GetTeamNumber() ))
continue;
pBestEntity = pLimpet;
fBestDistanceSq = fDistSq;
}
}
if( pBestEntity )
return pBestEntity;
}
// Check to see if we are designating a combat object:
for ( int i = 0; i < pPlayer->GetObjectCount(); i++ )
{
CBaseObject *pObj = pPlayer->GetObject(i);
if ( !pObj || !pObj->IsSentrygun() )
continue;
Vector vecObjCenter = pObj->WorldSpaceCenter();
Vector vecNearestPoint = PointOnLineNearestPoint( vecSrc, vecEnd, vecObjCenter );
float fDistSq = ( vecObjCenter - vecNearestPoint ).LengthSqr();
if (( fDistSq > fMaxDistSq ) || ( fDistSq >= fBestDistanceSq ))
continue;
if (TFGameRules()->IsBlockedByEnemyShields( vecSrc, vecObjCenter, GetOwner()->GetTeamNumber() ))
continue;
pBestEntity = pObj;
fBestDistanceSq = fDistSq;
}
// Valid or NULL
return pBestEntity;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::WeaponIdle( void )
{
if ( HasWeaponIdleTimeElapsed() )
{
if ( m_bDesignating || m_flStartedLaunchingAt )
{
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
else
{
SendWeaponAnim( ACT_VM_IDLE );
}
SetWeaponIdleTime( gpGlobals->curtime + 0.2 );
}
}
//-----------------------------------------------------------------------------
// Purpose: Start designating with the laser
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::StartDesignating( void )
{
m_bDesignating = true;
// Add myself to the designated target list
Vector vecOrigin(0,0,0);
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
#ifndef CLIENT_DLL
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
if ( !pPlayer )
return;
if ( !m_pBeam )
{
m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 5 );
m_pBeam->PointEntInit( vec3_origin, this );
m_pBeam->SetEndAttachment( 1 );
m_pBeam->SetColor( 255, 32, 32 );
m_pBeam->SetBrightness( 255 );
m_pBeam->SetNoise( 0 );
m_pBeam->SetWidth( 0.5 );
m_pBeam->SetEndWidth( 0.5 );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Stop designating with the laser
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::StopDesignating( void )
{
SendWeaponAnim( ACT_VM_IDLE );
m_bDesignating = false;
#ifndef CLIENT_DLL
if ( m_pBeam )
{
UTIL_Remove( m_pBeam );
m_pBeam = NULL;
}
if ( m_hLaserDesignation )
{
m_hLaserDesignation->SetActive( false );
}
// Remove any designated target:
m_hDesignatedEntity.Set( NULL );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Update the beam position and do designator functions
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::UpdateBeamTarget()
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
if ( !pPlayer )
return;
// "Fire" the designator beam
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
Vector vecAiming;
pPlayer->EyeVectors( &vecAiming );
Vector vecEnd = vecSrc + vecAiming * weapon_laserdesignator_range.GetFloat();
trace_t tr;
TFGameRules()->WeaponTraceLine(vecSrc, vecEnd, MASK_SHOT, pPlayer, DMG_PROBE, &tr);
// Only update our designated target point if we hit something
#ifndef CLIENT_DLL
if ( tr.fraction != 1.0 && m_bDesignating )
{
m_hLaserDesignation->SetActive( true );
m_hLaserDesignation->SetAbsOrigin( tr.endpos );
}
else
{
m_hLaserDesignation->SetActive( false );
}
// Update beam visual
if( m_pBeam )
{
m_pBeam->SetStartPos( tr.endpos );
m_pBeam->RelinkBeam();
}
CBaseEntity* pEntity= NULL;
// Perform designator functions
// Did we hit something?
//pEntity = CBaseEntity::Instance( tr.u.ent );
// If we hit a target we don't care about, try and grab the nearest valid target to our crosshair
//if ( !ValidDesignationTarget( pEntity ) )
{
// Get the designated entity, ignoring trace information:
pEntity = GetDesignatedEntity( pPlayer );
if( !ValidDesignationTarget( pEntity ) )
{
m_hDesignatedEntity.Set( NULL );
return;
}
}
// We have a valid entity, light it up.
m_hDesignatedEntity.Set( pEntity );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponLimpetmine::DecrementLimpets( void )
{
#ifndef CLIENT_DLL
if ( m_iDeployedLimpets )
{
m_iDeployedLimpets -= 1;
}
#endif
}
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Limpet mine's always selectable, because the laser designator can always fire
//-----------------------------------------------------------------------------
bool C_WeaponLimpetmine::CanBeSelected( void )
{
return true;
}
void C_WeaponLimpetmine::PreDataUpdate( DataUpdateType_t updateType )
{
BaseClass::PreDataUpdate( updateType );
m_hOldDesignatedEntity = m_hDesignatedEntity;
}
void C_WeaponLimpetmine::PostDataUpdate( DataUpdateType_t updateType )
{
BaseClass::PostDataUpdate( updateType );
if ( GetOwner() == C_BaseTFPlayer::GetLocalPlayer() )
{
// Old target isn't valid anymore, so reset it's render properties
if ( m_hOldDesignatedEntity.Get() && m_hOldDesignatedEntity != m_hDesignatedEntity )
{
m_hOldDesignatedEntity->m_nRenderFX = m_eDesignatedEntityOriginalRenderFx;
m_hOldDesignatedEntity->SetRenderMode( (RenderMode_t)m_eDesignatedEntityOriginalRenderMode );
}
// New target? Set it's render properties
if ( m_hDesignatedEntity != NULL && m_hOldDesignatedEntity != m_hDesignatedEntity )
{
// Store off original info for designated entity
m_eDesignatedEntityOriginalRenderFx = m_hDesignatedEntity->m_nRenderFX;
m_eDesignatedEntityOriginalRenderMode = m_hDesignatedEntity->GetRenderMode();
// Set up alpha blended pulsefast renderer
m_hDesignatedEntity->m_nRenderFX = kRenderFxPulseFastWider;
m_hDesignatedEntity->SetRenderMode( kRenderTransAlpha );
}
}
}
#endif