summaryrefslogtreecommitdiff
path: root/game/server/tf/tf_obj_spy_trap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf/tf_obj_spy_trap.cpp')
-rw-r--r--game/server/tf/tf_obj_spy_trap.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/game/server/tf/tf_obj_spy_trap.cpp b/game/server/tf/tf_obj_spy_trap.cpp
new file mode 100644
index 0000000..b7c52a5
--- /dev/null
+++ b/game/server/tf/tf_obj_spy_trap.cpp
@@ -0,0 +1,452 @@
+//========= 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