Counter Strike : Global Offensive Source Code
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.

1170 lines
32 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ivieweffects.h"
  9. #include "shake.h"
  10. #include "hud_macros.h"
  11. #include "isaverestore.h"
  12. #include "view_shared.h"
  13. #include "iviewrender.h"
  14. #include "viewrender.h"
  15. #include "con_nprint.h"
  16. #include "saverestoretypes.h"
  17. #include "c_rumble.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. extern IntroData_t *g_pIntroData;
  21. // Arbitrary limit so that bad entity logic on the server can't consume tons of memory on the client.
  22. #define MAX_SHAKES 32
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Screen fade variables
  25. //-----------------------------------------------------------------------------
  26. struct screenfade_t
  27. {
  28. float Speed; // How fast to fade (tics / second) (+ fade in, - fade out)
  29. float End; // When the fading hits maximum
  30. float Reset; // When to reset to not fading (for fadeout and hold)
  31. byte r, g, b, alpha; // Fade color
  32. int Flags; // Fading flags
  33. DECLARE_SIMPLE_DATADESC();
  34. };
  35. BEGIN_SIMPLE_DATADESC( screenfade_t )
  36. DEFINE_FIELD( Speed, FIELD_FLOAT ),
  37. DEFINE_FIELD( End, FIELD_TIME ),
  38. DEFINE_FIELD( Reset, FIELD_TIME ),
  39. DEFINE_FIELD( r, FIELD_CHARACTER ),
  40. DEFINE_FIELD( g, FIELD_CHARACTER ),
  41. DEFINE_FIELD( b, FIELD_CHARACTER ),
  42. DEFINE_FIELD( alpha, FIELD_CHARACTER ),
  43. DEFINE_FIELD( Flags, FIELD_INTEGER ),
  44. END_DATADESC()
  45. //-----------------------------------------------------------------------------
  46. // Purpose: Screen shake variables
  47. //-----------------------------------------------------------------------------
  48. struct screenshake_t
  49. {
  50. float endtime;
  51. float duration;
  52. float amplitude;
  53. float frequency;
  54. float nextShake;
  55. Vector offset;
  56. float angle;
  57. int command;
  58. Vector direction; // used only by kSHAKE_DIRECTIONAL
  59. // there are different types of screenshake --
  60. // eventually these different types could become
  61. // proper classes, but given the existing infrastructure
  62. // for transmitting screenshakes, right now it's just
  63. // easier to use an enum and switch.
  64. enum ShakeType_t
  65. {
  66. kSHAKE_BASIC, ///< the original screenshake mechanism, a random offset selected every few frames.
  67. kSHAKE_DIRECTIONAL, ///< a pseudo-damped-spring-ish punch to a specific screen space direction.
  68. };
  69. uint8 nShakeType; // actually a ShakeType_t, packed into eight bits (the datadesc system doesn't like bitfields)
  70. screenshake_t() : nShakeType(kSHAKE_BASIC) {}; // nothing else is explicitly initialized
  71. DECLARE_SIMPLE_DATADESC();
  72. };
  73. BEGIN_SIMPLE_DATADESC( screenshake_t )
  74. DEFINE_FIELD( endtime, FIELD_TIME ),
  75. DEFINE_FIELD( duration, FIELD_FLOAT ),
  76. DEFINE_FIELD( amplitude, FIELD_FLOAT ),
  77. DEFINE_FIELD( frequency, FIELD_FLOAT ),
  78. DEFINE_FIELD( nextShake, FIELD_TIME ),
  79. DEFINE_FIELD( offset, FIELD_VECTOR ),
  80. DEFINE_FIELD( angle, FIELD_FLOAT ),
  81. DEFINE_FIELD( nShakeType, FIELD_CHARACTER ),
  82. DEFINE_FIELD( direction, FIELD_VECTOR ),
  83. END_DATADESC()
  84. void CC_Shake_Stop();
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Screen tilt variables
  87. //-----------------------------------------------------------------------------
  88. struct screentilt_t
  89. {
  90. bool easeInOut;
  91. QAngle angle;
  92. float starttime;
  93. float endtime;
  94. float duration;
  95. float tiltTime;
  96. Vector offset;
  97. int command;
  98. DECLARE_SIMPLE_DATADESC();
  99. };
  100. BEGIN_SIMPLE_DATADESC( screentilt_t )
  101. DEFINE_FIELD( angle, FIELD_VECTOR ),
  102. DEFINE_FIELD( starttime, FIELD_TIME ),
  103. DEFINE_FIELD( endtime, FIELD_TIME ),
  104. DEFINE_FIELD( duration, FIELD_FLOAT ),
  105. DEFINE_FIELD( tiltTime, FIELD_TIME ),
  106. DEFINE_FIELD( offset, FIELD_VECTOR ),
  107. END_DATADESC()
  108. //-----------------------------------------------------------------------------
  109. // Purpose: Implements the view effects interface for the client .dll
  110. //-----------------------------------------------------------------------------
  111. class CViewEffects : public IViewEffects
  112. {
  113. public:
  114. ~CViewEffects()
  115. {
  116. ClearAllFades();
  117. }
  118. virtual void Init( void );
  119. virtual void LevelInit( void );
  120. virtual void GetFadeParams( byte *r, byte *g, byte *b, byte *a, bool *blend );
  121. virtual void CalcShake( void );
  122. virtual void ApplyShake( Vector& origin, QAngle& angles, float factor );
  123. virtual void CalcTilt( void );
  124. virtual void ApplyTilt( QAngle& angles, float factor );
  125. virtual void Shake( const ScreenShake_t &data );
  126. virtual void Tilt( ScreenTilt_t &data );
  127. virtual void Fade( ScreenFade_t &data );
  128. virtual void ClearPermanentFades( void );
  129. virtual void FadeCalculate( void );
  130. virtual void ClearAllFades( void );
  131. // Save / Restore
  132. virtual void Save( ISave *pSave );
  133. virtual void Restore( IRestore *pRestore, bool fCreatePlayers );
  134. CUserMessageBinder m_UMCMsgShake;
  135. CUserMessageBinder m_UMCMsgFade;
  136. private:
  137. void ClearAllShakes();
  138. screenshake_t *FindLongestShake();
  139. void ClearAllTilts();
  140. screentilt_t *FindLongestTilt();
  141. // helper subfunctions used inside CalcShake
  142. void CalcShake_Basic( screenshake_t * pShake, float * RESTRICT pflRumbleAngle );
  143. void CalcShake_Directional( screenshake_t * pShake, float * RESTRICT pflRumbleAngle );
  144. CUtlVector<screenfade_t *> m_FadeList;
  145. CUtlVector<screenshake_t *> m_ShakeList;
  146. Vector m_vecShakeAppliedOffset;
  147. float m_flShakeAppliedAngle;
  148. CUtlVector<screentilt_t *> m_TiltList;
  149. QAngle m_vecTiltAppliedAngle;
  150. int m_FadeColorRGBA[4];
  151. bool m_bModulate;
  152. friend void CC_Shake_Stop();
  153. };
  154. static CViewEffects g_ViewEffects[ MAX_SPLITSCREEN_PLAYERS ];
  155. IViewEffects *GetViewEffects()
  156. {
  157. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  158. return &g_ViewEffects[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
  159. }
  160. static CViewEffects &GetCViewEffects()
  161. {
  162. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  163. return g_ViewEffects[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
  164. }
  165. // Callback function to call at end of screen m_Fade.
  166. static int s_nCallbackParameter;
  167. static void ( *s_pfnFadeDoneCallback )( int parm1 );
  168. //-----------------------------------------------------------------------------
  169. // Purpose:
  170. // Input : *pszName -
  171. // iSize -
  172. // *pbuf -
  173. // Output : static int
  174. //-----------------------------------------------------------------------------
  175. bool __MsgFunc_Shake( const CCSUsrMsg_Shake &msg )
  176. {
  177. ScreenShake_t shake;
  178. shake.command = (ShakeCommand_t)(msg.command());
  179. shake.amplitude = msg.local_amplitude();
  180. shake.frequency = msg.frequency();
  181. shake.duration = msg.duration();
  182. GetCViewEffects().Shake( shake );
  183. return true;
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. // Input : *pszName -
  188. // iSize -
  189. // *pbuf -
  190. // Output : static int
  191. //-----------------------------------------------------------------------------
  192. void __MsgFunc_ShakeDir( bf_read &msg )
  193. {
  194. ScreenShake_t shake;
  195. shake.command = (ShakeCommand_t)msg.ReadByte();
  196. shake.amplitude = msg.ReadFloat();
  197. shake.frequency = msg.ReadFloat();
  198. shake.duration = msg.ReadFloat();
  199. msg.ReadBitVec3Normal( shake.direction );
  200. GetCViewEffects().Shake( shake );
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. // Input : *pszName -
  205. // iSize -
  206. // *pbuf -
  207. // Output : static int
  208. //-----------------------------------------------------------------------------
  209. void __MsgFunc_Tilt( bf_read &msg )
  210. {
  211. ScreenTilt_t tilt;
  212. Vector vecAngle;
  213. tilt.command = msg.ReadByte();
  214. tilt.easeInOut = msg.ReadByte() ? true : false;
  215. tilt.angle.x = msg.ReadFloat();
  216. tilt.angle.y = msg.ReadFloat();
  217. tilt.angle.z = msg.ReadFloat();
  218. tilt.duration = msg.ReadFloat();
  219. tilt.time = msg.ReadFloat();
  220. GetCViewEffects().Tilt( tilt );
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose:
  224. // Input : *pszName -
  225. // iSize -
  226. // *pbuf -
  227. // Output : static int
  228. //-----------------------------------------------------------------------------
  229. bool __MsgFunc_Fade( const CCSUsrMsg_Fade &msg )
  230. {
  231. ScreenFade_t fade;
  232. fade.duration = msg.duration(); // fade lasts this long
  233. fade.holdTime = msg.hold_time(); // fade lasts this long
  234. fade.fadeFlags = msg.flags(); // fade type (in / out)
  235. fade.r = msg.clr().r(); // fade red
  236. fade.g = msg.clr().g(); // fade green
  237. fade.b = msg.clr().b(); // fade blue
  238. fade.a = msg.clr().a(); // fade blue
  239. GetCViewEffects().Fade( fade );
  240. return true;
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. //-----------------------------------------------------------------------------
  245. void CViewEffects::Init( void )
  246. {
  247. HOOK_MESSAGE( Shake );
  248. #ifdef INFESTED_DLL // the user message ShakeDir isn't registered for other games, but if you add it to your RegisterUserMessages, then you can un-#ifdef this
  249. HOOK_MESSAGE( ShakeDir ); // directional screen shake
  250. #endif
  251. #ifdef HL2_CLIENT
  252. // @TODO: Jeep, this causes assert in other games w/o this guard ifdef [6/3/2008 tom]
  253. HOOK_MESSAGE( Tilt );
  254. #endif
  255. HOOK_MESSAGE( Fade );
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose:
  259. //-----------------------------------------------------------------------------
  260. void CViewEffects::LevelInit( void )
  261. {
  262. ClearAllShakes();
  263. ClearAllTilts();
  264. ClearAllFades();
  265. }
  266. static ConVar shake_show( "shake_show", "0", 0, "Displays a list of the active screen shakes." );
  267. //-----------------------------------------------------------------------------
  268. // Purpose: Stops all active screen shakes.
  269. //-----------------------------------------------------------------------------
  270. void CC_Shake_Stop()
  271. {
  272. GetCViewEffects().ClearAllShakes();
  273. }
  274. static ConCommand shake_stop("shake_stop", CC_Shake_Stop, "Stops all active screen shakes.\n", FCVAR_CHEAT );
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Test a punch-type screen shake
  277. //-----------------------------------------------------------------------------
  278. void CC_Shake_TestPunch( const CCommand &args )
  279. {
  280. if ( args.ArgC() < 7 )
  281. {
  282. Msg("Usage: %s x y z f a d\n"
  283. "where x,y,z are direction of screen punch\n"
  284. " f is frequency (1 means three bounces before settling)\n"
  285. " a is amplitude\n"
  286. " d is duration\n",
  287. args[0] );
  288. }
  289. const float x = atof( args[1] );
  290. const float y = atof( args[2] );
  291. const float z = atof( args[3] );
  292. const float f = atof( args[4] );
  293. const float a = atof( args[5] );
  294. const float d = atof( args[6] );
  295. ScreenShake_t shake;
  296. shake.command = SHAKE_START;
  297. shake.amplitude = a;
  298. shake.frequency = f;
  299. shake.duration = d;
  300. shake.direction = Vector(x,y,z);
  301. GetCViewEffects().Shake(shake);
  302. }
  303. static ConCommand shake_testpunch("shake_testpunch", CC_Shake_TestPunch, "Test a punch-style screen shake.\n", FCVAR_CHEAT );
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Apply noise to the eye position.
  306. // UNDONE: Feedback a bit of this into the view model position. It shakes too much
  307. //-----------------------------------------------------------------------------
  308. void CViewEffects::CalcShake( void )
  309. {
  310. // We'll accumulate the aggregate shake for this frame into these data members.
  311. m_vecShakeAppliedOffset.Init(0, 0, 0);
  312. m_flShakeAppliedAngle = 0;
  313. float flRumbleAngle = 0;
  314. bool bShow = shake_show.GetBool();
  315. int nShakeCount = m_ShakeList.Count();
  316. for ( int nShake = nShakeCount - 1; nShake >= 0; nShake-- )
  317. {
  318. screenshake_t * RESTRICT pShake = m_ShakeList.Element( nShake );
  319. if ( pShake->endtime == 0 )
  320. {
  321. // Shouldn't be any such shakes in the list.
  322. AssertMsg( false, "A screenshake has null endtime in CViewEffects::CalcShake\n" );
  323. continue;
  324. }
  325. if ( ( gpGlobals->curtime > pShake->endtime ) ||
  326. pShake->duration <= 0 ||
  327. pShake->amplitude <= 0 ||
  328. pShake->frequency <= 0 )
  329. {
  330. // Retire this shake.
  331. delete m_ShakeList.Element( nShake );
  332. m_ShakeList.FastRemove( nShake );
  333. continue;
  334. }
  335. if ( bShow )
  336. {
  337. con_nprint_t np;
  338. np.time_to_live = 2.0f;
  339. np.fixed_width_font = true;
  340. np.color[0] = 1.0;
  341. np.color[1] = 0.8;
  342. np.color[2] = 0.1;
  343. np.index = nShake + 2;
  344. engine->Con_NXPrintf( &np, "%02d: dur(%8.2f) amp(%8.2f) freq(%8.2f)", nShake + 1, (double)pShake->duration, (double)pShake->amplitude, (double)pShake->frequency );
  345. }
  346. // select the appropriate behavior based on screenshake type
  347. switch ( pShake->nShakeType )
  348. {
  349. case screenshake_t::kSHAKE_BASIC:
  350. CalcShake_Basic( pShake, &flRumbleAngle );
  351. break;
  352. case screenshake_t::kSHAKE_DIRECTIONAL:
  353. CalcShake_Directional( pShake, &flRumbleAngle );
  354. break;
  355. default:
  356. AssertMsg1( false, "Unknown shake type %d\n", pShake->nShakeType );
  357. }
  358. }
  359. // Feed this to the rumble system!
  360. UpdateScreenShakeRumble( XBX_GetActiveUserId(), flRumbleAngle );
  361. }
  362. void CViewEffects::CalcShake_Basic( screenshake_t * RESTRICT pShake, float * RESTRICT pflRumbleAngle )
  363. {
  364. float fraction, freq;
  365. if ( gpGlobals->curtime > pShake->nextShake )
  366. {
  367. // Higher frequency means we recalc the extents more often and perturb the display again
  368. pShake->nextShake = gpGlobals->curtime + (1.0f / pShake->frequency);
  369. // Compute random shake extents (the shake will settle down from this)
  370. for (int i = 0; i < 3; i++ )
  371. {
  372. pShake->offset[i] = random->RandomFloat( -pShake->amplitude, pShake->amplitude );
  373. }
  374. pShake->angle = random->RandomFloat( -pShake->amplitude*0.25, pShake->amplitude*0.25 );
  375. }
  376. // Ramp down amplitude over duration (fraction goes from 1 to 0 linearly with slope 1/duration)
  377. fraction = ( pShake->endtime - gpGlobals->curtime ) / pShake->duration;
  378. // Ramp up frequency over duration
  379. if ( fraction )
  380. {
  381. freq = (pShake->frequency / fraction);
  382. }
  383. else
  384. {
  385. freq = 0;
  386. }
  387. // square fraction to approach zero more quickly
  388. fraction *= fraction;
  389. // Sine wave that slowly settles to zero
  390. float angle = gpGlobals->curtime * freq;
  391. if ( angle > 1e8 )
  392. {
  393. angle = 1e8;
  394. }
  395. fraction = fraction * sin( angle );
  396. if( pShake->command != SHAKE_START_NORUMBLE )
  397. {
  398. // As long as this isn't a NO RUMBLE effect, then accumulate rumble
  399. *pflRumbleAngle += pShake->angle * fraction;
  400. }
  401. if( pShake->command != SHAKE_START_RUMBLEONLY )
  402. {
  403. // As long as this isn't a RUMBLE ONLY effect, then accumulate screen shake
  404. // Add to view origin
  405. m_vecShakeAppliedOffset += pShake->offset * fraction;
  406. // Add to roll
  407. m_flShakeAppliedAngle += pShake->angle * fraction;
  408. }
  409. // Drop amplitude a bit, less for higher frequency shakes
  410. pShake->amplitude -= pShake->amplitude * ( gpGlobals->frametime / (pShake->duration * pShake->frequency) );
  411. }
  412. void CViewEffects::CalcShake_Directional( screenshake_t * RESTRICT pShake, float * RESTRICT pflRumbleAngle )
  413. {
  414. // a screen punch follows an equation of the form
  415. // y = sin(fx) * ( 1 - x / (3pi) ) for x=0..3pi
  416. // where the duration is transformed to occupy
  417. // the region 0..3pi
  418. // and the frequency can be any number (which controls the number of oscillations
  419. // before lapsing out)
  420. // because of the resolution of this shake, it is performed every frame
  421. // (ignores nextShake)
  422. pShake->nextShake = gpGlobals->curtime + 0.001;
  423. float t = 1 - ( pShake->endtime - gpGlobals->curtime ) / pShake->duration; // t varies 0 .. 1 over life of shake
  424. float fraction = ( 1 - t ); // compiler will hopefully elide the double subtraction
  425. // transform the duration and so that x varies 0 .. 3PI over lifespan
  426. t *= ( 3 * M_PI ); // t varies 0 .. 3PI
  427. const float x = t * pShake->frequency;
  428. const float y = sin(x) * fraction;
  429. // transform this -1..1 sinusoid by amplitude and direction
  430. pShake->offset = pShake->direction * ( pShake->amplitude * y );
  431. if( pShake->command != SHAKE_START_NORUMBLE )
  432. {
  433. // As long as this isn't a NO RUMBLE effect, then accumulate rumble
  434. *pflRumbleAngle += y;
  435. }
  436. if( pShake->command != SHAKE_START_RUMBLEONLY )
  437. {
  438. // As long as this isn't a RUMBLE ONLY effect, then accumulate screen shake
  439. // Add to view origin
  440. m_vecShakeAppliedOffset += pShake->offset ;
  441. }
  442. }
  443. //-----------------------------------------------------------------------------
  444. // Purpose: Apply the current screen shake to this origin/angles. Factor is the amount to apply
  445. // This is so you can blend in part of the shake
  446. // Input : origin -
  447. // angles -
  448. // factor -
  449. //-----------------------------------------------------------------------------
  450. void CViewEffects::ApplyShake( Vector& origin, QAngle& angles, float factor )
  451. {
  452. VectorMA( origin, factor, m_vecShakeAppliedOffset, origin );
  453. angles.z += m_flShakeAppliedAngle * factor;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: Apply noise to the eye position.
  457. // UNDONE: Feedback a bit of this into the view model position. It shakes too much
  458. //-----------------------------------------------------------------------------
  459. void CViewEffects::CalcTilt( void )
  460. {
  461. m_vecTiltAppliedAngle.Init();
  462. int nTiltCount = m_TiltList.Count();
  463. for ( int nTilt = nTiltCount - 1; nTilt >= 0; nTilt-- )
  464. {
  465. screentilt_t *pTilt = m_TiltList.Element( nTilt );
  466. if ( pTilt->endtime == 0 )
  467. {
  468. // Shouldn't be any such tilts in the list.
  469. Assert( false );
  470. continue;
  471. }
  472. if ( ( gpGlobals->curtime > pTilt->endtime ) ||
  473. pTilt->duration <= 0 || pTilt->angle == QAngle( 0.0f, 0.0f, 0.0f ) )
  474. {
  475. // Retire this tilt.
  476. delete m_TiltList.Element( nTilt );
  477. m_TiltList.FastRemove( nTilt );
  478. continue;
  479. }
  480. float flInterp = ( gpGlobals->curtime - pTilt->starttime ) / pTilt->tiltTime;
  481. float flReturnInterp = ( pTilt->endtime - gpGlobals->curtime ) / pTilt->tiltTime;
  482. if ( flReturnInterp < flInterp )
  483. {
  484. flInterp = flReturnInterp;
  485. }
  486. float flSmoothInterp = clamp( flInterp, 0.0f, 1.0f );
  487. if ( pTilt->easeInOut )
  488. {
  489. // Do a smooth ease in and out
  490. flSmoothInterp = 1.0f - 0.5f * ( cosf( flSmoothInterp * M_PI ) + 1.0f );
  491. }
  492. // Accumulate world tilt
  493. m_vecTiltAppliedAngle += pTilt->angle * flSmoothInterp;
  494. }
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose: Apply the current screen shake to this origin/angles. Factor is the amount to apply
  498. // This is so you can blend in part of the shake
  499. // Input : origin -
  500. // angles -
  501. // factor -
  502. //-----------------------------------------------------------------------------
  503. void CViewEffects::ApplyTilt( QAngle& angles, float factor )
  504. {
  505. if ( m_vecTiltAppliedAngle == QAngle( 0.0f, 0.0f, 0.0f ) )
  506. {
  507. // Fast out, no tilt to apply
  508. return;
  509. }
  510. matrix3x4_t matTilt;
  511. AngleIMatrix( m_vecTiltAppliedAngle, matTilt );
  512. matrix3x4_t matToWorld;
  513. AngleMatrix( angles, matToWorld );
  514. matrix3x4_t matTiltToWorld;
  515. ConcatTransforms( matTilt, matToWorld, matTiltToWorld);
  516. Vector vecForwardTilted, vecUpTilted;
  517. VectorTransform( Vector( 1.0f, 0.0, 0.0f ), matTiltToWorld, vecForwardTilted );
  518. VectorTransform( Vector( 0.0f, 0.0, 1.0f ), matTiltToWorld, vecUpTilted );
  519. QAngle anglesTilted;
  520. VectorAngles( vecForwardTilted, vecUpTilted, anglesTilted );
  521. angles = anglesTilted;
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose: Zeros out all active screen shakes.
  525. //-----------------------------------------------------------------------------
  526. void CViewEffects::ClearAllShakes()
  527. {
  528. int nShakeCount = m_ShakeList.Count();
  529. for ( int i = 0; i < nShakeCount; i++ )
  530. {
  531. delete m_ShakeList.Element( i );
  532. }
  533. m_ShakeList.Purge();
  534. }
  535. //-----------------------------------------------------------------------------
  536. // Purpose: Returns the shake with the longest duration. This is the shake we
  537. // use anytime we get an amplitude or frequency command, because the
  538. // most likely case is that we're modifying a shake with a long
  539. // duration rather than a brief shake caused by an explosion, etc.
  540. //-----------------------------------------------------------------------------
  541. screenshake_t *CViewEffects::FindLongestShake()
  542. {
  543. screenshake_t *pLongestShake = NULL;
  544. int nShakeCount = m_ShakeList.Count();
  545. for ( int i = 0; i < nShakeCount; i++ )
  546. {
  547. screenshake_t *pShake = m_ShakeList.Element( i );
  548. if ( pShake && ( !pLongestShake || ( pShake->duration > pLongestShake->duration ) ) )
  549. {
  550. pLongestShake = pShake;
  551. }
  552. }
  553. return pLongestShake;
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose: Message hook to parse ScreenShake messages
  557. // Input : pszName -
  558. // iSize -
  559. // pbuf -
  560. // Output :
  561. //-----------------------------------------------------------------------------
  562. void CViewEffects::Shake( const ScreenShake_t &data )
  563. {
  564. if ( ( data.command == SHAKE_START || data.command == SHAKE_START_RUMBLEONLY ) && ( m_ShakeList.Count() < MAX_SHAKES ) )
  565. {
  566. screenshake_t * RESTRICT pNewShake = new screenshake_t; // ugh, should just make these a static array
  567. pNewShake->amplitude = data.amplitude;
  568. pNewShake->frequency = data.frequency;
  569. pNewShake->duration = data.duration;
  570. pNewShake->nextShake = 0;
  571. pNewShake->endtime = gpGlobals->curtime + data.duration;
  572. pNewShake->command = data.command;
  573. pNewShake->direction = data.direction;
  574. pNewShake->nShakeType = data.direction.IsZeroFast() ? screenshake_t::kSHAKE_BASIC : screenshake_t::kSHAKE_DIRECTIONAL;
  575. m_ShakeList.AddToTail( (screenshake_t *) pNewShake );
  576. }
  577. else if ( data.command == SHAKE_STOP)
  578. {
  579. ClearAllShakes();
  580. }
  581. else if ( data.command == SHAKE_AMPLITUDE )
  582. {
  583. // Look for the most likely shake to modify.
  584. screenshake_t * RESTRICT pShake = FindLongestShake();
  585. if ( pShake )
  586. {
  587. pShake->amplitude = data.amplitude;
  588. }
  589. }
  590. else if ( data.command == SHAKE_FREQUENCY )
  591. {
  592. // Look for the most likely shake to modify.
  593. screenshake_t * RESTRICT pShake = FindLongestShake();
  594. if ( pShake )
  595. {
  596. pShake->frequency = data.frequency;
  597. }
  598. }
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose: Zeros out all active screen tilts.
  602. //-----------------------------------------------------------------------------
  603. void CViewEffects::ClearAllTilts()
  604. {
  605. int nTiltCount = m_TiltList.Count();
  606. for ( int i = 0; i < nTiltCount; i++ )
  607. {
  608. delete m_TiltList.Element( i );
  609. }
  610. m_TiltList.Purge();
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Purpose: Returns the shake with the longest duration. This is the shake we
  614. // use anytime we get an amplitude or frequency command, because the
  615. // most likely case is that we're modifying a shake with a long
  616. // duration rather than a brief shake caused by an explosion, etc.
  617. //-----------------------------------------------------------------------------
  618. screentilt_t *CViewEffects::FindLongestTilt()
  619. {
  620. screentilt_t *pLongestTilt = NULL;
  621. int nTiltCount = m_TiltList.Count();
  622. for ( int i = 0; i < nTiltCount; i++ )
  623. {
  624. screentilt_t *pTilt = m_TiltList.Element( i );
  625. if ( pTilt && ( !pLongestTilt || ( pTilt->duration > pLongestTilt->duration ) ) )
  626. {
  627. pLongestTilt = pTilt;
  628. }
  629. }
  630. return pLongestTilt;
  631. }
  632. //-----------------------------------------------------------------------------
  633. // Purpose: Message hook to parse ScreenTilt messages
  634. // Input : pszName -
  635. // iSize -
  636. // pbuf -
  637. // Output :
  638. //-----------------------------------------------------------------------------
  639. void CViewEffects::Tilt( ScreenTilt_t &data )
  640. {
  641. if ( ( data.command == SHAKE_START || data.command == SHAKE_START_RUMBLEONLY ) && ( m_ShakeList.Count() < MAX_SHAKES ) )
  642. {
  643. screentilt_t *pNewTilt = new screentilt_t;
  644. pNewTilt->easeInOut = data.easeInOut;
  645. pNewTilt->angle = data.angle;
  646. pNewTilt->duration = data.duration;
  647. pNewTilt->tiltTime = data.time;
  648. pNewTilt->starttime = gpGlobals->curtime;
  649. pNewTilt->endtime = pNewTilt->starttime + data.duration;
  650. pNewTilt->command = data.command;
  651. m_TiltList.AddToTail( pNewTilt );
  652. }
  653. else if ( data.command == SHAKE_STOP)
  654. {
  655. ClearAllTilts();
  656. }
  657. }
  658. //-----------------------------------------------------------------------------
  659. // Purpose: Message hook to parse ScreenFade messages
  660. // Input : *pszName -
  661. // iSize -
  662. // *pbuf -
  663. // Output : int
  664. //-----------------------------------------------------------------------------
  665. void CViewEffects::Fade( ScreenFade_t &data )
  666. {
  667. // Create a new fade and append it to the list
  668. screenfade_t *pNewFade = new screenfade_t;
  669. pNewFade->End = data.duration * (1.0f/(float)(1<<SCREENFADE_FRACBITS));
  670. pNewFade->Reset = data.holdTime * (1.0f/(float)(1<<SCREENFADE_FRACBITS));
  671. pNewFade->r = data.r;
  672. pNewFade->g = data.g;
  673. pNewFade->b = data.b;
  674. pNewFade->alpha = data.a;
  675. pNewFade->Flags = data.fadeFlags;
  676. pNewFade->Speed = 0;
  677. // Calc fade speed
  678. if ( data.duration > 0 )
  679. {
  680. if ( data.fadeFlags & FFADE_OUT )
  681. {
  682. if ( pNewFade->End )
  683. {
  684. pNewFade->Speed = -(float)pNewFade->alpha / pNewFade->End;
  685. }
  686. pNewFade->End += gpGlobals->curtime;
  687. pNewFade->Reset += pNewFade->End;
  688. }
  689. else
  690. {
  691. if ( pNewFade->End )
  692. {
  693. pNewFade->Speed = (float)pNewFade->alpha / pNewFade->End;
  694. }
  695. pNewFade->Reset += gpGlobals->curtime;
  696. pNewFade->End += pNewFade->Reset;
  697. }
  698. }
  699. if ( data.fadeFlags & FFADE_PURGE )
  700. {
  701. ClearAllFades();
  702. }
  703. m_FadeList.AddToTail( pNewFade );
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose: Compute the overall color & alpha of the fades
  707. //-----------------------------------------------------------------------------
  708. void CViewEffects::FadeCalculate( void )
  709. {
  710. // Cycle through all fades and remove any that have finished (work backwards)
  711. int i;
  712. int iSize = m_FadeList.Count();
  713. for (i = iSize-1; i >= 0; i-- )
  714. {
  715. screenfade_t *pFade = m_FadeList[i];
  716. // Keep pushing reset time out indefinitely
  717. if ( pFade->Flags & FFADE_STAYOUT )
  718. {
  719. pFade->Reset = gpGlobals->curtime + 0.1;
  720. }
  721. // All done?
  722. if ( ( gpGlobals->curtime > pFade->Reset ) && ( gpGlobals->curtime > pFade->End ) )
  723. {
  724. // User passed in a callback function, call it now
  725. if ( s_pfnFadeDoneCallback )
  726. {
  727. s_pfnFadeDoneCallback( s_nCallbackParameter );
  728. s_pfnFadeDoneCallback = NULL;
  729. s_nCallbackParameter = 0;
  730. }
  731. // Remove this Fade from the list
  732. m_FadeList.FindAndRemove( pFade );
  733. delete pFade;
  734. }
  735. }
  736. m_bModulate = false;
  737. m_FadeColorRGBA[0] = m_FadeColorRGBA[1] = m_FadeColorRGBA[2] = m_FadeColorRGBA[3] = 0;
  738. // Cycle through all fades in the list and calculate the overall color/alpha
  739. for ( i = 0; i < m_FadeList.Count(); i++ )
  740. {
  741. screenfade_t *pFade = m_FadeList[i];
  742. // Color
  743. m_FadeColorRGBA[0] += pFade->r;
  744. m_FadeColorRGBA[1] += pFade->g;
  745. m_FadeColorRGBA[2] += pFade->b;
  746. // Fading...
  747. int iFadeAlpha;
  748. if ( pFade->Flags & (FFADE_OUT|FFADE_IN) )
  749. {
  750. iFadeAlpha = pFade->Speed * ( pFade->End - gpGlobals->curtime );
  751. if ( pFade->Flags & FFADE_OUT )
  752. {
  753. iFadeAlpha += pFade->alpha;
  754. }
  755. iFadeAlpha = MIN( iFadeAlpha, pFade->alpha );
  756. iFadeAlpha = MAX( 0, iFadeAlpha );
  757. }
  758. else
  759. {
  760. iFadeAlpha = pFade->alpha;
  761. }
  762. // Use highest alpha
  763. if ( iFadeAlpha > m_FadeColorRGBA[3] )
  764. {
  765. m_FadeColorRGBA[3] = iFadeAlpha;
  766. }
  767. // Modulate?
  768. if ( pFade->Flags & FFADE_MODULATE )
  769. {
  770. m_bModulate = true;
  771. }
  772. }
  773. // Divide colors
  774. if ( m_FadeList.Count() )
  775. {
  776. m_FadeColorRGBA[0] /= m_FadeList.Count();
  777. m_FadeColorRGBA[1] /= m_FadeList.Count();
  778. m_FadeColorRGBA[2] /= m_FadeList.Count();
  779. }
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Purpose: Clear only the permanent fades in our fade list
  783. //-----------------------------------------------------------------------------
  784. void CViewEffects::ClearPermanentFades( void )
  785. {
  786. int iSize = m_FadeList.Count();
  787. for (int i = iSize-1; i >= 0; i-- )
  788. {
  789. screenfade_t *pFade = m_FadeList[i];
  790. if ( pFade->Flags & FFADE_STAYOUT )
  791. {
  792. // Destroy this fade
  793. m_FadeList.FindAndRemove( pFade );
  794. delete pFade;
  795. }
  796. }
  797. }
  798. //-----------------------------------------------------------------------------
  799. // Purpose: Purge & delete all fades in the queue
  800. //-----------------------------------------------------------------------------
  801. void CViewEffects::ClearAllFades( void )
  802. {
  803. int iSize = m_FadeList.Count();
  804. for (int i = iSize-1; i >= 0; i-- )
  805. {
  806. delete m_FadeList[i];
  807. }
  808. m_FadeList.Purge();
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose:
  812. // Input : context - Which call to Render is this ( CViewSetup::context )
  813. // *r -
  814. // *g -
  815. // *b -
  816. // *a -
  817. // *blend -
  818. //-----------------------------------------------------------------------------
  819. void CViewEffects::GetFadeParams( byte *r, byte *g, byte *b, byte *a, bool *blend )
  820. {
  821. // If the intro is overriding our fade, use that instead
  822. if ( g_pIntroData && g_pIntroData->m_flCurrentFadeColor[3] )
  823. {
  824. *r = g_pIntroData->m_flCurrentFadeColor[0];
  825. *g = g_pIntroData->m_flCurrentFadeColor[1];
  826. *b = g_pIntroData->m_flCurrentFadeColor[2];
  827. *a = g_pIntroData->m_flCurrentFadeColor[3];
  828. *blend = false;
  829. return;
  830. }
  831. FadeCalculate();
  832. *r = m_FadeColorRGBA[0];
  833. *g = m_FadeColorRGBA[1];
  834. *b = m_FadeColorRGBA[2];
  835. *a = m_FadeColorRGBA[3];
  836. *blend = m_bModulate;
  837. }
  838. //-----------------------------------------------------------------------------
  839. // Purpose:
  840. // Input : *pSave -
  841. //-----------------------------------------------------------------------------
  842. void CViewEffects::Save( ISave *pSave )
  843. {
  844. // Save the view fades
  845. int iCount = m_FadeList.Count();
  846. pSave->WriteInt( &iCount );
  847. for ( int i = 0; i < iCount; i++ )
  848. {
  849. pSave->StartBlock();
  850. pSave->WriteAll( m_FadeList[i] );
  851. pSave->EndBlock();
  852. }
  853. // Save the view shakes
  854. iCount = m_ShakeList.Count();
  855. pSave->WriteInt( &iCount );
  856. for ( int i = 0; i < iCount; i++ )
  857. {
  858. pSave->StartBlock();
  859. pSave->WriteAll( m_ShakeList[i] );
  860. pSave->EndBlock();
  861. }
  862. }
  863. //-----------------------------------------------------------------------------
  864. // Purpose:
  865. // Input : *pRestore -
  866. // fCreatePlayers -
  867. //-----------------------------------------------------------------------------
  868. void CViewEffects::Restore( IRestore *pRestore, bool fCreatePlayers )
  869. {
  870. CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
  871. // View effects is a singleton so we only need to restore it once,
  872. // from the level that we are going into.
  873. if( !pSaveData->levelInfo.fUseLandmark )
  874. {
  875. ClearAllFades();
  876. ClearAllShakes();
  877. // Read in the view fades
  878. int iCount = pRestore->ReadInt();
  879. for ( int i = 0; i < iCount; i++ )
  880. {
  881. screenfade_t *pNewFade = new screenfade_t;
  882. pRestore->StartBlock();
  883. pRestore->ReadAll( pNewFade );
  884. pRestore->EndBlock();
  885. m_FadeList.AddToTail( pNewFade );
  886. }
  887. // Read in the view shakes
  888. iCount = pRestore->ReadInt();
  889. for ( int i = 0; i < iCount; i++ )
  890. {
  891. screenshake_t *pNewShake = new screenshake_t;
  892. pRestore->StartBlock();
  893. pRestore->ReadAll( pNewShake );
  894. pRestore->EndBlock();
  895. m_ShakeList.AddToTail( pNewShake );
  896. }
  897. }
  898. }
  899. //====================================================================================================
  900. // CLIENTSIDE VIEW EFFECTS SAVE/RESTORE
  901. //====================================================================================================
  902. static short VIEWEFFECTS_SAVE_RESTORE_VERSION = 2;
  903. class CViewEffectsSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
  904. {
  905. struct QueuedItem_t;
  906. public:
  907. CViewEffectsSaveRestoreBlockHandler()
  908. {
  909. }
  910. const char *GetBlockName()
  911. {
  912. return "ViewEffects";
  913. }
  914. //---------------------------------
  915. virtual void PreSave( CSaveRestoreData * )
  916. {
  917. }
  918. //---------------------------------
  919. virtual void Save( ISave *pSave )
  920. {
  921. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  922. GetViewEffects()->Save( pSave );
  923. }
  924. //---------------------------------
  925. virtual void WriteSaveHeaders( ISave *pSave )
  926. {
  927. pSave->WriteShort( &VIEWEFFECTS_SAVE_RESTORE_VERSION );
  928. }
  929. //---------------------------------
  930. virtual void PostSave()
  931. {
  932. }
  933. //---------------------------------
  934. virtual void PreRestore()
  935. {
  936. }
  937. //---------------------------------
  938. virtual void ReadRestoreHeaders( IRestore *pRestore )
  939. {
  940. // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
  941. short version = pRestore->ReadShort();
  942. m_bDoLoad = ( version == VIEWEFFECTS_SAVE_RESTORE_VERSION );
  943. }
  944. //---------------------------------
  945. virtual void Restore( IRestore *pRestore, bool fCreatePlayers )
  946. {
  947. if ( m_bDoLoad )
  948. {
  949. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  950. GetViewEffects()->Restore( pRestore, fCreatePlayers );
  951. }
  952. }
  953. //---------------------------------
  954. virtual void PostRestore()
  955. {
  956. }
  957. private:
  958. bool m_bDoLoad;
  959. };
  960. //-----------------------------------------------------------------------------
  961. CViewEffectsSaveRestoreBlockHandler g_ViewEffectsSaveRestoreBlockHandler;
  962. ISaveRestoreBlockHandler *GetViewEffectsRestoreBlockHandler()
  963. {
  964. return &g_ViewEffectsSaveRestoreBlockHandler;
  965. }