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.

411 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_spotlight.h"
  8. #include "ai_basenpc.h"
  9. #include "spotlightend.h"
  10. #include "beam_shared.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. //-----------------------------------------------------------------------------
  14. // Parameters for how the scanner relates to citizens.
  15. //-----------------------------------------------------------------------------
  16. #define SPOTLIGHT_WIDTH 96
  17. //-----------------------------------------------------------------------------
  18. // Save/load
  19. //-----------------------------------------------------------------------------
  20. BEGIN_SIMPLE_DATADESC( CAI_Spotlight )
  21. // Robin: Don't save, recreated after restore/transition.
  22. //DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ),
  23. //DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ),
  24. DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ),
  25. DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ),
  26. DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ),
  27. DEFINE_FIELD( m_flSpotlightMaxLength, FIELD_FLOAT ),
  28. DEFINE_FIELD( m_flConstraintAngle, FIELD_FLOAT ),
  29. DEFINE_FIELD( m_nHaloSprite, FIELD_MODELINDEX ),
  30. DEFINE_FIELD( m_nSpotlightAttachment, FIELD_INTEGER ),
  31. DEFINE_FIELD( m_nFlags, FIELD_INTEGER ),
  32. DEFINE_FIELD( m_vAngularVelocity, FIELD_QUATERNION ),
  33. END_DATADESC()
  34. //-----------------------------------------------------------------------------
  35. // Purpose:
  36. //-----------------------------------------------------------------------------
  37. CAI_Spotlight::CAI_Spotlight()
  38. {
  39. #ifdef _DEBUG
  40. m_vSpotlightTargetPos.Init();
  41. m_vSpotlightDir.Init();
  42. #endif
  43. }
  44. CAI_Spotlight::~CAI_Spotlight()
  45. {
  46. SpotlightDestroy();
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. //-----------------------------------------------------------------------------
  51. void CAI_Spotlight::Precache(void)
  52. {
  53. // Sprites
  54. m_nHaloSprite = GetOuter()->PrecacheModel("sprites/light_glow03.vmt");
  55. GetOuter()->PrecacheModel( "sprites/glow_test02.vmt" );
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. void CAI_Spotlight::Init( CAI_BaseNPC *pOuter, int nFlags, float flConstraintAngle, float flMaxLength )
  61. {
  62. SetOuter( pOuter );
  63. m_nFlags = nFlags;
  64. m_flConstraintAngle = flConstraintAngle;
  65. m_flSpotlightMaxLength = flMaxLength;
  66. // Check for user error
  67. if (m_flSpotlightMaxLength <= 0)
  68. {
  69. DevMsg("ERROR: Invalid spotlight length <= 0, setting to 500\n");
  70. m_flSpotlightMaxLength = 500;
  71. }
  72. Precache();
  73. m_vSpotlightTargetPos = vec3_origin;
  74. m_hSpotlight = NULL;
  75. m_hSpotlightTarget = NULL;
  76. AngleVectors( GetAbsAngles(), &m_vSpotlightDir );
  77. m_vAngularVelocity.Init( 0, 0, 0, 1 );
  78. m_flSpotlightCurLength = m_flSpotlightMaxLength;
  79. }
  80. //------------------------------------------------------------------------------
  81. // Computes the spotlight endpoint
  82. //------------------------------------------------------------------------------
  83. void CAI_Spotlight::ComputeEndpoint( const Vector &vecStartPoint, Vector *pEndPoint )
  84. {
  85. // Create the endpoint
  86. trace_t tr;
  87. AI_TraceLine( vecStartPoint, vecStartPoint + m_vSpotlightDir * 2 * m_flSpotlightMaxLength, MASK_OPAQUE, GetOuter(), COLLISION_GROUP_NONE, &tr );
  88. *pEndPoint = tr.endpos;
  89. }
  90. //------------------------------------------------------------------------------
  91. // Purpose:
  92. //------------------------------------------------------------------------------
  93. void CAI_Spotlight::SpotlightCreate( int nAttachment, const Vector &vecInitialDir )
  94. {
  95. // Make sure we don't already have one
  96. if ( m_hSpotlight != NULL )
  97. return;
  98. m_vSpotlightDir = vecInitialDir;
  99. VectorNormalize( m_vSpotlightDir );
  100. m_nSpotlightAttachment = nAttachment;
  101. CreateSpotlightEntities();
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose: Create the beam & spotlight end for this spotlight.
  105. // Will be re-called after restore/transition
  106. //-----------------------------------------------------------------------------
  107. void CAI_Spotlight::CreateSpotlightEntities( void )
  108. {
  109. m_vAngularVelocity.Init( 0, 0, 0, 1 );
  110. // Create the endpoint
  111. // Get the initial position...
  112. Vector vecStartPoint;
  113. if ( m_nSpotlightAttachment == 0 )
  114. {
  115. vecStartPoint = GetOuter()->GetAbsOrigin();
  116. }
  117. else
  118. {
  119. GetOuter()->GetAttachment( m_nSpotlightAttachment, vecStartPoint );
  120. }
  121. Vector vecEndPoint;
  122. ComputeEndpoint( vecStartPoint, &vecEndPoint );
  123. m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" );
  124. m_hSpotlightTarget->Spawn();
  125. m_hSpotlightTarget->SetAbsOrigin( vecEndPoint );
  126. m_hSpotlightTarget->SetOwnerEntity( GetOuter() );
  127. m_hSpotlightTarget->SetRenderColor( 255, 255, 255 );
  128. m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength;
  129. if ( FBitSet (m_nFlags, AI_SPOTLIGHT_NO_DLIGHTS) )
  130. {
  131. m_hSpotlightTarget->m_flLightScale = 0.0;
  132. }
  133. else
  134. {
  135. m_hSpotlightTarget->m_flLightScale = SPOTLIGHT_WIDTH;
  136. }
  137. // Create the beam
  138. m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH );
  139. // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore)
  140. m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY );
  141. m_hSpotlight->SetColor( 255, 255, 255 );
  142. m_hSpotlight->SetHaloTexture( m_nHaloSprite );
  143. m_hSpotlight->SetHaloScale( 32 );
  144. m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() );
  145. m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) );
  146. m_hSpotlight->SetBrightness( 32 );
  147. m_hSpotlight->SetNoise( 0 );
  148. m_hSpotlight->EntsInit( GetOuter(), m_hSpotlightTarget );
  149. m_hSpotlight->SetStartAttachment( m_nSpotlightAttachment );
  150. }
  151. //------------------------------------------------------------------------------
  152. // Purpose:
  153. //------------------------------------------------------------------------------
  154. void CAI_Spotlight::SpotlightDestroy(void)
  155. {
  156. if ( m_hSpotlight )
  157. {
  158. UTIL_Remove(m_hSpotlight);
  159. m_hSpotlight = NULL;
  160. UTIL_Remove(m_hSpotlightTarget);
  161. m_hSpotlightTarget = NULL;
  162. }
  163. }
  164. //------------------------------------------------------------------------------
  165. // Purpose:
  166. //------------------------------------------------------------------------------
  167. void CAI_Spotlight::SetSpotlightTargetPos( const Vector &vSpotlightTargetPos )
  168. {
  169. m_vSpotlightTargetPos = vSpotlightTargetPos;
  170. }
  171. //------------------------------------------------------------------------------
  172. // Purpose:
  173. //------------------------------------------------------------------------------
  174. void CAI_Spotlight::SetSpotlightTargetDirection( const Vector &vSpotlightTargetDir )
  175. {
  176. if ( !m_hSpotlight )
  177. {
  178. CreateSpotlightEntities();
  179. }
  180. VectorMA( m_hSpotlight->GetAbsStartPos(), 1000.0f, vSpotlightTargetDir, m_vSpotlightTargetPos );
  181. }
  182. //------------------------------------------------------------------------------
  183. // Constrain to cone
  184. //------------------------------------------------------------------------------
  185. bool CAI_Spotlight::ConstrainToCone( Vector *pDirection )
  186. {
  187. Vector vecOrigin, vecForward;
  188. if ( m_nSpotlightAttachment == 0 )
  189. {
  190. QAngle vecAngles;
  191. vecAngles = GetOuter()->GetAbsAngles();
  192. AngleVectors( vecAngles, &vecForward );
  193. }
  194. else
  195. {
  196. GetOuter()->GetAttachment( m_nSpotlightAttachment, vecOrigin, &vecForward );
  197. }
  198. if ( m_flConstraintAngle == 0.0f )
  199. {
  200. *pDirection = vecForward;
  201. return true;
  202. }
  203. float flDot = DotProduct( vecForward, *pDirection );
  204. if ( flDot >= cos( DEG2RAD( m_flConstraintAngle ) ) )
  205. return false;
  206. Vector vecAxis;
  207. CrossProduct( *pDirection, vecForward, vecAxis );
  208. VectorNormalize( vecAxis );
  209. Quaternion q;
  210. AxisAngleQuaternion( vecAxis, -m_flConstraintAngle, q );
  211. Vector vecResult;
  212. matrix3x4_t rot;
  213. QuaternionMatrix( q, rot );
  214. VectorRotate( vecForward, rot, vecResult );
  215. VectorNormalize( vecResult );
  216. *pDirection = vecResult;
  217. return true;
  218. }
  219. //------------------------------------------------------------------------------
  220. // Purpose:
  221. //------------------------------------------------------------------------------
  222. #define QUAT_BLEND_FACTOR 0.4f
  223. void CAI_Spotlight::UpdateSpotlightDirection( void )
  224. {
  225. if ( !m_hSpotlight )
  226. {
  227. CreateSpotlightEntities();
  228. }
  229. // Compute the current beam direction
  230. Vector vTargetDir;
  231. VectorSubtract( m_vSpotlightTargetPos, m_hSpotlight->GetAbsStartPos(), vTargetDir );
  232. VectorNormalize(vTargetDir);
  233. ConstrainToCone( &vTargetDir );
  234. // Compute the amount to rotate
  235. float flDot = DotProduct( vTargetDir, m_vSpotlightDir );
  236. flDot = clamp( flDot, -1.0f, 1.0f );
  237. float flAngle = AngleNormalize( RAD2DEG( acos( flDot ) ) );
  238. float flClampedAngle = clamp( flAngle, 0.0f, 45.0f );
  239. float flBeamTurnRate = SimpleSplineRemapVal( flClampedAngle, 0.0f, 45.0f, 10.0f, 45.0f );
  240. if ( fabs(flAngle) > flBeamTurnRate * gpGlobals->frametime )
  241. {
  242. flAngle = flBeamTurnRate * gpGlobals->frametime;
  243. }
  244. // Compute the rotation axis
  245. Vector vecRotationAxis;
  246. CrossProduct( m_vSpotlightDir, vTargetDir, vecRotationAxis );
  247. if ( VectorNormalize( vecRotationAxis ) < 1e-3 )
  248. {
  249. vecRotationAxis.Init( 0, 0, 1 );
  250. }
  251. // Compute the actual rotation amount, using quat slerp blending
  252. Quaternion desiredQuat, resultQuat;
  253. AxisAngleQuaternion( vecRotationAxis, flAngle, desiredQuat );
  254. QuaternionSlerp( m_vAngularVelocity, desiredQuat, QUAT_BLEND_FACTOR, resultQuat );
  255. m_vAngularVelocity = resultQuat;
  256. // If we're really close, and we're not moving very quickly, slam.
  257. float flActualRotation = AngleNormalize( RAD2DEG(2 * acos(m_vAngularVelocity.w)) );
  258. if (( flActualRotation < 1e-3 ) && (flAngle < 1e-3 ))
  259. {
  260. m_vSpotlightDir = vTargetDir;
  261. m_vAngularVelocity.Init( 0, 0, 0, 1 );
  262. return;
  263. }
  264. // Update the desired direction
  265. matrix3x4_t rot;
  266. Vector vecNewDir;
  267. QuaternionMatrix( m_vAngularVelocity, rot );
  268. VectorRotate( m_vSpotlightDir, rot, vecNewDir );
  269. m_vSpotlightDir = vecNewDir;
  270. VectorNormalize(m_vSpotlightDir);
  271. }
  272. //------------------------------------------------------------------------------
  273. // Purpose:
  274. //------------------------------------------------------------------------------
  275. void CAI_Spotlight::UpdateSpotlightEndpoint( void )
  276. {
  277. if ( !m_hSpotlight )
  278. {
  279. CreateSpotlightEntities();
  280. }
  281. Vector vecStartPoint, vecEndPoint;
  282. vecStartPoint = m_hSpotlight->GetAbsStartPos();
  283. ComputeEndpoint( vecStartPoint, &vecEndPoint );
  284. // If I'm not facing the spotlight turn it off
  285. Vector vecSpotDir;
  286. VectorSubtract( vecEndPoint, vecStartPoint, vecSpotDir );
  287. float flBeamLength = VectorNormalize(vecSpotDir);
  288. m_hSpotlightTarget->SetAbsOrigin( vecEndPoint );
  289. m_hSpotlightTarget->SetAbsVelocity( vec3_origin );
  290. m_hSpotlightTarget->m_vSpotlightOrg = vecStartPoint;
  291. m_hSpotlightTarget->m_vSpotlightDir = vecSpotDir;
  292. // Avoid sudden change in where beam fades out when cross disconinuities
  293. m_flSpotlightCurLength = Lerp( 0.20f, m_flSpotlightCurLength, flBeamLength );
  294. // Fade out spotlight end if past max length.
  295. if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength)
  296. {
  297. m_hSpotlightTarget->SetRenderColorA( 0 );
  298. m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
  299. }
  300. else if (m_flSpotlightCurLength > m_flSpotlightMaxLength)
  301. {
  302. m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) );
  303. m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
  304. }
  305. else
  306. {
  307. m_hSpotlightTarget->SetRenderColorA( 1.0 );
  308. m_hSpotlight->SetFadeLength(m_flSpotlightCurLength);
  309. }
  310. // Adjust end width to keep beam width constant
  311. float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength / m_flSpotlightMaxLength );
  312. flNewWidth = MIN( 100, flNewWidth );
  313. m_hSpotlight->SetWidth(flNewWidth);
  314. m_hSpotlight->SetEndWidth(flNewWidth);
  315. // Adjust width of light on the end.
  316. if ( FBitSet (m_nFlags, AI_SPOTLIGHT_NO_DLIGHTS) )
  317. {
  318. m_hSpotlightTarget->m_flLightScale = 0.0;
  319. }
  320. else
  321. {
  322. m_hSpotlightTarget->m_flLightScale = flNewWidth;
  323. }
  324. }
  325. //------------------------------------------------------------------------------
  326. // Purpose: Update the direction and position of my spotlight (if it's active)
  327. //------------------------------------------------------------------------------
  328. void CAI_Spotlight::Update(void)
  329. {
  330. if ( !m_hSpotlight )
  331. {
  332. CreateSpotlightEntities();
  333. }
  334. // Update the beam direction
  335. UpdateSpotlightDirection();
  336. UpdateSpotlightEndpoint();
  337. }