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.

537 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the big scary boom-boom machine Antlions fear.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "baseentity.h"
  8. #include "rotorwash.h"
  9. #include "soundenvelope.h"
  10. #include "engine/IEngineSound.h"
  11. #include "te_effect_dispatch.h"
  12. #include "point_posecontroller.h"
  13. #include "prop_portal_shared.h"
  14. #define TELESCOPE_ENABLE_TIME 1.0f
  15. #define TELESCOPE_DISABLE_TIME 2.0f
  16. #define TELESCOPE_ROTATEX_TIME 0.5f
  17. #define TELESCOPE_ROTATEY_TIME 0.5f
  18. #define TELESCOPING_ARM_MODEL_NAME "models/props/telescopic_arm.mdl"
  19. #define DEBUG_TELESCOPIC_ARM_AIM 1
  20. class CPropTelescopicArm : public CBaseAnimating
  21. {
  22. public:
  23. DECLARE_CLASS( CPropTelescopicArm, CBaseAnimating );
  24. DECLARE_DATADESC();
  25. virtual void UpdateOnRemove( void );
  26. virtual void Spawn( void );
  27. virtual void Precache( void );
  28. virtual void Activate ( void );
  29. void DisabledThink( void );
  30. void EnabledThink( void );
  31. void AimAt( Vector vTarget );
  32. bool TestLOS( const Vector& vAimPoint );
  33. void SetTarget( const char *pTargetName );
  34. void SetTarget( CBaseEntity *pTarget );
  35. void InputDisable( inputdata_t &inputdata );
  36. void InputEnable( inputdata_t &inputdata );
  37. void InputSetTarget( inputdata_t &inputdata );
  38. void InputTargetPlayer( inputdata_t &inputdata );
  39. private:
  40. Vector FindTargetAimPoint( void );
  41. Vector FindAimPointThroughPortal ( const CProp_Portal* pPortal );
  42. bool m_bEnabled;
  43. bool m_bCanSeeTarget;
  44. int m_iFrontMarkerAttachment;
  45. EHANDLE m_hRotXPoseController;
  46. EHANDLE m_hRotYPoseController;
  47. EHANDLE m_hTelescopicPoseController;
  48. EHANDLE m_hAimTarget;
  49. COutputEvent m_OnLostTarget;
  50. COutputEvent m_OnFoundTarget;
  51. };
  52. LINK_ENTITY_TO_CLASS( prop_telescopic_arm, CPropTelescopicArm );
  53. //-----------------------------------------------------------------------------
  54. // Save/load
  55. //-----------------------------------------------------------------------------
  56. BEGIN_DATADESC( CPropTelescopicArm )
  57. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  58. DEFINE_FIELD( m_bCanSeeTarget, FIELD_BOOLEAN ),
  59. DEFINE_FIELD( m_iFrontMarkerAttachment, FIELD_INTEGER ),
  60. DEFINE_FIELD( m_hRotXPoseController, FIELD_EHANDLE ),
  61. DEFINE_FIELD( m_hRotYPoseController, FIELD_EHANDLE ),
  62. DEFINE_FIELD( m_hTelescopicPoseController, FIELD_EHANDLE ),
  63. DEFINE_FIELD( m_hAimTarget, FIELD_EHANDLE ),
  64. DEFINE_THINKFUNC( DisabledThink ),
  65. DEFINE_THINKFUNC( EnabledThink ),
  66. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  67. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  68. DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ),
  69. DEFINE_INPUTFUNC( FIELD_VOID, "TargetPlayer", InputTargetPlayer ),
  70. DEFINE_OUTPUT ( m_OnLostTarget, "OnLostTarget" ),
  71. DEFINE_OUTPUT ( m_OnFoundTarget, "OnFoundTarget" ),
  72. END_DATADESC()
  73. void CPropTelescopicArm::UpdateOnRemove( void )
  74. {
  75. CPoseController *pPoseController;
  76. pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() );
  77. if ( pPoseController )
  78. UTIL_Remove( pPoseController );
  79. m_hRotXPoseController = 0;
  80. pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() );
  81. if ( pPoseController )
  82. UTIL_Remove( pPoseController );
  83. m_hRotYPoseController = 0;
  84. pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
  85. if ( pPoseController )
  86. UTIL_Remove( pPoseController );
  87. m_hTelescopicPoseController = 0;
  88. }
  89. void CPropTelescopicArm::Spawn( void )
  90. {
  91. char *szModel = (char *)STRING( GetModelName() );
  92. if (!szModel || !*szModel)
  93. {
  94. szModel = TELESCOPING_ARM_MODEL_NAME;
  95. SetModelName( AllocPooledString(szModel) );
  96. }
  97. Precache();
  98. SetModel( szModel );
  99. SetSolid( SOLID_VPHYSICS );
  100. SetMoveType( MOVETYPE_PUSH );
  101. VPhysicsInitStatic();
  102. BaseClass::Spawn();
  103. m_bEnabled = false;
  104. m_bCanSeeTarget = false;
  105. SetThink( &CPropTelescopicArm::DisabledThink );
  106. SetNextThink( gpGlobals->curtime + 1.0f );
  107. int iSequence = SelectHeaviestSequence ( ACT_IDLE );
  108. if ( iSequence != ACT_INVALID )
  109. {
  110. SetSequence( iSequence );
  111. ResetSequenceInfo();
  112. //Do this so we get the nice ramp-up effect.
  113. m_flPlaybackRate = random->RandomFloat( 0.0f, 1.0f );
  114. }
  115. m_iFrontMarkerAttachment = LookupAttachment( "Front_marker" );
  116. CPoseController *pPoseController;
  117. pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) );
  118. DispatchSpawn( pPoseController );
  119. if ( pPoseController )
  120. {
  121. pPoseController->SetProp( this );
  122. pPoseController->SetInterpolationWrap( true );
  123. pPoseController->SetPoseParameterName( "rot_x" );
  124. m_hRotXPoseController = pPoseController;
  125. }
  126. pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) );
  127. DispatchSpawn( pPoseController );
  128. if ( pPoseController )
  129. {
  130. pPoseController->SetProp( this );
  131. pPoseController->SetInterpolationWrap( true );
  132. pPoseController->SetPoseParameterName( "rot_y" );
  133. m_hRotYPoseController = pPoseController;
  134. }
  135. pPoseController = static_cast<CPoseController*>( CreateEntityByName( "point_posecontroller" ) );
  136. DispatchSpawn( pPoseController );
  137. if ( pPoseController )
  138. {
  139. pPoseController->SetProp( this );
  140. pPoseController->SetPoseParameterName( "telescopic" );
  141. m_hTelescopicPoseController = pPoseController;
  142. }
  143. }
  144. void CPropTelescopicArm::Precache( void )
  145. {
  146. BaseClass::Precache();
  147. PrecacheModel( STRING( GetModelName() ) );
  148. PrecacheScriptSound( "coast.thumper_hit" );
  149. PrecacheScriptSound( "coast.thumper_ambient" );
  150. PrecacheScriptSound( "coast.thumper_dust" );
  151. PrecacheScriptSound( "coast.thumper_startup" );
  152. PrecacheScriptSound( "coast.thumper_shutdown" );
  153. PrecacheScriptSound( "coast.thumper_large_hit" );
  154. }
  155. void CPropTelescopicArm::Activate( void )
  156. {
  157. BaseClass::Activate();
  158. }
  159. void CPropTelescopicArm::DisabledThink( void )
  160. {
  161. SetNextThink( gpGlobals->curtime + 1.0 );
  162. }
  163. void CPropTelescopicArm::EnabledThink( void )
  164. {
  165. CBaseEntity *pTarget = m_hAimTarget.Get();
  166. if ( !pTarget )
  167. {
  168. //SetTarget ( UTIL_PlayerByIndex( 1 ) );
  169. // Default to targeting a player
  170. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  171. {
  172. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  173. if( pPlayer && FVisible( pPlayer ) && pPlayer->IsAlive() )
  174. {
  175. pTarget = pPlayer;
  176. break;
  177. }
  178. }
  179. if( pTarget == NULL )
  180. {
  181. //search again, but don't require the player to be visible
  182. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  183. {
  184. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  185. if( pPlayer && pPlayer->IsAlive() )
  186. {
  187. pTarget = pPlayer;
  188. break;
  189. }
  190. }
  191. if( pTarget == NULL )
  192. {
  193. //search again, but don't require the player to be visible or alive
  194. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  195. {
  196. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  197. if( pPlayer )
  198. {
  199. pTarget = pPlayer;
  200. break;
  201. }
  202. }
  203. }
  204. }
  205. if( pTarget )
  206. SetTarget( pTarget );
  207. }
  208. if ( pTarget )
  209. {
  210. // Aim at the center of the abs box
  211. Vector vAimPoint = FindTargetAimPoint();
  212. Assert ( vAimPoint != vec3_invalid );
  213. AimAt( vAimPoint );
  214. // We have direct line of sight to our target
  215. if ( TestLOS ( vAimPoint ) )
  216. {
  217. // Just aquired LOS
  218. if ( !m_bCanSeeTarget )
  219. {
  220. m_OnFoundTarget.FireOutput( m_hAimTarget, this );
  221. }
  222. m_bCanSeeTarget = true;
  223. }
  224. // No LOS to target
  225. else
  226. {
  227. // Just lost LOS
  228. if ( m_bCanSeeTarget )
  229. {
  230. m_OnLostTarget.FireOutput( m_hAimTarget, this );
  231. }
  232. m_bCanSeeTarget = false;
  233. }
  234. }
  235. SetNextThink( gpGlobals->curtime + 0.1 );
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose: Finds the point to aim at in order to see the target entity.
  239. // Note: Also considers aim paths through portals.
  240. // Output : Point of the target
  241. //-----------------------------------------------------------------------------
  242. Vector CPropTelescopicArm::FindTargetAimPoint( void )
  243. {
  244. CBaseEntity *pTarget = m_hAimTarget.Get();
  245. if ( !pTarget )
  246. {
  247. // No target to aim at, can't return meaningful info
  248. Warning( "CPropTelescopicArm::FindTargetAimPoint called with no valid target entity." );
  249. return vec3_invalid;
  250. }
  251. else
  252. {
  253. Vector vFrontPoint;
  254. GetAttachment( m_iFrontMarkerAttachment, vFrontPoint, NULL, NULL, NULL );
  255. // Aim at the target through the world
  256. Vector vAimPoint = pTarget->GetAbsOrigin() + ( pTarget->WorldAlignMins() + pTarget->WorldAlignMaxs() ) * 0.5f;
  257. //float fDistToPoint = vFrontPoint.DistToSqr( vAimPoint );
  258. CProp_Portal *pShortestDistPortal = NULL;
  259. UTIL_Portal_ShortestDistance( vFrontPoint, vAimPoint, &pShortestDistPortal, true );
  260. Vector ptShortestAimPoint;
  261. if( pShortestDistPortal )
  262. {
  263. ptShortestAimPoint = FindAimPointThroughPortal( pShortestDistPortal );
  264. if( ptShortestAimPoint == vec3_invalid )
  265. ptShortestAimPoint = vAimPoint;
  266. }
  267. else
  268. {
  269. ptShortestAimPoint = vAimPoint;
  270. }
  271. return ptShortestAimPoint;
  272. }
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: Find the center of the target entity as seen through the specified portal
  276. // Input : pPortal - The portal to look through
  277. // Output : Vector& output point in world space where the target *appears* to be as seen through the portal
  278. //-----------------------------------------------------------------------------
  279. Vector CPropTelescopicArm::FindAimPointThroughPortal( const CProp_Portal* pPortal )
  280. {
  281. if ( pPortal && pPortal->m_bActivated )
  282. {
  283. CProp_Portal* pLinked = pPortal->m_hLinkedPortal.Get();
  284. CBaseEntity* pTarget = m_hAimTarget.Get();
  285. if ( pLinked && pLinked->m_bActivated && pTarget )
  286. {
  287. VMatrix matToPortalView = pLinked->m_matrixThisToLinked;
  288. Vector vTargetAimPoint = pTarget->GetAbsOrigin() + ( pTarget->WorldAlignMins() + pTarget->WorldAlignMaxs() ) * 0.5f;
  289. return matToPortalView * vTargetAimPoint;
  290. }
  291. }
  292. // Bad portal pointer, not linked, no target or otherwise failed
  293. return vec3_invalid;
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose: Tests if this prop's front point has direct line of sight to it's target entity
  297. // Input : vAimPoint - The point to aim at
  298. // Output : Returns true if target is in direct line of sight, false otherwise.
  299. //-----------------------------------------------------------------------------
  300. bool CPropTelescopicArm::TestLOS( const Vector& vAimPoint )
  301. {
  302. // Test for LOS and fire outputs if the sight condition changes
  303. Vector vFaceOrigin;
  304. trace_t tr;
  305. GetAttachment( m_iFrontMarkerAttachment, vFaceOrigin, NULL, NULL, NULL );
  306. Ray_t ray;
  307. ray.Init( vFaceOrigin, vAimPoint );
  308. ray.m_IsRay = true;
  309. // This aim point does hit target, now make sure there are no blocking objects in the way
  310. CTraceFilterWorldAndPropsOnly filter;
  311. UTIL_Portal_TraceRay( ray, MASK_SHOT, &filter, &tr );
  312. return !(tr.fraction < 1.0f);
  313. }
  314. void CPropTelescopicArm::AimAt( Vector vTarget )
  315. {
  316. Vector vFaceOrigin;
  317. GetAttachment( m_iFrontMarkerAttachment, vFaceOrigin, NULL, NULL, NULL );
  318. Vector vNormalToTarget = vTarget - vFaceOrigin;
  319. VectorNormalize( vNormalToTarget );
  320. VMatrix vWorldToLocalRotation = EntityToWorldTransform();
  321. vNormalToTarget = vWorldToLocalRotation.InverseTR().ApplyRotation( vNormalToTarget );
  322. Vector vUp;
  323. GetVectors( NULL, NULL, &vUp );
  324. QAngle qAnglesToTarget;
  325. VectorAngles( vNormalToTarget, vUp, qAnglesToTarget );
  326. float fNewX = ( qAnglesToTarget.x + 90.0f ) / 360.0f;
  327. float fNewY = qAnglesToTarget.y / 360.0f;
  328. if ( fNewY < 0.0f )
  329. fNewY += 1.0f;
  330. CPoseController *pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() );
  331. if ( pPoseController )
  332. {
  333. pPoseController->SetInterpolationTime( TELESCOPE_ROTATEX_TIME );
  334. pPoseController->SetPoseValue( fNewX );
  335. }
  336. pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() );
  337. if ( pPoseController )
  338. {
  339. pPoseController->SetInterpolationTime( TELESCOPE_ROTATEY_TIME );
  340. pPoseController->SetPoseValue( fNewY );
  341. }
  342. }
  343. void CPropTelescopicArm::SetTarget( const char *pchTargetName )
  344. {
  345. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, pchTargetName, NULL, NULL );
  346. //if ( pTarget == NULL )
  347. // pTarget = UTIL_PlayerByIndex( 1 );
  348. return SetTarget( pTarget );
  349. }
  350. void CPropTelescopicArm::SetTarget( CBaseEntity *pTarget )
  351. {
  352. m_hAimTarget = pTarget;
  353. }
  354. void CPropTelescopicArm::InputDisable( inputdata_t &inputdata )
  355. {
  356. if ( m_bEnabled )
  357. {
  358. m_bEnabled = false;
  359. EmitSound( "coast.thumper_shutdown" );
  360. CPoseController *pPoseController;
  361. pPoseController = static_cast<CPoseController*>( m_hRotXPoseController.Get() );
  362. if ( pPoseController )
  363. {
  364. pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME * 0.5f );
  365. pPoseController->SetPoseValue( 0.0f );
  366. }
  367. pPoseController = static_cast<CPoseController*>( m_hRotYPoseController.Get() );
  368. if ( pPoseController )
  369. {
  370. pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME * 0.5f );
  371. pPoseController->SetPoseValue( 0.0f );
  372. }
  373. pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
  374. if ( pPoseController )
  375. {
  376. pPoseController->SetInterpolationTime( TELESCOPE_DISABLE_TIME );
  377. pPoseController->SetPoseValue( 0.0f );
  378. }
  379. SetThink( &CPropTelescopicArm::DisabledThink );
  380. SetNextThink( gpGlobals->curtime + TELESCOPE_DISABLE_TIME );
  381. }
  382. }
  383. void CPropTelescopicArm::InputEnable( inputdata_t &inputdata )
  384. {
  385. if ( !m_bEnabled )
  386. {
  387. m_bEnabled = true;
  388. EmitSound( "coast.thumper_startup" );
  389. CPoseController *pPoseController;
  390. pPoseController = static_cast<CPoseController*>( m_hTelescopicPoseController.Get() );
  391. if ( pPoseController )
  392. {
  393. pPoseController->SetInterpolationTime( TELESCOPE_ENABLE_TIME );
  394. pPoseController->SetPoseValue( 1.0f );
  395. }
  396. SetThink( &CPropTelescopicArm::EnabledThink );
  397. SetNextThink( gpGlobals->curtime + TELESCOPE_ENABLE_TIME );
  398. }
  399. }
  400. void CPropTelescopicArm::InputSetTarget( inputdata_t &inputdata )
  401. {
  402. SetTarget( inputdata.value.String() );
  403. }
  404. void CPropTelescopicArm::InputTargetPlayer( inputdata_t &inputdata )
  405. {
  406. //SetTarget( UTIL_PlayerByIndex( 1 ) );
  407. CBaseEntity *pTarget = NULL;
  408. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  409. {
  410. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  411. if( pPlayer && FVisible( pPlayer ) && pPlayer->IsAlive() )
  412. {
  413. pTarget = pPlayer;
  414. break;
  415. }
  416. }
  417. if( pTarget == NULL )
  418. {
  419. //search again, but don't require the player to be visible
  420. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  421. {
  422. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  423. if( pPlayer && pPlayer->IsAlive() )
  424. {
  425. pTarget = pPlayer;
  426. break;
  427. }
  428. }
  429. if( pTarget == NULL )
  430. {
  431. //search again, but don't require the player to be visible or alive
  432. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  433. {
  434. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  435. if( pPlayer )
  436. {
  437. pTarget = pPlayer;
  438. break;
  439. }
  440. }
  441. }
  442. }
  443. if( pTarget )
  444. SetTarget( pTarget );
  445. }