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.

1048 lines
31 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_ai_basenpc.h"
  9. #include "c_te_particlesystem.h"
  10. #include "fx.h"
  11. #include "fx_sparks.h"
  12. #include "c_tracer.h"
  13. #include "clientsideeffects.h"
  14. #include "iefx.h"
  15. #include "dlight.h"
  16. #include "bone_setup.h"
  17. #include "c_rope.h"
  18. #include "fx_line.h"
  19. #include "c_sprite.h"
  20. #include "view.h"
  21. #include "view_scene.h"
  22. #include "materialsystem/imaterialvar.h"
  23. #include "simple_keys.h"
  24. #include "fx_envelope.h"
  25. #include "iclientvehicle.h"
  26. #include "engine/ivdebugoverlay.h"
  27. #include "particles_localspace.h"
  28. #include "dlight.h"
  29. #include "iefx.h"
  30. #include "c_te_effect_dispatch.h"
  31. #include "tier0/vprof.h"
  32. #include "clienteffectprecachesystem.h"
  33. #include <bitbuf.h>
  34. #include "fx_water.h"
  35. // memdbgon must be the last include file in a .cpp file!!!
  36. #include "tier0/memdbgon.h"
  37. #define STRIDER_MSG_BIG_SHOT 1
  38. #define STRIDER_MSG_STREAKS 2
  39. #define STRIDER_MSG_DEAD 3
  40. #define STOMP_IK_SLOT 11
  41. const int NUM_STRIDER_IK_TARGETS = 6;
  42. const float STRIDERFX_BIG_SHOT_TIME = 1.25f;
  43. const float STRIDERFX_END_ALL_TIME = 4.0f;
  44. class C_StriderFX : public C_EnvelopeFX
  45. {
  46. public:
  47. typedef C_EnvelopeFX BaseClass;
  48. C_StriderFX();
  49. ~C_StriderFX()
  50. {
  51. EffectShutdown();
  52. }
  53. void Update( C_BaseEntity *pOwner, const Vector &targetPos );
  54. // Returns the bounds relative to the origin (render bounds)
  55. virtual void GetRenderBounds( Vector& mins, Vector& maxs )
  56. {
  57. ClearBounds( mins, maxs );
  58. AddPointToBounds( m_worldPosition, mins, maxs );
  59. AddPointToBounds( m_targetPosition, mins, maxs );
  60. mins -= GetRenderOrigin();
  61. maxs -= GetRenderOrigin();
  62. }
  63. virtual void EffectInit( int entityIndex, int attachment )
  64. {
  65. m_limitHitTime = 0;
  66. BaseClass::EffectInit( entityIndex, attachment );
  67. }
  68. virtual void EffectShutdown( void )
  69. {
  70. m_limitHitTime = 0;
  71. BaseClass::EffectShutdown();
  72. }
  73. virtual int DrawModel( int flags );
  74. virtual void LimitTime( float tmax )
  75. {
  76. float dt = tmax - m_t;
  77. if ( dt < 0 )
  78. {
  79. dt = 0;
  80. }
  81. m_limitHitTime = gpGlobals->curtime + dt;
  82. BaseClass::LimitTime( tmax );
  83. }
  84. C_BaseEntity *m_pOwner;
  85. Vector m_targetPosition;
  86. Vector m_beamEndPosition;
  87. pixelvis_handle_t m_queryHandleGun;
  88. pixelvis_handle_t m_queryHandleBeamEnd;
  89. float m_limitHitTime;
  90. };
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. class C_Strider : public C_AI_BaseNPC
  95. {
  96. DECLARE_CLASS( C_Strider, C_AI_BaseNPC );
  97. public:
  98. DECLARE_CLIENTCLASS();
  99. DECLARE_INTERPOLATION();
  100. C_Strider();
  101. virtual ~C_Strider();
  102. // model specific
  103. virtual void ReceiveMessage( int classID, bf_read &msg );
  104. virtual void CalculateIKLocks( float currentTime )
  105. {
  106. // NOTE: All strider IK is solved on the server, enable this to do it client-side
  107. //BaseClass::CalculateIKLocks( currentTime );
  108. if ( m_pIk && m_pIk->m_target.Count() )
  109. {
  110. Assert(m_pIk->m_target.Count() > STOMP_IK_SLOT);
  111. // HACKHACK: Hardcoded 11??? Not a cleaner way to do this
  112. CIKTarget &target = m_pIk->m_target[STOMP_IK_SLOT];
  113. target.SetPos( m_vecHitPos );
  114. // target.latched.pos = m_vecHitPos;
  115. for ( int i = 0; i < NUM_STRIDER_IK_TARGETS; i++ )
  116. {
  117. CIKTarget &target = m_pIk->m_target[i];
  118. target.SetPos( m_vecIKTarget[i] );
  119. #if 0
  120. debugoverlay->AddBoxOverlay( m_vecIKTarget[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2), QAngle( 0, 0, 0 ), (int)255*m_pIk->m_target[i].est.latched, 0, 0, 0, 0 );
  121. #endif
  122. }
  123. }
  124. }
  125. virtual void OnDataChanged( DataUpdateType_t updateType );
  126. virtual void GetRenderBounds( Vector& theMins, Vector& theMaxs );
  127. virtual void ClientThink();
  128. private:
  129. C_Strider( const C_Strider & );
  130. C_StriderFX m_cannonFX;
  131. Vector m_vecHitPos;
  132. Vector m_vecIKTarget[NUM_STRIDER_IK_TARGETS];
  133. CInterpolatedVar< Vector > m_iv_vecHitPos;
  134. CInterpolatedVarArray< Vector, NUM_STRIDER_IK_TARGETS > m_iv_vecIKTarget;
  135. Vector m_vecRenderMins;
  136. Vector m_vecRenderMaxs;
  137. float m_flNextRopeCutTime;
  138. };
  139. IMPLEMENT_CLIENTCLASS_DT(C_Strider, DT_NPC_Strider, CNPC_Strider)
  140. RecvPropVector(RECVINFO(m_vecHitPos)),
  141. RecvPropVector(RECVINFO(m_vecIKTarget[0])),
  142. RecvPropVector(RECVINFO(m_vecIKTarget[1])),
  143. RecvPropVector(RECVINFO(m_vecIKTarget[2])),
  144. RecvPropVector(RECVINFO(m_vecIKTarget[3])),
  145. RecvPropVector(RECVINFO(m_vecIKTarget[4])),
  146. RecvPropVector(RECVINFO(m_vecIKTarget[5])),
  147. END_RECV_TABLE()
  148. C_StriderFX::C_StriderFX()
  149. {
  150. m_pOwner = NULL;
  151. m_active = false;
  152. }
  153. void C_StriderFX::Update( C_BaseEntity *pOwner, const Vector &targetPos )
  154. {
  155. BaseClass::Update();
  156. m_pOwner = pOwner;
  157. if ( m_active )
  158. {
  159. m_targetPosition = targetPos;
  160. }
  161. }
  162. // --on gun
  163. // warpy sprite bit
  164. // darkening sprite
  165. // glowy blue flare sprite
  166. // bubble warpy sprite
  167. // after glow sprite
  168. // --on line of sight
  169. // narrow beam
  170. // wide beam
  171. // --on impact point
  172. // sparkly white bits
  173. // sparkly white streaks
  174. // pale blue particle steam
  175. enum
  176. {
  177. STRIDERFX_WARP_SCALE = 0,
  178. STRIDERFX_DARKNESS,
  179. STRIDERFX_FLARE_COLOR,
  180. STRIDERFX_FLARE_SIZE,
  181. STRIDERFX_BUBBLE_SIZE,
  182. STRIDERFX_BUBBLE_REFRACT,
  183. STRIDERFX_NARROW_BEAM_COLOR,
  184. STRIDERFX_NARROW_BEAM_SIZE,
  185. STRIDERFX_WIDE_BEAM_COLOR,
  186. STRIDERFX_WIDE_BEAM_SIZE,
  187. STRIDERFX_AFTERGLOW_COLOR,
  188. STRIDERFX_WIDE_BEAM_LENGTH,
  189. STRIDERFX_SPARK_COUNT,
  190. STRIDERFX_STREAK_COUNT,
  191. STRIDERFX_STEAM_COUNT,
  192. // must be last
  193. STRIDERFX_PARAMETERS,
  194. };
  195. class CStriderFXEnvelope
  196. {
  197. public:
  198. CStriderFXEnvelope();
  199. void AddKey( int parameterIndex, const CSimpleKeyInterp &key )
  200. {
  201. Assert( parameterIndex >= 0 && parameterIndex < STRIDERFX_PARAMETERS );
  202. if ( parameterIndex >= 0 && parameterIndex < STRIDERFX_PARAMETERS )
  203. {
  204. m_parameters[parameterIndex].Insert( key );
  205. }
  206. }
  207. CSimpleKeyList m_parameters[STRIDERFX_PARAMETERS];
  208. };
  209. // NOTE: Beam widths are half-widths or radii, so this is a beam that represents a cylinder with 2" radius
  210. const float NARROW_BEAM_WIDTH = 2;
  211. const float WIDE_BEAM_WIDTH = 16;
  212. const float FLARE_SIZE = 128;
  213. const float DARK_SIZE = 64;
  214. const float AFTERGLOW_SIZE = 64;
  215. const float WARP_SIZE = 512;
  216. const float WARP_REFRACT = 0.075f;
  217. const float WARP_BUBBLE_SIZE = 256;
  218. const float WARP_BUBBLE_REFRACT = 1.0f;
  219. CStriderFXEnvelope::CStriderFXEnvelope()
  220. {
  221. AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 0, KEY_LINEAR, 0 ) );
  222. AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 1.25, KEY_ACCELERATE, 1 ) );
  223. AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1 ) );
  224. AddKey( STRIDERFX_WARP_SCALE, CSimpleKeyInterp( 1.3, KEY_LINEAR, 0 ) );
  225. AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
  226. AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 0.5, KEY_SPLINE, 1 ) );
  227. AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 1.0, KEY_LINEAR, 1 ) );
  228. AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 1.25, KEY_SPLINE, 0 ) );
  229. AddKey( STRIDERFX_DARKNESS, CSimpleKeyInterp( 2.0, KEY_SPLINE, 0 ) );
  230. AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 0, KEY_LINEAR, 0 ) );
  231. AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 0.5, KEY_LINEAR, 0 ) );
  232. AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 1.25, KEY_ACCELERATE, 1 ) );
  233. AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 1.5, KEY_LINEAR, 1 ) );
  234. AddKey( STRIDERFX_FLARE_COLOR, CSimpleKeyInterp( 2.0, KEY_SPLINE, 0 ) );
  235. AddKey( STRIDERFX_FLARE_SIZE, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
  236. AddKey( STRIDERFX_FLARE_SIZE, CSimpleKeyInterp( 1.0, KEY_LINEAR, 1 ) );
  237. AddKey( STRIDERFX_FLARE_SIZE, CSimpleKeyInterp( 2.0, KEY_LINEAR, 1 ) );
  238. AddKey( STRIDERFX_BUBBLE_SIZE, CSimpleKeyInterp( 1.3, KEY_LINEAR, 0.5 ) );
  239. AddKey( STRIDERFX_BUBBLE_SIZE, CSimpleKeyInterp( 2.0, KEY_DECELERATE, 2 ) );
  240. AddKey( STRIDERFX_BUBBLE_REFRACT, CSimpleKeyInterp( 1.3, KEY_LINEAR, 1 ) );
  241. AddKey( STRIDERFX_BUBBLE_REFRACT, CSimpleKeyInterp( 2.0, KEY_LINEAR, 0 ) );
  242. AddKey( STRIDERFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
  243. AddKey( STRIDERFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 1.25, KEY_ACCELERATE, 1.0 ) );
  244. AddKey( STRIDERFX_NARROW_BEAM_COLOR, CSimpleKeyInterp( 1.5, KEY_SPLINE, 0 ) );
  245. AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 0.0, KEY_LINEAR, 0 ) );
  246. AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 0.5, KEY_ACCELERATE, 1 ) );
  247. AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1 ) );
  248. AddKey( STRIDERFX_NARROW_BEAM_SIZE, CSimpleKeyInterp( 1.5, KEY_DECELERATE, 2 ) );
  249. AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 1.25, KEY_LINEAR, 0 ) );
  250. AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 1.5, KEY_SPLINE, 1 ) );
  251. AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 1.75, KEY_LINEAR, 1 ) );
  252. AddKey( STRIDERFX_WIDE_BEAM_COLOR, CSimpleKeyInterp( 2.1, KEY_SPLINE, 0 ) );
  253. AddKey( STRIDERFX_WIDE_BEAM_SIZE, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1 ) );
  254. AddKey( STRIDERFX_WIDE_BEAM_SIZE, CSimpleKeyInterp( 2.1, KEY_LINEAR, 1 ) );
  255. AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 1.0, KEY_LINEAR, 0 ) );
  256. AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 1.25, KEY_SPLINE, 1 ) );
  257. AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 3.0, KEY_LINEAR, 1 ) );
  258. AddKey( STRIDERFX_AFTERGLOW_COLOR, CSimpleKeyInterp( 3.5, KEY_ACCELERATE, 0 ) );
  259. AddKey( STRIDERFX_WIDE_BEAM_LENGTH, CSimpleKeyInterp( 1.25, KEY_LINEAR, 1.0 ) );
  260. AddKey( STRIDERFX_WIDE_BEAM_LENGTH, CSimpleKeyInterp( 1.5, KEY_ACCELERATE, 0.0 ) );
  261. AddKey( STRIDERFX_WIDE_BEAM_LENGTH, CSimpleKeyInterp( 2.1, KEY_LINEAR, 0 ) );
  262. //AddKey( STRIDERFX_SPARK_COUNT,
  263. //AddKey( STRIDERFX_STREAK_COUNT,
  264. //AddKey( STRIDERFX_STEAM_COUNT,
  265. }
  266. CStriderFXEnvelope g_StriderCannonEnvelope;
  267. void ScaleColor( color32 &out, const color32 &in, float scale )
  268. {
  269. out.r = (byte)(int)((float)in.r * scale);
  270. out.g = (byte)(int)((float)in.g * scale);
  271. out.b = (byte)(int)((float)in.b * scale);
  272. out.a = (byte)(int)((float)in.a * scale);
  273. }
  274. void DrawSpriteTangentSpace( const Vector &vecOrigin, float flWidth, float flHeight, color32 color )
  275. {
  276. unsigned char pColor[4] = { color.r, color.g, color.b, color.a };
  277. // Generate half-widths
  278. flWidth *= 0.5f;
  279. flHeight *= 0.5f;
  280. // Compute direction vectors for the sprite
  281. Vector fwd, right( 1, 0, 0 ), up( 0, 1, 0 );
  282. VectorSubtract( CurrentViewOrigin(), vecOrigin, fwd );
  283. float flDist = VectorNormalize( fwd );
  284. if (flDist >= 1e-3)
  285. {
  286. CrossProduct( CurrentViewUp(), fwd, right );
  287. flDist = VectorNormalize( right );
  288. if (flDist >= 1e-3)
  289. {
  290. CrossProduct( fwd, right, up );
  291. }
  292. else
  293. {
  294. // In this case, fwd == g_vecVUp, it's right above or
  295. // below us in screen space
  296. CrossProduct( fwd, CurrentViewRight(), up );
  297. VectorNormalize( up );
  298. CrossProduct( up, fwd, right );
  299. }
  300. }
  301. Vector left = -right;
  302. Vector down = -up;
  303. Vector back = -fwd;
  304. CMeshBuilder meshBuilder;
  305. Vector point;
  306. CMatRenderContextPtr pRenderContext( materials );
  307. IMesh* pMesh = pRenderContext->GetDynamicMesh( );
  308. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  309. meshBuilder.Color4ubv (pColor);
  310. meshBuilder.TexCoord2f (0, 0, 1);
  311. VectorMA (vecOrigin, -flHeight, up, point);
  312. VectorMA (point, -flWidth, right, point);
  313. meshBuilder.TangentS3fv( left.Base() );
  314. meshBuilder.TangentT3fv( down.Base() );
  315. meshBuilder.Normal3fv( back.Base() );
  316. meshBuilder.Position3fv (point.Base());
  317. meshBuilder.AdvanceVertex();
  318. meshBuilder.Color4ubv (pColor);
  319. meshBuilder.TexCoord2f (0, 0, 0);
  320. VectorMA (vecOrigin, flHeight, up, point);
  321. VectorMA (point, -flWidth, right, point);
  322. meshBuilder.TangentS3fv( left.Base() );
  323. meshBuilder.TangentT3fv( down.Base() );
  324. meshBuilder.Normal3fv( back.Base() );
  325. meshBuilder.Position3fv (point.Base());
  326. meshBuilder.AdvanceVertex();
  327. meshBuilder.Color4ubv (pColor);
  328. meshBuilder.TexCoord2f (0, 1, 0);
  329. VectorMA (vecOrigin, flHeight, up, point);
  330. VectorMA (point, flWidth, right, point);
  331. meshBuilder.TangentS3fv( left.Base() );
  332. meshBuilder.TangentT3fv( down.Base() );
  333. meshBuilder.Normal3fv( back.Base() );
  334. meshBuilder.Position3fv (point.Base());
  335. meshBuilder.AdvanceVertex();
  336. meshBuilder.Color4ubv (pColor);
  337. meshBuilder.TexCoord2f (0, 1, 1);
  338. VectorMA (vecOrigin, -flHeight, up, point);
  339. VectorMA (point, flWidth, right, point);
  340. meshBuilder.TangentS3fv( left.Base() );
  341. meshBuilder.TangentT3fv( down.Base() );
  342. meshBuilder.Normal3fv( back.Base() );
  343. meshBuilder.Position3fv (point.Base());
  344. meshBuilder.AdvanceVertex();
  345. meshBuilder.End();
  346. pMesh->Draw();
  347. }
  348. void Strider_DrawSprite( const Vector &vecOrigin, float size, const color32 &color )
  349. {
  350. DrawSpriteTangentSpace( vecOrigin, size, size, color );
  351. }
  352. void Strider_DrawLine( const Vector &start, const Vector &end, float width, IMaterial *pMaterial, const color32 &color )
  353. {
  354. FX_DrawLineFade( start, end, width, pMaterial, color, 8.0f );
  355. }
  356. int C_StriderFX::DrawModel( int )
  357. {
  358. static color32 white = {255,255,255,255};
  359. Vector params[STRIDERFX_PARAMETERS];
  360. bool hasParam[STRIDERFX_PARAMETERS];
  361. if ( !m_active )
  362. return 1;
  363. C_BaseEntity *ent = cl_entitylist->GetEnt( m_entityIndex );
  364. if ( ent )
  365. {
  366. QAngle angles;
  367. ent->GetAttachment( m_attachment, m_worldPosition, angles );
  368. }
  369. // This forces time to drive from the main clock instead of being integrated per-draw below
  370. // that way the effect moves on even when culled for visibility
  371. if ( m_limitHitTime > 0 && m_tMax > 0 )
  372. {
  373. float dt = m_limitHitTime - gpGlobals->curtime;
  374. if ( dt < 0 )
  375. {
  376. dt = 0;
  377. }
  378. // if the clock needs to move, update it.
  379. if ( m_tMax - dt > m_t )
  380. {
  381. m_t = m_tMax - dt;
  382. m_beamEndPosition = m_worldPosition;
  383. }
  384. }
  385. else
  386. {
  387. // don't have enough info to derive the time, integrate current frame time
  388. m_t += gpGlobals->frametime;
  389. if ( m_tMax > 0 )
  390. {
  391. m_t = clamp( m_t, 0, m_tMax );
  392. m_beamEndPosition = m_worldPosition;
  393. }
  394. }
  395. float t = m_t;
  396. bool hasAny = false;
  397. memset( hasParam, 0, sizeof(hasParam) );
  398. for ( int i = 0; i < STRIDERFX_PARAMETERS; i++ )
  399. {
  400. hasParam[i] = g_StriderCannonEnvelope.m_parameters[i].Interp( params[i], t );
  401. hasAny = hasAny || hasParam[i];
  402. }
  403. pixelvis_queryparams_t gunParams;
  404. gunParams.Init(m_worldPosition, 4.0f);
  405. float gunFractionVisible = PixelVisibility_FractionVisible( gunParams, &m_queryHandleGun );
  406. bool gunVisible = gunFractionVisible > 0.0f ? true : false;
  407. // draw the narrow beam
  408. if ( hasParam[STRIDERFX_NARROW_BEAM_COLOR] && hasParam[STRIDERFX_NARROW_BEAM_SIZE] )
  409. {
  410. IMaterial *pMat = materials->FindMaterial( "sprites/bluelaser1", TEXTURE_GROUP_CLIENT_EFFECTS );
  411. float width = NARROW_BEAM_WIDTH * params[STRIDERFX_NARROW_BEAM_SIZE].x;
  412. color32 color;
  413. float bright = params[STRIDERFX_NARROW_BEAM_COLOR].x;
  414. ScaleColor( color, white, bright );
  415. Strider_DrawLine( m_beamEndPosition, m_targetPosition, width, pMat, color );
  416. }
  417. // draw the wide beam
  418. if ( hasParam[STRIDERFX_WIDE_BEAM_COLOR] && hasParam[STRIDERFX_WIDE_BEAM_SIZE] )
  419. {
  420. IMaterial *pMat = materials->FindMaterial( "effects/blueblacklargebeam", TEXTURE_GROUP_CLIENT_EFFECTS );
  421. float width = WIDE_BEAM_WIDTH * params[STRIDERFX_WIDE_BEAM_SIZE].x;
  422. color32 color;
  423. float bright = params[STRIDERFX_WIDE_BEAM_COLOR].x;
  424. ScaleColor( color, white, bright );
  425. Vector wideBeamEnd = m_beamEndPosition;
  426. if ( hasParam[STRIDERFX_WIDE_BEAM_LENGTH] )
  427. {
  428. float amt = params[STRIDERFX_WIDE_BEAM_LENGTH].x;
  429. wideBeamEnd = m_beamEndPosition * amt + m_targetPosition * (1-amt);
  430. }
  431. Strider_DrawLine( wideBeamEnd, m_targetPosition, width, pMat, color );
  432. }
  433. // after glow sprite
  434. bool updated = false;
  435. CMatRenderContextPtr pRenderContext( materials );
  436. // warpy sprite bit
  437. if ( hasParam[STRIDERFX_WARP_SCALE] && !hasParam[STRIDERFX_BUBBLE_SIZE] && gunVisible )
  438. {
  439. if ( !updated )
  440. {
  441. updated = true;
  442. pRenderContext->Flush();
  443. UpdateRefractTexture();
  444. }
  445. IMaterial *pMat = materials->FindMaterial( "effects/strider_pinch_dudv", TEXTURE_GROUP_CLIENT_EFFECTS );
  446. float size = WARP_SIZE;
  447. float refract = params[STRIDERFX_WARP_SCALE].x * WARP_REFRACT * gunFractionVisible;
  448. pRenderContext->Bind( pMat, (IClientRenderable*)this );
  449. IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL );
  450. pVar->SetFloatValue( refract );
  451. Strider_DrawSprite( m_worldPosition, size, white );
  452. }
  453. // darkening sprite
  454. // glowy blue flare sprite
  455. if ( hasParam[STRIDERFX_FLARE_COLOR] && hasParam[STRIDERFX_FLARE_SIZE] && hasParam[STRIDERFX_DARKNESS] && gunVisible )
  456. {
  457. IMaterial *pMat = materials->FindMaterial( "effects/blueblackflash", TEXTURE_GROUP_CLIENT_EFFECTS );
  458. float size = FLARE_SIZE * params[STRIDERFX_FLARE_SIZE].x;
  459. color32 color;
  460. float bright = params[STRIDERFX_FLARE_COLOR].x * gunFractionVisible;
  461. ScaleColor( color, white, bright );
  462. color.a = (int)(255 * params[STRIDERFX_DARKNESS].x);
  463. pRenderContext->Bind( pMat, (IClientRenderable*)this );
  464. Strider_DrawSprite( m_worldPosition, size, color );
  465. }
  466. // bubble warpy sprite
  467. if ( hasParam[STRIDERFX_BUBBLE_SIZE] )
  468. {
  469. Vector wideBeamEnd = m_beamEndPosition;
  470. if ( hasParam[STRIDERFX_WIDE_BEAM_LENGTH] )
  471. {
  472. float amt = params[STRIDERFX_WIDE_BEAM_LENGTH].x;
  473. wideBeamEnd = m_beamEndPosition * amt + m_targetPosition * (1-amt);
  474. }
  475. pixelvis_queryparams_t endParams;
  476. endParams.Init(wideBeamEnd, 4.0f, 0.001f);
  477. float endFractionVisible = PixelVisibility_FractionVisible( endParams, &m_queryHandleBeamEnd );
  478. bool endVisible = endFractionVisible > 0.0f ? true : false;
  479. if ( endVisible )
  480. {
  481. if ( !updated )
  482. {
  483. updated = true;
  484. pRenderContext->Flush();
  485. UpdateRefractTexture();
  486. }
  487. IMaterial *pMat = materials->FindMaterial( "effects/strider_bulge_dudv", TEXTURE_GROUP_CLIENT_EFFECTS );
  488. float refract = endFractionVisible * WARP_BUBBLE_REFRACT * params[STRIDERFX_BUBBLE_REFRACT].x;
  489. float size = WARP_BUBBLE_SIZE * params[STRIDERFX_BUBBLE_SIZE].x;
  490. IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL );
  491. pVar->SetFloatValue( refract );
  492. pRenderContext->Bind( pMat, (IClientRenderable*)this );
  493. Strider_DrawSprite( wideBeamEnd, size, white );
  494. }
  495. }
  496. else
  497. {
  498. // call this to have the check ready on the first frame
  499. pixelvis_queryparams_t endParams;
  500. endParams.Init(m_beamEndPosition, 4.0f, 0.001f);
  501. PixelVisibility_FractionVisible( endParams, &m_queryHandleBeamEnd );
  502. }
  503. if ( hasParam[STRIDERFX_AFTERGLOW_COLOR] && gunVisible )
  504. {
  505. IMaterial *pMat = materials->FindMaterial( "effects/blueblackflash", TEXTURE_GROUP_CLIENT_EFFECTS );
  506. float size = AFTERGLOW_SIZE;// * params[STRIDERFX_FLARE_SIZE].x;
  507. color32 color;
  508. float bright = params[STRIDERFX_AFTERGLOW_COLOR].x * gunFractionVisible;
  509. ScaleColor( color, white, bright );
  510. pRenderContext->Bind( pMat, (IClientRenderable*)this );
  511. Strider_DrawSprite( m_worldPosition, size, color );
  512. dlight_t *dl = effects->CL_AllocDlight( m_entityIndex );
  513. dl->origin = m_worldPosition;
  514. dl->color.r = 40;
  515. dl->color.g = 60;
  516. dl->color.b = 255;
  517. dl->color.exponent = 5;
  518. dl->radius = bright * 128;
  519. dl->die = gpGlobals->curtime + 0.001;
  520. }
  521. if ( m_t >= STRIDERFX_END_ALL_TIME && !hasAny )
  522. {
  523. EffectShutdown();
  524. }
  525. return 1;
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose: Strider class implementation
  529. //-----------------------------------------------------------------------------
  530. C_Strider::C_Strider() :
  531. m_iv_vecHitPos("C_Strider::m_iv_vecHitPos"),
  532. m_iv_vecIKTarget("C_Strider::m_iv_vecIKTarget")
  533. {
  534. AddVar( &m_vecHitPos, &m_iv_vecHitPos, LATCH_ANIMATION_VAR );
  535. memset(m_vecIKTarget, 0, sizeof(m_vecIKTarget));
  536. AddVar( &m_vecIKTarget, &m_iv_vecIKTarget, LATCH_ANIMATION_VAR );
  537. m_flNextRopeCutTime = 0;
  538. }
  539. C_Strider::~C_Strider()
  540. {
  541. }
  542. void C_Strider::ReceiveMessage( int classID, bf_read &msg )
  543. {
  544. if ( classID != GetClientClass()->m_ClassID )
  545. {
  546. // message is for subclass
  547. BaseClass::ReceiveMessage( classID, msg );
  548. return;
  549. }
  550. int messageType = msg.ReadByte();
  551. switch( messageType )
  552. {
  553. case STRIDER_MSG_STREAKS:
  554. {
  555. Vector pos;
  556. msg.ReadBitVec3Coord( pos );
  557. m_cannonFX.SetRenderOrigin( pos );
  558. m_cannonFX.EffectInit( entindex(), LookupAttachment( "BigGun" ) );
  559. m_cannonFX.LimitTime( STRIDERFX_BIG_SHOT_TIME );
  560. }
  561. break;
  562. case STRIDER_MSG_BIG_SHOT:
  563. {
  564. Vector tmp;
  565. msg.ReadBitVec3Coord( tmp );
  566. m_cannonFX.SetTime( STRIDERFX_BIG_SHOT_TIME );
  567. m_cannonFX.LimitTime( STRIDERFX_END_ALL_TIME );
  568. }
  569. break;
  570. case STRIDER_MSG_DEAD:
  571. {
  572. m_cannonFX.EffectShutdown();
  573. }
  574. break;
  575. }
  576. }
  577. void C_Strider::OnDataChanged( DataUpdateType_t updateType )
  578. {
  579. if ( updateType == DATA_UPDATE_CREATED )
  580. {
  581. // We need to have our render bounds defined or shadow creation won't work correctly
  582. ClientThink();
  583. ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
  584. }
  585. BaseClass::OnDataChanged( updateType );
  586. m_cannonFX.Update( this, m_vecHitPos );
  587. }
  588. //-----------------------------------------------------------------------------
  589. // Purpose: Recompute my rendering box
  590. //-----------------------------------------------------------------------------
  591. void C_Strider::ClientThink()
  592. {
  593. // The reason why this is here, as opposed to in SetObjectCollisionBox,
  594. // is because of IK. The code below recomputes bones so as to get at the hitboxes,
  595. // which causes IK to trigger, which causes raycasts against the other entities to occur,
  596. // which is illegal to do while in the Relink phase.
  597. ComputeEntitySpaceHitboxSurroundingBox( &m_vecRenderMins, &m_vecRenderMaxs );
  598. // UNDONE: Disabled this until we can get closer to a final map and tune
  599. #if 0
  600. // Cut ropes.
  601. if ( gpGlobals->curtime >= m_flNextRopeCutTime )
  602. {
  603. // Blow the bbox out a little.
  604. Vector vExtendedMins = vecMins - Vector( 50, 50, 50 );
  605. Vector vExtendedMaxs = vecMaxs + Vector( 50, 50, 50 );
  606. C_RopeKeyframe *ropes[512];
  607. int nRopes = C_RopeKeyframe::GetRopesIntersectingAABB( ropes, ARRAYSIZE( ropes ), GetAbsOrigin() + vExtendedMins, GetAbsOrigin() + vExtendedMaxs );
  608. for ( int i=0; i < nRopes; i++ )
  609. {
  610. C_RopeKeyframe *pRope = ropes[i];
  611. if ( pRope->GetEndEntity() )
  612. {
  613. Vector vPos;
  614. if ( pRope->GetEndPointPos( 1, vPos ) )
  615. {
  616. // Detach the endpoint.
  617. pRope->SetEndEntity( NULL );
  618. // Make some spark effect here..
  619. g_pEffects->Sparks( vPos );
  620. }
  621. }
  622. }
  623. m_flNextRopeCutTime = gpGlobals->curtime + 0.5;
  624. }
  625. #endif
  626. // True argument because the origin may have stayed the same, but the size is expected to always change
  627. g_pClientShadowMgr->AddToDirtyShadowList( this, true );
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Purpose: Recompute my rendering box
  631. //-----------------------------------------------------------------------------
  632. void C_Strider::GetRenderBounds( Vector& theMins, Vector& theMaxs )
  633. {
  634. theMins = m_vecRenderMins;
  635. theMaxs = m_vecRenderMaxs;
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Strider muzzle flashes
  639. //-----------------------------------------------------------------------------
  640. void MuzzleFlash_Strider( ClientEntityHandle_t hEntity, int attachmentIndex )
  641. {
  642. VPROF_BUDGET( "MuzzleFlash_Strider", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  643. // If the client hasn't seen this entity yet, bail.
  644. matrix3x4_t matAttachment;
  645. if ( !FX_GetAttachmentTransform( hEntity, attachmentIndex, matAttachment ) )
  646. return;
  647. CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash_Strider", hEntity, attachmentIndex );
  648. SimpleParticle *pParticle;
  649. Vector forward(1,0,0), offset; //NOTENOTE: All coords are in local space
  650. float flScale = random->RandomFloat( 3.0f, 4.0f );
  651. float burstSpeed = random->RandomFloat( 400.0f, 600.0f );
  652. #define FRONT_LENGTH 12
  653. // Front flash
  654. for ( int i = 1; i < FRONT_LENGTH; i++ )
  655. {
  656. offset = (forward * (i*2.0f*flScale));
  657. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset );
  658. if ( pParticle == NULL )
  659. return;
  660. pParticle->m_flLifetime = 0.0f;
  661. pParticle->m_flDieTime = 0.1f;
  662. pParticle->m_vecVelocity = forward * burstSpeed;
  663. pParticle->m_uchColor[0] = 255;
  664. pParticle->m_uchColor[1] = 255;
  665. pParticle->m_uchColor[2] = 255;
  666. pParticle->m_uchStartAlpha = 255.0f;
  667. pParticle->m_uchEndAlpha = 0;
  668. pParticle->m_uchStartSize = ( (random->RandomFloat( 6.0f, 8.0f ) * (FRONT_LENGTH-(i))/(FRONT_LENGTH*0.75f)) * flScale );
  669. pParticle->m_uchEndSize = pParticle->m_uchStartSize;
  670. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  671. pParticle->m_flRollDelta = 0.0f;
  672. }
  673. Vector right(0,1,0), up(0,0,1);
  674. Vector dir = right - up;
  675. #define SIDE_LENGTH 8
  676. burstSpeed = random->RandomFloat( 400.0f, 600.0f );
  677. // Diagonal flash
  678. for ( int i = 1; i < SIDE_LENGTH; i++ )
  679. {
  680. offset = (dir * (i*flScale));
  681. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset );
  682. if ( pParticle == NULL )
  683. return;
  684. pParticle->m_flLifetime = 0.0f;
  685. pParticle->m_flDieTime = 0.2f;
  686. pParticle->m_vecVelocity = dir * burstSpeed * 0.25f;
  687. pParticle->m_uchColor[0] = 255;
  688. pParticle->m_uchColor[1] = 255;
  689. pParticle->m_uchColor[2] = 255;
  690. pParticle->m_uchStartAlpha = 255;
  691. pParticle->m_uchEndAlpha = 0;
  692. pParticle->m_uchStartSize = ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale );
  693. pParticle->m_uchEndSize = pParticle->m_uchStartSize;
  694. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  695. pParticle->m_flRollDelta = 0.0f;
  696. }
  697. dir = right + up;
  698. burstSpeed = random->RandomFloat( 400.0f, 600.0f );
  699. // Diagonal flash
  700. for ( int i = 1; i < SIDE_LENGTH; i++ )
  701. {
  702. offset = (-dir * (i*flScale));
  703. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset );
  704. if ( pParticle == NULL )
  705. return;
  706. pParticle->m_flLifetime = 0.0f;
  707. pParticle->m_flDieTime = 0.2f;
  708. pParticle->m_vecVelocity = dir * -burstSpeed * 0.25f;
  709. pParticle->m_uchColor[0] = 255;
  710. pParticle->m_uchColor[1] = 255;
  711. pParticle->m_uchColor[2] = 255;
  712. pParticle->m_uchStartAlpha = 255;
  713. pParticle->m_uchEndAlpha = 0;
  714. pParticle->m_uchStartSize = ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale );
  715. pParticle->m_uchEndSize = pParticle->m_uchStartSize;
  716. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  717. pParticle->m_flRollDelta = 0.0f;
  718. }
  719. dir = up;
  720. burstSpeed = random->RandomFloat( 400.0f, 600.0f );
  721. // Top flash
  722. for ( int i = 1; i < SIDE_LENGTH; i++ )
  723. {
  724. offset = (dir * (i*flScale));
  725. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset );
  726. if ( pParticle == NULL )
  727. return;
  728. pParticle->m_flLifetime = 0.0f;
  729. pParticle->m_flDieTime = 0.2f;
  730. pParticle->m_vecVelocity = dir * burstSpeed * 0.25f;
  731. pParticle->m_uchColor[0] = 255;
  732. pParticle->m_uchColor[1] = 255;
  733. pParticle->m_uchColor[2] = 255;
  734. pParticle->m_uchStartAlpha = 255;
  735. pParticle->m_uchEndAlpha = 0;
  736. pParticle->m_uchStartSize = ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale );
  737. pParticle->m_uchEndSize = pParticle->m_uchStartSize;
  738. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  739. pParticle->m_flRollDelta = 0.0f;
  740. }
  741. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/strider_muzzle" ), vec3_origin );
  742. if ( pParticle == NULL )
  743. return;
  744. pParticle->m_flLifetime = 0.0f;
  745. pParticle->m_flDieTime = random->RandomFloat( 0.3f, 0.4f );
  746. pParticle->m_vecVelocity.Init();
  747. pParticle->m_uchColor[0] = 255;
  748. pParticle->m_uchColor[1] = 255;
  749. pParticle->m_uchColor[2] = 255;
  750. pParticle->m_uchStartAlpha = 255;
  751. pParticle->m_uchEndAlpha = 0;
  752. pParticle->m_uchStartSize = flScale * random->RandomFloat( 12.0f, 16.0f );
  753. pParticle->m_uchEndSize = 0.0f;
  754. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  755. pParticle->m_flRollDelta = 0.0f;
  756. Vector origin;
  757. MatrixGetColumn( matAttachment, 3, &origin );
  758. int entityIndex = ClientEntityList().HandleToEntIndex( hEntity );
  759. if ( entityIndex >= 0 )
  760. {
  761. dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + entityIndex );
  762. el->origin = origin;
  763. el->color.r = 64;
  764. el->color.g = 128;
  765. el->color.b = 255;
  766. el->color.exponent = 5;
  767. el->radius = random->RandomInt( 100, 150 );
  768. el->decay = el->radius / 0.05f;
  769. el->die = gpGlobals->curtime + 0.1f;
  770. }
  771. }
  772. //-----------------------------------------------------------------------------
  773. // Purpose:
  774. // Input : &data -
  775. //-----------------------------------------------------------------------------
  776. void StriderMuzzleFlashCallback( const CEffectData &data )
  777. {
  778. MuzzleFlash_Strider( data.m_hEntity, data.m_nAttachmentIndex );
  779. }
  780. DECLARE_CLIENT_EFFECT( "StriderMuzzleFlash", StriderMuzzleFlashCallback );
  781. #define BLOOD_MIN_SPEED 64.0f*2.0f
  782. #define BLOOD_MAX_SPEED 256.0f*8.0f
  783. //-----------------------------------------------------------------------------
  784. // Purpose:
  785. // Input : &origin -
  786. // &normal -
  787. // scale -
  788. //-----------------------------------------------------------------------------
  789. void StriderBlood( const Vector &origin, const Vector &normal, float scale )
  790. {
  791. VPROF_BUDGET( "StriderBlood", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  792. //Find area ambient light color and use it to tint smoke
  793. Vector worldLight = WorldGetLightForPoint( origin, true );
  794. Vector tint;
  795. float luminosity;
  796. UTIL_GetNormalizedColorTintAndLuminosity( worldLight, &tint, &luminosity );
  797. // We only take a portion of the tint
  798. tint = (tint * 0.25f)+(Vector(0.75f,0.75f,0.75f));
  799. // Rescale to a character range
  800. luminosity = MAX( 200, luminosity*255 );
  801. CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
  802. pSimple->SetSortOrigin( origin );
  803. int i;
  804. float flScale = scale / 8.0f;
  805. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/slime1" );
  806. float length = 0.2f;
  807. Vector vForward, vRight, vUp;
  808. Vector offDir;
  809. TrailParticle *tParticle;
  810. CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "splash" );
  811. if ( !sparkEmitter )
  812. return;
  813. sparkEmitter->SetSortOrigin( origin );
  814. sparkEmitter->m_ParticleCollision.SetGravity( 600.0f );
  815. sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
  816. sparkEmitter->SetVelocityDampen( 2.0f );
  817. //Dump out drops
  818. Vector offset;
  819. for ( i = 0; i < 64; i++ )
  820. {
  821. offset = origin;
  822. offset[0] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
  823. offset[1] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
  824. offset[2] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
  825. tParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
  826. if ( tParticle == NULL )
  827. break;
  828. tParticle->m_flLifetime = 0.0f;
  829. tParticle->m_flDieTime = 1.0f;
  830. offDir = normal + RandomVector( -1.0f, 1.0f );
  831. tParticle->m_vecVelocity = offDir * random->RandomFloat( BLOOD_MIN_SPEED * flScale * 2.0f, BLOOD_MAX_SPEED * flScale * 2.0f );
  832. tParticle->m_vecVelocity[2] += random->RandomFloat( 8.0f, 32.0f ) * flScale;
  833. tParticle->m_flWidth = random->RandomFloat( 20.0f, 26.0f ) * flScale;
  834. tParticle->m_flLength = random->RandomFloat( length*0.5f, length ) * flScale;
  835. int nColor = random->RandomInt( luminosity*0.75f, luminosity );
  836. tParticle->m_color.r = nColor * tint.x;
  837. tParticle->m_color.g = nColor * tint.y;
  838. tParticle->m_color.b = nColor * tint.z;
  839. tParticle->m_color.a = 255;
  840. }
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose:
  844. // Input : &data -
  845. //-----------------------------------------------------------------------------
  846. void StriderBloodCallback( const CEffectData &data )
  847. {
  848. StriderBlood( data.m_vOrigin, data.m_vNormal, data.m_flScale );
  849. }
  850. DECLARE_CLIENT_EFFECT( "StriderBlood", StriderBloodCallback );