|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//
//
//=============================================================================
#include "cbase.h"
#include "tf_obj_spy_trap.h"
#include "tf_team.h"
#include "tf_player.h"
#include "bot/tf_bot.h"
#include "tf_gamerules.h"
#include "tf_fx.h"
#ifdef STAGING_ONLY
#define SPY_TRAP_THINK_CONTEXT "SpyTrapContext"
#define SPY_TRAP_SAP_MODEL_HOLD "models/buildables/teleporter_light.mdl"
#define SPY_TRAP_SAP_MODEL_PLACED "models/buildables/teleporter_light.mdl"
#define SPY_TRAP_RERPOGRAMMER_MODEL_HOLD "models/buildables/teleporter_light.mdl"
#define SPY_TRAP_REPROGRAMMER_MODEL_PLACED "models/buildables/teleporter_light.mdl"
#define SPY_TRAP_MAGNET_MODEL_HOLD "models/buildables/teleporter_light.mdl"
#define SPY_TRAP_MAGNET_MODEL_PLACED "models/buildables/teleporter_light.mdl"
ConVar tf_spy_trap_duration( "tf_spy_trap_duration", "20.0", FCVAR_CHEAT ); ConVar tf_spy_trap_cloak_duration( "tf_spy_trap_cloak_duration", "10", FCVAR_CHEAT ); ConVar tf_spy_trap_magnet_duration( "tf_spy_trap_magnet_duration", "5", FCVAR_CHEAT ); ConVar tf_spy_trap_magnet_force( "tf_spy_trap_magnet_force", "650", FCVAR_CHEAT );
const Vector TRAP_MINS = Vector( -24, -24, 0); const Vector TRAP_MAXS = Vector( 24, 24, 12);
BEGIN_DATADESC( CObjectSpyTrap ) DEFINE_THINKFUNC( SpyTrapThink ), END_DATADESC()
PRECACHE_REGISTER( obj_spy_trap );
LINK_ENTITY_TO_CLASS( obj_spy_trap, CObjectSpyTrap );
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CObjectSpyTrap::CObjectSpyTrap() { SetType( OBJ_SPY_TRAP ); m_attributeFlags = 0; m_bActive = false; m_nTrapMode = MODE_SPY_TRAP_RADIUS_STEALTH; m_flNextTrapEffectTime = 0.f; m_flTrapExpireTime = 0.f; m_flNextPulseTime = 0.f;
UseClientSideAnimation(); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::Spawn() { SetSolid( SOLID_BBOX ); SetModel( SPY_TRAP_SAP_MODEL_HOLD ); UTIL_SetSize( this, TRAP_MINS, TRAP_MAXS );
BaseClass::Spawn(); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::Precache() { BaseClass::Precache();
PrecacheScriptSound( "Saxxy.TurnGold" ); PrecacheScriptSound( "Weapon_Upgrade.ExplosiveHeadshot" ); PrecacheModel( SPY_TRAP_SAP_MODEL_HOLD ); PrecacheModel( SPY_TRAP_SAP_MODEL_PLACED ); PrecacheModel( SPY_TRAP_RERPOGRAMMER_MODEL_HOLD ); PrecacheModel( SPY_TRAP_REPROGRAMMER_MODEL_PLACED ); PrecacheModel( SPY_TRAP_MAGNET_MODEL_HOLD ); PrecacheModel( SPY_TRAP_MAGNET_MODEL_PLACED ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::SpyTrapThink() { // Touch-triggered traps expire after a period of time
if ( !m_bActive && GetConstructionStartTime() + tf_spy_trap_duration.GetFloat() < gpGlobals->curtime ) { Destroy(); }
// Traps that repeat their effect over time
if ( HasAttribute( TRAP_PULSE_EFFECT ) ) { if ( m_bActive ) { // Still active
if ( m_flTrapExpireTime > gpGlobals->curtime ) { // Time for another pulse
if ( m_flNextPulseTime && gpGlobals->curtime > m_flNextPulseTime ) { switch ( GetTrapType() ) { case MODE_SPY_TRAP_RADIUS_STEALTH: { TriggerTrap_RadiusCloak(); break; } case MODE_SPY_TRAP_MAGNET: { TriggerTrap_Magnet(); break; } } } } // Timer expired
else { Destroy(); } } }
SetContextThink( &CObjectSpyTrap::SpyTrapThink, gpGlobals->curtime + 0.1f, SPY_TRAP_THINK_CONTEXT ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::OnGoActive() { BaseClass::OnGoActive();
switch ( GetTrapType() ) { case MODE_SPY_TRAP_RADIUS_STEALTH: { SetModel( SPY_TRAP_SAP_MODEL_PLACED ); SetAttribute( TRAP_TRIGGER_ONBUILD | TRAP_PULSE_EFFECT ); m_flTrapExpireTime = gpGlobals->curtime + tf_spy_trap_cloak_duration.GetFloat(); break; } case MODE_SPY_TRAP_REPROGRAM: { SetModel( SPY_TRAP_REPROGRAMMER_MODEL_PLACED ); break; } case MODE_SPY_TRAP_MAGNET: { SetModel( SPY_TRAP_MAGNET_MODEL_PLACED ); SetAttribute( TRAP_TRIGGER_ONBUILD | TRAP_PULSE_EFFECT ); m_flTrapExpireTime = gpGlobals->curtime + tf_spy_trap_magnet_duration.GetFloat(); break; } } m_takedamage = DAMAGE_NO;
m_bActive = HasAttribute( TRAP_TRIGGER_ONBUILD ); if ( m_bActive ) { m_flNextPulseTime = gpGlobals->curtime; }
SetContextThink( &CObjectSpyTrap::SpyTrapThink, gpGlobals->curtime + 0.1, SPY_TRAP_THINK_CONTEXT ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectSpyTrap::SetObjectMode( int iVal ) { Assert( iVal >= MODE_SPY_TRAP_RADIUS_STEALTH && iVal <= MODE_SPY_TRAP_MAGNET ); SetTrapType( (ESpyTrapType_t)iVal );
BaseClass::SetObjectMode( iVal ); }
//-----------------------------------------------------------------------------
// Traps that trigger via touch activate here
//-----------------------------------------------------------------------------
void CObjectSpyTrap::Activate( CBaseEntity *pTouchEntity ) { if ( m_bActive ) return;
switch ( GetTrapType() ) { case MODE_SPY_TRAP_REPROGRAM: { TriggerTrap_Reprogrammer( pTouchEntity ); break; } } }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::Destroy( void ) { Explode(); UTIL_Remove( this ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::TriggerTrapEffects( void ) { if ( gpGlobals->curtime < m_flNextTrapEffectTime ) return;
Vector vecOrigin = GetAbsOrigin(); CPVSFilter filter( vecOrigin ); TE_TFParticleEffect( filter, 0.f, "Explosion_ShockWave_01", vecOrigin, vec3_angle ); EmitSound( filter, entindex(), "Weapon_Upgrade.ExplosiveHeadshot" );
m_flNextTrapEffectTime = gpGlobals->curtime + 1.f; }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CObjectSpyTrap::IsPlacementPosValid( void ) { // This is mostly duplicated from baseobject. Poop.
// The alternative is modifying a bunch of call sites
// and derived classes to handle an object pointer,
// and having special case code in the base method.
// It's a "which is poopier" contest.
CTFPlayer *pPlayer = GetOwner(); if ( !pPlayer ) return false;
bool bValid = CalculatePlacementPos(); if ( !bValid ) return false;
#ifndef CLIENT_DLL
if ( !EstimateValidBuildPos() ) return false; #endif
// Verify that the entire object can fit here - ignoring players
trace_t tr; CTraceFilterIgnorePlayers filter( this, COLLISION_GROUP_PLAYER ); UTIL_TraceEntity( this, m_vecBuildOrigin, m_vecBuildOrigin, MASK_SOLID, &filter, &tr ); if ( tr.fraction < 1.0f ) return false;
// Make sure we can see the final position
UTIL_TraceLine( pPlayer->EyePosition(), m_vecBuildOrigin + Vector( 0, 0, m_vecBuildMaxs[2] * 0.5 ), MASK_PLAYERSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction < 1.0 ) return false;
return true; }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::StartTouch( CBaseEntity *pOther ) { BaseClass::StartTouch( pOther );
if ( !m_bActive && pOther->IsPlayer() && !InSameTeam( pOther ) ) { if ( ( InSameTeam( pOther ) && HasAttribute( TRAP_TRIGGER_FRIENDLY ) ) || ( !InSameTeam( pOther ) ) ) { Activate( pOther ); } } }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::EndTouch( CBaseEntity *pOther ) { BaseClass::EndTouch( pOther ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : collisionGroup -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CObjectSpyTrap::ShouldCollide( int collisionGroup, int contentsMask ) const { // Ignore player collisions when trap pulses
if ( HasAttribute( TRAP_PULSE_EFFECT ) ) { if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) { return false; } }
return BaseClass::ShouldCollide( collisionGroup, contentsMask ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CObjectSpyTrap::TriggerTrap_RadiusCloak( void ) { int nRadius = 250; float flDuration = 2.f;
for ( int i = 0; i < gpGlobals->maxClients; i++ ) { CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); if ( !pPlayer ) continue;
// Same team, alive, etc
if ( !IsValidRadiusCloakTarget( pPlayer ) ) continue;
// Range check from pTarget
Vector vecDist = pPlayer->GetAbsOrigin() - GetAbsOrigin(); if ( vecDist.LengthSqr() > nRadius * nRadius ) continue;
// Ignore bots we can't see
trace_t trace; UTIL_TraceLine( pPlayer->WorldSpaceCenter(), WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace ); if ( trace.fraction < 1.0f ) continue;
// Apply
pPlayer->m_Shared.AddCond( TF_COND_STEALTHED_USER_BUFF, flDuration, GetBuilder() ); }
TriggerTrapEffects();
m_flNextPulseTime = gpGlobals->curtime + 0.25f; }
//-----------------------------------------------------------------------------
// Purpose: Valid player to apply cloak effects to?
//-----------------------------------------------------------------------------
bool CObjectSpyTrap::IsValidRadiusCloakTarget( CTFPlayer *pTarget ) { if ( !pTarget ) return false;
if ( !pTarget->IsAlive() ) return false;
if ( !InSameTeam( pTarget ) ) return false;
return true; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectSpyTrap::TriggerTrap_Reprogrammer( CBaseEntity *pTouchEntity ) { if ( !pTouchEntity ) return;
if ( pTouchEntity->IsPlayer() ) { CTFPlayer *pTFPlayer = ToTFPlayer( pTouchEntity ); if ( pTFPlayer && pTFPlayer->IsBot() ) { if ( pTFPlayer->IsMiniBoss() ) return;
pTFPlayer->m_Shared.AddCond( TF_COND_REPROGRAMMED ); } }
CPVSFilter filter( GetAbsOrigin() ); EmitSound( filter, entindex(), "Saxxy.TurnGold" ); TriggerTrapEffects();
Destroy(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectSpyTrap::TriggerTrap_Magnet( void ) { int nRadius = 700;
for ( int i = 1; i < gpGlobals->maxClients; i++ ) { CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); if ( !pPlayer ) continue;
// Same team, alive, etc
if ( !pPlayer->IsAlive() ) continue;
if ( InSameTeam( pPlayer ) ) continue;
// Range check from pTarget
Vector vecDist = pPlayer->GetAbsOrigin() - GetAbsOrigin(); if ( vecDist.LengthSqr() > nRadius * nRadius ) continue;
// Ignore bots we can't see
trace_t trace; UTIL_TraceLine( pPlayer->WorldSpaceCenter(), WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace ); if ( trace.fraction < 1.0f ) continue;
// Find where we're going
Vector vecSourcePos = pPlayer->GetAbsOrigin(); Vector vecTargetPos = GetAbsOrigin(); vecTargetPos.z -= 32.0f;
Vector vecVelocity = vecTargetPos - vecSourcePos; vecVelocity.z += 150.f;
// Send us flying
if ( pPlayer->GetFlags() & FL_ONGROUND ) { pPlayer->SetGroundEntity( NULL ); pPlayer->SetGroundChangeTime( gpGlobals->curtime + 0.5f ); }
pPlayer->Teleport( NULL, NULL, &vecVelocity ); pPlayer->m_Shared.StunPlayer( 0.5, 0.85f, TF_STUN_MOVEMENT, GetOwner() ); }
TriggerTrapEffects();
m_flNextPulseTime = gpGlobals->curtime + 0.2f; }
#endif // STAGING_ONLY
|