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.

581 lines
16 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements a screen shake effect that can also shake physics objects.
  4. //
  5. // NOTE: UTIL_ScreenShake() will only shake players who are on the ground
  6. //
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "shake.h"
  11. #include "physics_saverestore.h"
  12. #include "rope.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. class CPhysicsShake : public IMotionEvent
  16. {
  17. DECLARE_SIMPLE_DATADESC();
  18. public:
  19. virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  20. {
  21. Vector contact;
  22. if ( !pObject->GetContactPoint( &contact, NULL ) )
  23. return SIM_NOTHING;
  24. // fudge the force a bit to make it more dramatic
  25. pObject->CalculateForceOffset( m_force * (1.0f + pObject->GetMass()*0.4f), contact, &linear, &angular );
  26. return SIM_LOCAL_FORCE;
  27. }
  28. Vector m_force;
  29. };
  30. BEGIN_SIMPLE_DATADESC( CPhysicsShake )
  31. DEFINE_FIELD( m_force, FIELD_VECTOR ),
  32. END_DATADESC()
  33. class CEnvShake : public CPointEntity
  34. {
  35. private:
  36. float m_Amplitude;
  37. float m_Frequency;
  38. float m_Duration;
  39. float m_Radius; // radius of 0 means all players
  40. float m_stopTime;
  41. float m_nextShake;
  42. float m_currentAmp;
  43. Vector m_maxForce;
  44. IPhysicsMotionController *m_pShakeController;
  45. CPhysicsShake m_shakeCallback;
  46. DECLARE_DATADESC();
  47. public:
  48. DECLARE_CLASS( CEnvShake, CPointEntity );
  49. virtual ~CEnvShake( void );
  50. virtual void Precache( void );
  51. virtual void Spawn( void );
  52. virtual void OnRestore( void );
  53. inline float Amplitude( void ) { return m_Amplitude; }
  54. inline float Frequency( void ) { return m_Frequency; }
  55. inline float Duration( void ) { return m_Duration; }
  56. float Radius( bool bPlayers = true );
  57. inline void SetAmplitude( float amplitude ) { m_Amplitude = amplitude; }
  58. inline void SetFrequency( float frequency ) { m_Frequency = frequency; }
  59. inline void SetDuration( float duration ) { m_Duration = duration; }
  60. inline void SetRadius( float radius ) { m_Radius = radius; }
  61. int DrawDebugTextOverlays(void);
  62. // Input handlers
  63. void InputStartShake( inputdata_t &inputdata );
  64. void InputStopShake( inputdata_t &inputdata );
  65. void InputAmplitude( inputdata_t &inputdata );
  66. void InputFrequency( inputdata_t &inputdata );
  67. // Causes the camera/physics shakes to happen:
  68. void ApplyShake( ShakeCommand_t command );
  69. void Think( void );
  70. };
  71. LINK_ENTITY_TO_CLASS( env_shake, CEnvShake );
  72. BEGIN_DATADESC( CEnvShake )
  73. DEFINE_KEYFIELD( m_Amplitude, FIELD_FLOAT, "amplitude" ),
  74. DEFINE_KEYFIELD( m_Frequency, FIELD_FLOAT, "frequency" ),
  75. DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ),
  76. DEFINE_KEYFIELD( m_Radius, FIELD_FLOAT, "radius" ),
  77. DEFINE_FIELD( m_stopTime, FIELD_TIME ),
  78. DEFINE_FIELD( m_nextShake, FIELD_TIME ),
  79. DEFINE_FIELD( m_currentAmp, FIELD_FLOAT ),
  80. DEFINE_FIELD( m_maxForce, FIELD_VECTOR ),
  81. DEFINE_PHYSPTR( m_pShakeController ),
  82. DEFINE_EMBEDDED( m_shakeCallback ),
  83. DEFINE_INPUTFUNC( FIELD_VOID, "StartShake", InputStartShake ),
  84. DEFINE_INPUTFUNC( FIELD_VOID, "StopShake", InputStopShake ),
  85. DEFINE_INPUTFUNC( FIELD_FLOAT, "Amplitude", InputAmplitude ),
  86. DEFINE_INPUTFUNC( FIELD_FLOAT, "Frequency", InputFrequency ),
  87. END_DATADESC()
  88. #define SF_SHAKE_EVERYONE 0x0001 // Don't check radius
  89. #define SF_SHAKE_INAIR 0x0004 // Shake players in air
  90. #define SF_SHAKE_PHYSICS 0x0008 // Shake physically (not just camera)
  91. #define SF_SHAKE_ROPES 0x0010 // Shake ropes too.
  92. #define SF_SHAKE_NO_VIEW 0x0020 // DON'T shake the view (only ropes and/or physics objects)
  93. #define SF_SHAKE_NO_RUMBLE 0x0040 // DON'T Rumble the XBox Controller
  94. #define SF_TILT_EASE_INOUT 0x0080 // Ease in and out of the tilt
  95. //-----------------------------------------------------------------------------
  96. // Purpose: Destructor.
  97. //-----------------------------------------------------------------------------
  98. CEnvShake::~CEnvShake( void )
  99. {
  100. if ( m_pShakeController )
  101. {
  102. physenv->DestroyMotionController( m_pShakeController );
  103. }
  104. }
  105. float CEnvShake::Radius(bool bPlayers)
  106. {
  107. // The radius for players is zero if SF_SHAKE_EVERYONE is set
  108. if ( bPlayers && HasSpawnFlags(SF_SHAKE_EVERYONE))
  109. return 0;
  110. return m_Radius;
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: Sets default member values when spawning.
  114. //-----------------------------------------------------------------------------
  115. void CEnvShake::Precache()
  116. {
  117. BaseClass::Precache();
  118. CRopeKeyframe::PrecacheShakeRopes();
  119. }
  120. void CEnvShake::Spawn( void )
  121. {
  122. Precache();
  123. SetSolid( SOLID_NONE );
  124. SetMoveType( MOVETYPE_NONE );
  125. if ( GetSpawnFlags() & SF_SHAKE_EVERYONE )
  126. {
  127. m_Radius = 0;
  128. }
  129. if ( HasSpawnFlags( SF_SHAKE_NO_VIEW ) && !HasSpawnFlags( SF_SHAKE_PHYSICS ) && !HasSpawnFlags( SF_SHAKE_ROPES ) )
  130. {
  131. DevWarning( "env_shake %s with \"Don't shake view\" spawnflag set without \"Shake physics\" or \"Shake ropes\" spawnflags set.", GetDebugName() );
  132. }
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose: Restore the motion controller
  136. //-----------------------------------------------------------------------------
  137. void CEnvShake::OnRestore( void )
  138. {
  139. BaseClass::OnRestore();
  140. if ( m_pShakeController )
  141. {
  142. m_pShakeController->SetEventHandler( &m_shakeCallback );
  143. }
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose:
  147. //-----------------------------------------------------------------------------
  148. void CEnvShake::ApplyShake( ShakeCommand_t command )
  149. {
  150. if ( !HasSpawnFlags( SF_SHAKE_NO_VIEW ) || !HasSpawnFlags( SF_SHAKE_NO_RUMBLE ) )
  151. {
  152. bool air = (GetSpawnFlags() & SF_SHAKE_INAIR) ? true : false;
  153. UTIL_ScreenShake( GetAbsOrigin(), Amplitude(), Frequency(), Duration(), Radius(), command, air );
  154. }
  155. if ( GetSpawnFlags() & SF_SHAKE_ROPES )
  156. {
  157. CRopeKeyframe::ShakeRopes( GetAbsOrigin(), Radius(false), Frequency() );
  158. }
  159. if ( GetSpawnFlags() & SF_SHAKE_PHYSICS )
  160. {
  161. if ( !m_pShakeController )
  162. {
  163. m_pShakeController = physenv->CreateMotionController( &m_shakeCallback );
  164. }
  165. // do physics shake
  166. switch( command )
  167. {
  168. case SHAKE_START:
  169. case SHAKE_START_NORUMBLE:
  170. case SHAKE_START_RUMBLEONLY:
  171. {
  172. m_stopTime = gpGlobals->curtime + Duration();
  173. m_nextShake = 0;
  174. m_pShakeController->ClearObjects();
  175. SetNextThink( gpGlobals->curtime );
  176. m_currentAmp = Amplitude();
  177. CBaseEntity *list[1024];
  178. float radius = Radius(false);
  179. // probably checked "Shake Everywhere" do a big radius
  180. if ( !radius )
  181. {
  182. radius = 512;
  183. }
  184. Vector extents = Vector(radius, radius, radius);
  185. extents.z = MAX(extents.z, 100);
  186. Vector mins = GetAbsOrigin() - extents;
  187. Vector maxs = GetAbsOrigin() + extents;
  188. int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 );
  189. for ( int i = 0; i < count; i++ )
  190. {
  191. //
  192. // Only shake physics entities that players can see. This is one frame out of date
  193. // so it's possible that we could miss objects if a player changed PVS this frame.
  194. //
  195. if ( ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS ) )
  196. {
  197. IPhysicsObject *pPhys = list[i]->VPhysicsGetObject();
  198. if ( pPhys && pPhys->IsMoveable() )
  199. {
  200. m_pShakeController->AttachObject( pPhys, false );
  201. pPhys->Wake();
  202. }
  203. }
  204. }
  205. }
  206. break;
  207. case SHAKE_STOP:
  208. m_pShakeController->ClearObjects();
  209. break;
  210. case SHAKE_AMPLITUDE:
  211. m_currentAmp = Amplitude();
  212. case SHAKE_FREQUENCY:
  213. m_pShakeController->WakeObjects();
  214. break;
  215. }
  216. }
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose: Input handler that starts the screen shake.
  220. //-----------------------------------------------------------------------------
  221. void CEnvShake::InputStartShake( inputdata_t &inputdata )
  222. {
  223. if ( HasSpawnFlags( SF_SHAKE_NO_RUMBLE ) )
  224. {
  225. ApplyShake( SHAKE_START_NORUMBLE );
  226. }
  227. else if ( HasSpawnFlags( SF_SHAKE_NO_VIEW ) )
  228. {
  229. ApplyShake( SHAKE_START_RUMBLEONLY );
  230. }
  231. else
  232. {
  233. ApplyShake( SHAKE_START );
  234. }
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Input handler that stops the screen shake.
  238. //-----------------------------------------------------------------------------
  239. void CEnvShake::InputStopShake( inputdata_t &inputdata )
  240. {
  241. ApplyShake( SHAKE_STOP );
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose: Handles changes to the shake amplitude from an external source.
  245. //-----------------------------------------------------------------------------
  246. void CEnvShake::InputAmplitude( inputdata_t &inputdata )
  247. {
  248. SetAmplitude( inputdata.value.Float() );
  249. ApplyShake( SHAKE_AMPLITUDE );
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose: Handles changes to the shake frequency from an external source.
  253. //-----------------------------------------------------------------------------
  254. void CEnvShake::InputFrequency( inputdata_t &inputdata )
  255. {
  256. SetFrequency( inputdata.value.Float() );
  257. ApplyShake( SHAKE_FREQUENCY );
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Calculates the physics shake values
  261. //-----------------------------------------------------------------------------
  262. void CEnvShake::Think( void )
  263. {
  264. int i;
  265. if ( gpGlobals->curtime > m_nextShake )
  266. {
  267. // Higher frequency means we recalc the extents more often and perturb the display again
  268. m_nextShake = gpGlobals->curtime + (1.0f / Frequency());
  269. // Compute random shake extents (the shake will settle down from this)
  270. for (i = 0; i < 2; i++ )
  271. {
  272. m_maxForce[i] = random->RandomFloat( -1, 1 );
  273. }
  274. // make the force it point mostly up
  275. m_maxForce.z = 4;
  276. VectorNormalize( m_maxForce );
  277. m_maxForce *= m_currentAmp * 400; // amplitude is the acceleration of a 100kg object
  278. }
  279. float fraction = ( m_stopTime - gpGlobals->curtime ) / Duration();
  280. if ( fraction < 0 )
  281. {
  282. m_pShakeController->ClearObjects();
  283. return;
  284. }
  285. float freq = 0;
  286. // Ramp up frequency over duration
  287. if ( fraction )
  288. {
  289. freq = (Frequency() / fraction);
  290. }
  291. // square fraction to approach zero more quickly
  292. fraction *= fraction;
  293. // Sine wave that slowly settles to zero
  294. fraction = fraction * sin( gpGlobals->curtime * freq );
  295. // Add to view origin
  296. for ( i = 0; i < 3; i++ )
  297. {
  298. // store the force in the controller callback
  299. m_shakeCallback.m_force[i] = m_maxForce[i] * fraction;
  300. }
  301. // Drop amplitude a bit, less for higher frequency shakes
  302. m_currentAmp -= m_currentAmp * ( gpGlobals->frametime / (Duration() * Frequency()) );
  303. SetNextThink( gpGlobals->curtime );
  304. }
  305. //------------------------------------------------------------------------------
  306. // Purpose: Console command to cause a screen shake.
  307. //------------------------------------------------------------------------------
  308. void CC_Shake( void )
  309. {
  310. CBasePlayer *pPlayer = UTIL_GetCommandClient();
  311. if (pPlayer)
  312. {
  313. UTIL_ScreenShake( pPlayer->WorldSpaceCenter(), 25.0, 150.0, 1.0, 750, SHAKE_START );
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: Draw any debug text overlays
  318. // Returns current text offset from the top
  319. //-----------------------------------------------------------------------------
  320. int CEnvShake::DrawDebugTextOverlays( void )
  321. {
  322. int text_offset = BaseClass::DrawDebugTextOverlays();
  323. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  324. {
  325. char tempstr[512];
  326. // print amplitude
  327. Q_snprintf(tempstr,sizeof(tempstr)," magnitude: %f", m_Amplitude);
  328. EntityText(text_offset,tempstr,0);
  329. text_offset++;
  330. // print frequency
  331. Q_snprintf(tempstr,sizeof(tempstr)," frequency: %f", m_Frequency);
  332. EntityText(text_offset,tempstr,0);
  333. text_offset++;
  334. // print duration
  335. Q_snprintf(tempstr,sizeof(tempstr)," duration: %f", m_Duration);
  336. EntityText(text_offset,tempstr,0);
  337. text_offset++;
  338. // print radius
  339. Q_snprintf(tempstr,sizeof(tempstr)," radius: %f", m_Radius);
  340. EntityText(text_offset,tempstr,0);
  341. text_offset++;
  342. }
  343. return text_offset;
  344. }
  345. static ConCommand shake("shake", CC_Shake, "Shake the screen.", FCVAR_CHEAT );
  346. // Tilt effect
  347. class CEnvTilt : public CPointEntity
  348. {
  349. private:
  350. float m_Duration;
  351. float m_Radius; // radius of 0 means all players
  352. float m_TiltTime;
  353. float m_stopTime;
  354. DECLARE_DATADESC();
  355. public:
  356. DECLARE_CLASS( CEnvShake, CPointEntity );
  357. virtual void Precache( void );
  358. virtual void Spawn( void );
  359. QAngle TiltAngle( void );
  360. inline float Duration( void ) { return m_Duration; }
  361. float Radius( bool bPlayers = true );
  362. inline float TiltTime( void ) { return m_TiltTime; }
  363. inline void SetDuration( float duration ) { m_Duration = duration; }
  364. inline void SetRadius( float radius ) { m_Radius = radius; }
  365. int DrawDebugTextOverlays(void);
  366. // Input handlers
  367. void InputStartTilt( inputdata_t &inputdata );
  368. void InputStopTilt( inputdata_t &inputdata );
  369. // Causes the camera/physics shakes to happen:
  370. void ApplyTilt( ShakeCommand_t command );
  371. };
  372. LINK_ENTITY_TO_CLASS( env_tilt, CEnvTilt );
  373. BEGIN_DATADESC( CEnvTilt )
  374. DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ),
  375. DEFINE_KEYFIELD( m_Radius, FIELD_FLOAT, "radius" ),
  376. DEFINE_KEYFIELD( m_TiltTime, FIELD_TIME, "tilttime" ),
  377. DEFINE_FIELD( m_stopTime, FIELD_TIME ),
  378. DEFINE_INPUTFUNC( FIELD_VOID, "StartTilt", InputStartTilt ),
  379. DEFINE_INPUTFUNC( FIELD_VOID, "StopTilt", InputStopTilt ),
  380. END_DATADESC()
  381. QAngle CEnvTilt::TiltAngle( void )
  382. {
  383. QAngle qTiltAngles = GetAbsAngles();
  384. qTiltAngles.y = 0.0f; // Tilting around the up axis makes no sense
  385. return qTiltAngles;
  386. }
  387. float CEnvTilt::Radius(bool bPlayers)
  388. {
  389. // The radius for players is zero if SF_SHAKE_EVERYONE is set
  390. if ( bPlayers && HasSpawnFlags(SF_SHAKE_EVERYONE))
  391. return 0;
  392. return m_Radius;
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose: Sets default member values when spawning.
  396. //-----------------------------------------------------------------------------
  397. void CEnvTilt::Precache()
  398. {
  399. BaseClass::Precache();
  400. CRopeKeyframe::PrecacheShakeRopes();
  401. }
  402. void CEnvTilt::Spawn( void )
  403. {
  404. Precache();
  405. SetSolid( SOLID_NONE );
  406. SetMoveType( MOVETYPE_NONE );
  407. if ( GetSpawnFlags() & SF_SHAKE_EVERYONE )
  408. {
  409. m_Radius = 0;
  410. }
  411. if ( HasSpawnFlags( SF_SHAKE_NO_VIEW ) && !HasSpawnFlags( SF_SHAKE_PHYSICS ) && !HasSpawnFlags( SF_SHAKE_ROPES ) )
  412. {
  413. DevWarning( "env_shake %s with \"Don't shake view\" spawnflag set without \"Shake physics\" or \"Shake ropes\" spawnflags set.", GetDebugName() );
  414. }
  415. }
  416. //-----------------------------------------------------------------------------
  417. // Purpose:
  418. //-----------------------------------------------------------------------------
  419. void CEnvTilt::ApplyTilt( ShakeCommand_t command )
  420. {
  421. if ( !HasSpawnFlags( SF_SHAKE_NO_VIEW ) || !HasSpawnFlags( SF_SHAKE_NO_RUMBLE ) )
  422. {
  423. UTIL_ScreenTilt( GetAbsOrigin(), TiltAngle(), Duration(), Radius(), TiltTime(), command, (GetSpawnFlags() & SF_TILT_EASE_INOUT) != 0 );
  424. }
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: Input handler that starts the screen shake.
  428. //-----------------------------------------------------------------------------
  429. void CEnvTilt::InputStartTilt( inputdata_t &inputdata )
  430. {
  431. if ( HasSpawnFlags( SF_SHAKE_NO_RUMBLE ) )
  432. {
  433. ApplyTilt( SHAKE_START_NORUMBLE );
  434. }
  435. else if ( HasSpawnFlags( SF_SHAKE_NO_VIEW ) )
  436. {
  437. ApplyTilt( SHAKE_START_RUMBLEONLY );
  438. }
  439. else
  440. {
  441. ApplyTilt( SHAKE_START );
  442. }
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose: Input handler that stops the screen shake.
  446. //-----------------------------------------------------------------------------
  447. void CEnvTilt::InputStopTilt( inputdata_t &inputdata )
  448. {
  449. ApplyTilt( SHAKE_STOP );
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose: Draw any debug text overlays
  453. // Returns current text offset from the top
  454. //-----------------------------------------------------------------------------
  455. int CEnvTilt::DrawDebugTextOverlays( void )
  456. {
  457. int text_offset = BaseClass::DrawDebugTextOverlays();
  458. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  459. {
  460. char tempstr[512];
  461. // print duration
  462. Q_snprintf(tempstr,sizeof(tempstr)," duration: %f", m_Duration);
  463. EntityText(text_offset,tempstr,0);
  464. text_offset++;
  465. // print radius
  466. Q_snprintf(tempstr,sizeof(tempstr)," radius: %f", m_Radius);
  467. EntityText(text_offset,tempstr,0);
  468. text_offset++;
  469. }
  470. return text_offset;
  471. }