Team Fortress 2 Source Code as on 22/4/2020
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.

452 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_obj_spy_trap.h"
  8. #include "tf_team.h"
  9. #include "tf_player.h"
  10. #include "bot/tf_bot.h"
  11. #include "tf_gamerules.h"
  12. #include "tf_fx.h"
  13. #ifdef STAGING_ONLY
  14. #define SPY_TRAP_THINK_CONTEXT "SpyTrapContext"
  15. #define SPY_TRAP_SAP_MODEL_HOLD "models/buildables/teleporter_light.mdl"
  16. #define SPY_TRAP_SAP_MODEL_PLACED "models/buildables/teleporter_light.mdl"
  17. #define SPY_TRAP_RERPOGRAMMER_MODEL_HOLD "models/buildables/teleporter_light.mdl"
  18. #define SPY_TRAP_REPROGRAMMER_MODEL_PLACED "models/buildables/teleporter_light.mdl"
  19. #define SPY_TRAP_MAGNET_MODEL_HOLD "models/buildables/teleporter_light.mdl"
  20. #define SPY_TRAP_MAGNET_MODEL_PLACED "models/buildables/teleporter_light.mdl"
  21. ConVar tf_spy_trap_duration( "tf_spy_trap_duration", "20.0", FCVAR_CHEAT );
  22. ConVar tf_spy_trap_cloak_duration( "tf_spy_trap_cloak_duration", "10", FCVAR_CHEAT );
  23. ConVar tf_spy_trap_magnet_duration( "tf_spy_trap_magnet_duration", "5", FCVAR_CHEAT );
  24. ConVar tf_spy_trap_magnet_force( "tf_spy_trap_magnet_force", "650", FCVAR_CHEAT );
  25. const Vector TRAP_MINS = Vector( -24, -24, 0);
  26. const Vector TRAP_MAXS = Vector( 24, 24, 12);
  27. BEGIN_DATADESC( CObjectSpyTrap )
  28. DEFINE_THINKFUNC( SpyTrapThink ),
  29. END_DATADESC()
  30. PRECACHE_REGISTER( obj_spy_trap );
  31. LINK_ENTITY_TO_CLASS( obj_spy_trap, CObjectSpyTrap );
  32. //-----------------------------------------------------------------------------
  33. //
  34. //-----------------------------------------------------------------------------
  35. CObjectSpyTrap::CObjectSpyTrap()
  36. {
  37. SetType( OBJ_SPY_TRAP );
  38. m_attributeFlags = 0;
  39. m_bActive = false;
  40. m_nTrapMode = MODE_SPY_TRAP_RADIUS_STEALTH;
  41. m_flNextTrapEffectTime = 0.f;
  42. m_flTrapExpireTime = 0.f;
  43. m_flNextPulseTime = 0.f;
  44. UseClientSideAnimation();
  45. }
  46. //-----------------------------------------------------------------------------
  47. //
  48. //-----------------------------------------------------------------------------
  49. void CObjectSpyTrap::Spawn()
  50. {
  51. SetSolid( SOLID_BBOX );
  52. SetModel( SPY_TRAP_SAP_MODEL_HOLD );
  53. UTIL_SetSize( this, TRAP_MINS, TRAP_MAXS );
  54. BaseClass::Spawn();
  55. }
  56. //-----------------------------------------------------------------------------
  57. //
  58. //-----------------------------------------------------------------------------
  59. void CObjectSpyTrap::Precache()
  60. {
  61. BaseClass::Precache();
  62. PrecacheScriptSound( "Saxxy.TurnGold" );
  63. PrecacheScriptSound( "Weapon_Upgrade.ExplosiveHeadshot" );
  64. PrecacheModel( SPY_TRAP_SAP_MODEL_HOLD );
  65. PrecacheModel( SPY_TRAP_SAP_MODEL_PLACED );
  66. PrecacheModel( SPY_TRAP_RERPOGRAMMER_MODEL_HOLD );
  67. PrecacheModel( SPY_TRAP_REPROGRAMMER_MODEL_PLACED );
  68. PrecacheModel( SPY_TRAP_MAGNET_MODEL_HOLD );
  69. PrecacheModel( SPY_TRAP_MAGNET_MODEL_PLACED );
  70. }
  71. //-----------------------------------------------------------------------------
  72. //
  73. //-----------------------------------------------------------------------------
  74. void CObjectSpyTrap::SpyTrapThink()
  75. {
  76. // Touch-triggered traps expire after a period of time
  77. if ( !m_bActive && GetConstructionStartTime() + tf_spy_trap_duration.GetFloat() < gpGlobals->curtime )
  78. {
  79. Destroy();
  80. }
  81. // Traps that repeat their effect over time
  82. if ( HasAttribute( TRAP_PULSE_EFFECT ) )
  83. {
  84. if ( m_bActive )
  85. {
  86. // Still active
  87. if ( m_flTrapExpireTime > gpGlobals->curtime )
  88. {
  89. // Time for another pulse
  90. if ( m_flNextPulseTime && gpGlobals->curtime > m_flNextPulseTime )
  91. {
  92. switch ( GetTrapType() )
  93. {
  94. case MODE_SPY_TRAP_RADIUS_STEALTH:
  95. {
  96. TriggerTrap_RadiusCloak();
  97. break;
  98. }
  99. case MODE_SPY_TRAP_MAGNET:
  100. {
  101. TriggerTrap_Magnet();
  102. break;
  103. }
  104. }
  105. }
  106. }
  107. // Timer expired
  108. else
  109. {
  110. Destroy();
  111. }
  112. }
  113. }
  114. SetContextThink( &CObjectSpyTrap::SpyTrapThink, gpGlobals->curtime + 0.1f, SPY_TRAP_THINK_CONTEXT );
  115. }
  116. //-----------------------------------------------------------------------------
  117. //
  118. //-----------------------------------------------------------------------------
  119. void CObjectSpyTrap::OnGoActive()
  120. {
  121. BaseClass::OnGoActive();
  122. switch ( GetTrapType() )
  123. {
  124. case MODE_SPY_TRAP_RADIUS_STEALTH:
  125. {
  126. SetModel( SPY_TRAP_SAP_MODEL_PLACED );
  127. SetAttribute( TRAP_TRIGGER_ONBUILD | TRAP_PULSE_EFFECT );
  128. m_flTrapExpireTime = gpGlobals->curtime + tf_spy_trap_cloak_duration.GetFloat();
  129. break;
  130. }
  131. case MODE_SPY_TRAP_REPROGRAM:
  132. {
  133. SetModel( SPY_TRAP_REPROGRAMMER_MODEL_PLACED );
  134. break;
  135. }
  136. case MODE_SPY_TRAP_MAGNET:
  137. {
  138. SetModel( SPY_TRAP_MAGNET_MODEL_PLACED );
  139. SetAttribute( TRAP_TRIGGER_ONBUILD | TRAP_PULSE_EFFECT );
  140. m_flTrapExpireTime = gpGlobals->curtime + tf_spy_trap_magnet_duration.GetFloat();
  141. break;
  142. }
  143. }
  144. m_takedamage = DAMAGE_NO;
  145. m_bActive = HasAttribute( TRAP_TRIGGER_ONBUILD );
  146. if ( m_bActive )
  147. {
  148. m_flNextPulseTime = gpGlobals->curtime;
  149. }
  150. SetContextThink( &CObjectSpyTrap::SpyTrapThink, gpGlobals->curtime + 0.1, SPY_TRAP_THINK_CONTEXT );
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CObjectSpyTrap::SetObjectMode( int iVal )
  156. {
  157. Assert( iVal >= MODE_SPY_TRAP_RADIUS_STEALTH && iVal <= MODE_SPY_TRAP_MAGNET );
  158. SetTrapType( (ESpyTrapType_t)iVal );
  159. BaseClass::SetObjectMode( iVal );
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Traps that trigger via touch activate here
  163. //-----------------------------------------------------------------------------
  164. void CObjectSpyTrap::Activate( CBaseEntity *pTouchEntity )
  165. {
  166. if ( m_bActive )
  167. return;
  168. switch ( GetTrapType() )
  169. {
  170. case MODE_SPY_TRAP_REPROGRAM:
  171. {
  172. TriggerTrap_Reprogrammer( pTouchEntity );
  173. break;
  174. }
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. //
  179. //-----------------------------------------------------------------------------
  180. void CObjectSpyTrap::Destroy( void )
  181. {
  182. Explode();
  183. UTIL_Remove( this );
  184. }
  185. //-----------------------------------------------------------------------------
  186. //
  187. //-----------------------------------------------------------------------------
  188. void CObjectSpyTrap::TriggerTrapEffects( void )
  189. {
  190. if ( gpGlobals->curtime < m_flNextTrapEffectTime )
  191. return;
  192. Vector vecOrigin = GetAbsOrigin();
  193. CPVSFilter filter( vecOrigin );
  194. TE_TFParticleEffect( filter, 0.f, "Explosion_ShockWave_01", vecOrigin, vec3_angle );
  195. EmitSound( filter, entindex(), "Weapon_Upgrade.ExplosiveHeadshot" );
  196. m_flNextTrapEffectTime = gpGlobals->curtime + 1.f;
  197. }
  198. //-----------------------------------------------------------------------------
  199. //
  200. //-----------------------------------------------------------------------------
  201. bool CObjectSpyTrap::IsPlacementPosValid( void )
  202. {
  203. // This is mostly duplicated from baseobject. Poop.
  204. // The alternative is modifying a bunch of call sites
  205. // and derived classes to handle an object pointer,
  206. // and having special case code in the base method.
  207. // It's a "which is poopier" contest.
  208. CTFPlayer *pPlayer = GetOwner();
  209. if ( !pPlayer )
  210. return false;
  211. bool bValid = CalculatePlacementPos();
  212. if ( !bValid )
  213. return false;
  214. #ifndef CLIENT_DLL
  215. if ( !EstimateValidBuildPos() )
  216. return false;
  217. #endif
  218. // Verify that the entire object can fit here - ignoring players
  219. trace_t tr;
  220. CTraceFilterIgnorePlayers filter( this, COLLISION_GROUP_PLAYER );
  221. UTIL_TraceEntity( this, m_vecBuildOrigin, m_vecBuildOrigin, MASK_SOLID, &filter, &tr );
  222. if ( tr.fraction < 1.0f )
  223. return false;
  224. // Make sure we can see the final position
  225. UTIL_TraceLine( pPlayer->EyePosition(), m_vecBuildOrigin + Vector( 0, 0, m_vecBuildMaxs[2] * 0.5 ), MASK_PLAYERSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr );
  226. if ( tr.fraction < 1.0 )
  227. return false;
  228. return true;
  229. }
  230. //-----------------------------------------------------------------------------
  231. //
  232. //-----------------------------------------------------------------------------
  233. void CObjectSpyTrap::StartTouch( CBaseEntity *pOther )
  234. {
  235. BaseClass::StartTouch( pOther );
  236. if ( !m_bActive && pOther->IsPlayer() && !InSameTeam( pOther ) )
  237. {
  238. if ( ( InSameTeam( pOther ) && HasAttribute( TRAP_TRIGGER_FRIENDLY ) ) ||
  239. ( !InSameTeam( pOther ) ) )
  240. {
  241. Activate( pOther );
  242. }
  243. }
  244. }
  245. //-----------------------------------------------------------------------------
  246. //
  247. //-----------------------------------------------------------------------------
  248. void CObjectSpyTrap::EndTouch( CBaseEntity *pOther )
  249. {
  250. BaseClass::EndTouch( pOther );
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : collisionGroup -
  255. // Output : Returns true on success, false on failure.
  256. //-----------------------------------------------------------------------------
  257. bool CObjectSpyTrap::ShouldCollide( int collisionGroup, int contentsMask ) const
  258. {
  259. // Ignore player collisions when trap pulses
  260. if ( HasAttribute( TRAP_PULSE_EFFECT ) )
  261. {
  262. if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
  263. {
  264. return false;
  265. }
  266. }
  267. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  268. }
  269. //-----------------------------------------------------------------------------
  270. //
  271. //-----------------------------------------------------------------------------
  272. void CObjectSpyTrap::TriggerTrap_RadiusCloak( void )
  273. {
  274. int nRadius = 250;
  275. float flDuration = 2.f;
  276. for ( int i = 0; i < gpGlobals->maxClients; i++ )
  277. {
  278. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  279. if ( !pPlayer )
  280. continue;
  281. // Same team, alive, etc
  282. if ( !IsValidRadiusCloakTarget( pPlayer ) )
  283. continue;
  284. // Range check from pTarget
  285. Vector vecDist = pPlayer->GetAbsOrigin() - GetAbsOrigin();
  286. if ( vecDist.LengthSqr() > nRadius * nRadius )
  287. continue;
  288. // Ignore bots we can't see
  289. trace_t trace;
  290. UTIL_TraceLine( pPlayer->WorldSpaceCenter(), WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  291. if ( trace.fraction < 1.0f )
  292. continue;
  293. // Apply
  294. pPlayer->m_Shared.AddCond( TF_COND_STEALTHED_USER_BUFF, flDuration, GetBuilder() );
  295. }
  296. TriggerTrapEffects();
  297. m_flNextPulseTime = gpGlobals->curtime + 0.25f;
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Purpose: Valid player to apply cloak effects to?
  301. //-----------------------------------------------------------------------------
  302. bool CObjectSpyTrap::IsValidRadiusCloakTarget( CTFPlayer *pTarget )
  303. {
  304. if ( !pTarget )
  305. return false;
  306. if ( !pTarget->IsAlive() )
  307. return false;
  308. if ( !InSameTeam( pTarget ) )
  309. return false;
  310. return true;
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Purpose:
  314. //-----------------------------------------------------------------------------
  315. void CObjectSpyTrap::TriggerTrap_Reprogrammer( CBaseEntity *pTouchEntity )
  316. {
  317. if ( !pTouchEntity )
  318. return;
  319. if ( pTouchEntity->IsPlayer() )
  320. {
  321. CTFPlayer *pTFPlayer = ToTFPlayer( pTouchEntity );
  322. if ( pTFPlayer && pTFPlayer->IsBot() )
  323. {
  324. if ( pTFPlayer->IsMiniBoss() )
  325. return;
  326. pTFPlayer->m_Shared.AddCond( TF_COND_REPROGRAMMED );
  327. }
  328. }
  329. CPVSFilter filter( GetAbsOrigin() );
  330. EmitSound( filter, entindex(), "Saxxy.TurnGold" );
  331. TriggerTrapEffects();
  332. Destroy();
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. //-----------------------------------------------------------------------------
  337. void CObjectSpyTrap::TriggerTrap_Magnet( void )
  338. {
  339. int nRadius = 700;
  340. for ( int i = 1; i < gpGlobals->maxClients; i++ )
  341. {
  342. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  343. if ( !pPlayer )
  344. continue;
  345. // Same team, alive, etc
  346. if ( !pPlayer->IsAlive() )
  347. continue;
  348. if ( InSameTeam( pPlayer ) )
  349. continue;
  350. // Range check from pTarget
  351. Vector vecDist = pPlayer->GetAbsOrigin() - GetAbsOrigin();
  352. if ( vecDist.LengthSqr() > nRadius * nRadius )
  353. continue;
  354. // Ignore bots we can't see
  355. trace_t trace;
  356. UTIL_TraceLine( pPlayer->WorldSpaceCenter(), WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  357. if ( trace.fraction < 1.0f )
  358. continue;
  359. // Find where we're going
  360. Vector vecSourcePos = pPlayer->GetAbsOrigin();
  361. Vector vecTargetPos = GetAbsOrigin();
  362. vecTargetPos.z -= 32.0f;
  363. Vector vecVelocity = vecTargetPos - vecSourcePos;
  364. vecVelocity.z += 150.f;
  365. // Send us flying
  366. if ( pPlayer->GetFlags() & FL_ONGROUND )
  367. {
  368. pPlayer->SetGroundEntity( NULL );
  369. pPlayer->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
  370. }
  371. pPlayer->Teleport( NULL, NULL, &vecVelocity );
  372. pPlayer->m_Shared.StunPlayer( 0.5, 0.85f, TF_STUN_MOVEMENT, GetOwner() );
  373. }
  374. TriggerTrapEffects();
  375. m_flNextPulseTime = gpGlobals->curtime + 0.2f;
  376. }
  377. #endif // STAGING_ONLY