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.

929 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #if defined( REPLAY_ENABLED )
  8. #include "replay/ireplaysystem.h"
  9. #include "replay/ienginereplay.h"
  10. #include "replay/ireplaymanager.h"
  11. #include "replay/replay.h"
  12. #include "replay/replaycamera.h"
  13. #include "cdll_client_int.h"
  14. #include "util_shared.h"
  15. #include "prediction.h"
  16. #include "movevars_shared.h"
  17. #include "in_buttons.h"
  18. #include "text_message.h"
  19. #include "vgui_controls/Controls.h"
  20. #include "vgui/ILocalize.h"
  21. #include "vguicenterprint.h"
  22. #include "game/client/iviewport.h"
  23. #include "vgui/IInput.h"
  24. #include <KeyValues.h>
  25. #include "iinput.h"
  26. #include "iclientmode.h"
  27. #include "ienginevgui.h"
  28. #include "vgui/IInput.h"
  29. #include "mathlib/noise.h"
  30. #ifdef CSTRIKE_DLL
  31. #include "c_cs_player.h"
  32. #endif
  33. ConVar replay_editor_camera_length( "replay_editor_camera_length", "15", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "This is the camera length used to simulate camera shake in the replay editor. The larger this number, the more the actual position will change. It can also be set to negative values." );
  34. //ConVar spec_autodirector( "spec_autodirector", "1", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director chooses best view modes while spectating" );
  35. extern ConVar spec_autodirector;
  36. // memdbgon must be the last include file in a .cpp file!!!
  37. #include "tier0/memdbgon.h"
  38. #define CHASE_CAM_DISTANCE_MAX 96.0f
  39. #define WALL_OFFSET 6.0f
  40. #define DEFAULT_ROAMING_ACCEL 5.0f
  41. #define DEFAULT_ROAMING_SPEED 3.0f
  42. static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
  43. static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
  44. //////////////////////////////////////////////////////////////////////
  45. // Construction/Destruction
  46. //////////////////////////////////////////////////////////////////////
  47. // converts all '\r' characters to '\n', so that the engine can deal with the properly
  48. // returns a pointer to str
  49. static wchar_t* ConvertCRtoNL( wchar_t *str )
  50. {
  51. for ( wchar_t *ch = str; *ch != 0; ch++ )
  52. if ( *ch == L'\r' )
  53. *ch = L'\n';
  54. return str;
  55. }
  56. static C_ReplayCamera s_ReplayCamera;
  57. C_ReplayCamera *ReplayCamera()
  58. {
  59. return &s_ReplayCamera;
  60. }
  61. C_ReplayCamera::C_ReplayCamera()
  62. {
  63. Reset();
  64. m_nNumSpectators = 0;
  65. m_szTitleText[0] = 0;
  66. }
  67. C_ReplayCamera::~C_ReplayCamera()
  68. {
  69. }
  70. void C_ReplayCamera::Init()
  71. {
  72. ListenForGameEvent( "game_newmap" );
  73. Reset();
  74. m_nNumSpectators = 0;
  75. m_szTitleText[0] = 0;
  76. }
  77. void C_ReplayCamera::Reset()
  78. {
  79. m_nCameraMode = OBS_MODE_FIXED;
  80. m_iTarget1 = m_iTarget2 = 0;
  81. m_flFOV = 90.0f;
  82. m_flDistance = m_flLastDistance = CHASE_CAM_DISTANCE_MAX;
  83. m_flInertia = 3.0f;
  84. m_flPhi = 0;
  85. m_flTheta = 0;
  86. m_flOffset = 0;
  87. m_bEntityPacketReceived = false;
  88. m_bOverrideView = false;
  89. m_flOldTime = 0.0f;
  90. m_bInputEnabled = true;
  91. m_flRoamingAccel = DEFAULT_ROAMING_ACCEL;
  92. m_flRoamingSpeed = DEFAULT_ROAMING_SPEED;
  93. m_flRoamingFov[0] = m_flRoamingFov[1] = 90.0f;
  94. m_flRoamingRotFilterFactor = 10.0f;
  95. m_flRoamingShakeAmount = 0.0f;
  96. m_flRoamingShakeSpeed = 0.0f;
  97. m_flNoiseSample = 0.0f;
  98. m_flRoamingShakeDir = Lerp( 0.5f, FREE_CAM_SHAKE_DIR_MIN, FREE_CAM_SHAKE_DIR_MAX );
  99. m_vCamOrigin.Init();
  100. m_aCamAngle.Init();
  101. m_aSmoothedRoamingAngles.Init();
  102. m_OverrideViewData.origin.Init();
  103. m_OverrideViewData.angles.Init();
  104. m_OverrideViewData.fov = 90;
  105. m_LastCmd.Reset();
  106. m_vecVelocity.Init();
  107. InitRoamingKeys();
  108. }
  109. void C_ReplayCamera::InitRoamingKeys()
  110. {
  111. m_aMovementButtons[DIR_FWD ] = KEY_W;
  112. m_aMovementButtons[DIR_BACK ] = KEY_S;
  113. m_aMovementButtons[DIR_LEFT ] = KEY_A;
  114. m_aMovementButtons[DIR_RIGHT] = KEY_D;
  115. m_aMovementButtons[DIR_DOWN ] = KEY_X;
  116. m_aMovementButtons[DIR_UP ] = KEY_Z;
  117. }
  118. bool C_ReplayCamera::ShouldUseDefaultRoamingSettings() const
  119. {
  120. return vgui::input()->IsKeyDown( KEY_LSHIFT );
  121. }
  122. void C_ReplayCamera::CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta )
  123. {
  124. bool bManual = true;
  125. Vector targetOrigin1, targetOrigin2, cameraOrigin, forward;
  126. if ( m_iTarget1 == 0 )
  127. return;
  128. // get primary target, also translates to ragdoll
  129. C_BaseEntity *target1 = GetPrimaryTarget();
  130. if ( !target1 )
  131. return;
  132. if ( target1->IsAlive() && target1->IsDormant() )
  133. return;
  134. targetOrigin1 = target1->GetRenderOrigin();
  135. if ( !target1->IsAlive() )
  136. {
  137. targetOrigin1 += VEC_DEAD_VIEWHEIGHT;
  138. }
  139. else if ( target1->GetFlags() & FL_DUCKING )
  140. {
  141. targetOrigin1 += VEC_DUCK_VIEW;
  142. }
  143. else
  144. {
  145. targetOrigin1 += VEC_VIEW;
  146. }
  147. // get secondary target if set
  148. C_BaseEntity *target2 = NULL;
  149. if ( m_iTarget2 > 0 && (m_iTarget2 != m_iTarget1) && !bManual )
  150. {
  151. target2 = ClientEntityList().GetBaseEntity( m_iTarget2 );
  152. // if target is out PVS and not dead, it's not valid
  153. if ( target2 && target2->IsDormant() && target2->IsAlive() )
  154. target2 = NULL;
  155. if ( target2 )
  156. {
  157. targetOrigin2 = target2->GetRenderOrigin();
  158. if ( !target2->IsAlive() )
  159. {
  160. targetOrigin2 += VEC_DEAD_VIEWHEIGHT;
  161. }
  162. else if ( target2->GetFlags() & FL_DUCKING )
  163. {
  164. targetOrigin2 += VEC_DUCK_VIEW;
  165. }
  166. else
  167. {
  168. targetOrigin2 += VEC_VIEW;
  169. }
  170. }
  171. }
  172. // apply angle offset & smoothing
  173. QAngle angleOffset( m_flPhi, m_flTheta, 0 );
  174. QAngle cameraAngles = m_aCamAngle;
  175. if ( bManual )
  176. {
  177. // let spectator choose the view angles
  178. engine->GetViewAngles( cameraAngles );
  179. }
  180. else if ( target2 )
  181. {
  182. // look into direction of second target
  183. forward = targetOrigin2 - targetOrigin1;
  184. VectorAngles( forward, cameraAngles );
  185. cameraAngles.z = 0; // no ROLL
  186. }
  187. else if ( m_iTarget2 == 0 || m_iTarget2 == m_iTarget1)
  188. {
  189. // look into direction where primary target is looking
  190. cameraAngles = target1->EyeAngles();
  191. cameraAngles.x = 0; // no PITCH
  192. cameraAngles.z = 0; // no ROLL
  193. }
  194. else
  195. {
  196. // target2 is missing, just keep angelsm, reset offset
  197. angleOffset.Init();
  198. }
  199. if ( !bManual )
  200. {
  201. if ( !target1->IsAlive() )
  202. {
  203. angleOffset.x = 15;
  204. }
  205. cameraAngles += angleOffset;
  206. }
  207. AngleVectors( cameraAngles, &forward );
  208. VectorNormalize( forward );
  209. // calc optimal camera position
  210. VectorMA(targetOrigin1, -m_flDistance, forward, cameraOrigin );
  211. targetOrigin1.z += m_flOffset; // add offset
  212. // clip against walls
  213. trace_t trace;
  214. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  215. UTIL_TraceHull( targetOrigin1, cameraOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, target1, COLLISION_GROUP_NONE, &trace );
  216. C_BaseEntity::PopEnableAbsRecomputations();
  217. float dist = VectorLength( trace.endpos - targetOrigin1 );
  218. // grow distance by 32 unit a second
  219. m_flLastDistance += flDelta * 32.0f;
  220. if ( dist > m_flLastDistance )
  221. {
  222. VectorMA(targetOrigin1, -m_flLastDistance, forward, cameraOrigin );
  223. }
  224. else
  225. {
  226. cameraOrigin = trace.endpos;
  227. m_flLastDistance = dist;
  228. }
  229. if ( target2 )
  230. {
  231. // if we have 2 targets look at point between them
  232. forward = (targetOrigin1+targetOrigin2)/2 - cameraOrigin;
  233. QAngle angle;
  234. VectorAngles( forward, angle );
  235. cameraAngles.y = angle.y;
  236. NormalizeAngles( cameraAngles );
  237. cameraAngles.x = clamp( cameraAngles.x, -60.f, 60.f );
  238. SmoothCameraAngle( cameraAngles );
  239. }
  240. else
  241. {
  242. SetCameraAngle( cameraAngles );
  243. }
  244. VectorCopy( cameraOrigin, m_vCamOrigin );
  245. VectorCopy( m_aCamAngle, eyeAngles );
  246. VectorCopy( m_vCamOrigin, eyeOrigin );
  247. fov = m_flFOV;
  248. }
  249. int C_ReplayCamera::GetMode()
  250. {
  251. return m_nCameraMode;
  252. }
  253. C_BaseEntity* C_ReplayCamera::GetPrimaryTarget()
  254. {
  255. if ( m_iTarget1 <= 0 )
  256. return NULL;
  257. C_BaseEntity* target = ClientEntityList().GetEnt( m_iTarget1 );
  258. return target;
  259. }
  260. void C_ReplayCamera::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta )
  261. {
  262. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_iTarget1 );
  263. if ( !pPlayer )
  264. return;
  265. if ( !pPlayer->IsAlive() )
  266. {
  267. // if dead, show from 3rd person
  268. CalcChaseCamView( eyeOrigin, eyeAngles, fov, flDelta );
  269. return;
  270. }
  271. m_aCamAngle = pPlayer->EyeAngles();
  272. m_vCamOrigin = pPlayer->GetAbsOrigin();
  273. m_flFOV = pPlayer->GetFOV();
  274. if ( pPlayer->GetFlags() & FL_DUCKING )
  275. {
  276. m_vCamOrigin += VEC_DUCK_VIEW;
  277. }
  278. else
  279. {
  280. m_vCamOrigin += VEC_VIEW;
  281. }
  282. eyeOrigin = m_vCamOrigin;
  283. eyeAngles = m_aCamAngle;
  284. fov = m_flFOV;
  285. pPlayer->CalcViewModelView( eyeOrigin, eyeAngles);
  286. C_BaseViewModel *pViewModel = pPlayer->GetViewModel( 0 );
  287. if ( pViewModel )
  288. {
  289. Assert( pViewModel->GetOwner() == pPlayer );
  290. pViewModel->UpdateVisibility();
  291. }
  292. // This fixes the bug where going from third or first person to free cam defaults to some arbitrary angle,
  293. // because free cam uses engine->GetViewAngles().
  294. engine->SetViewAngles( m_aCamAngle );
  295. }
  296. void C_ReplayCamera::Accelerate( Vector& wishdir, float wishspeed, float accel, float flDelta )
  297. {
  298. float addspeed, accelspeed, currentspeed;
  299. // See if we are changing direction a bit
  300. currentspeed =m_vecVelocity.Dot(wishdir);
  301. // Reduce wishspeed by the amount of veer.
  302. addspeed = wishspeed - currentspeed;
  303. // If not going to add any speed, done.
  304. if (addspeed <= 0)
  305. return;
  306. // Determine amount of acceleration.
  307. accelspeed = accel * flDelta * wishspeed;
  308. // Cap at addspeed
  309. if (accelspeed > addspeed)
  310. accelspeed = addspeed;
  311. // Adjust velocity.
  312. for (int i=0 ; i<3 ; i++)
  313. {
  314. m_vecVelocity[i] += accelspeed * wishdir[i];
  315. }
  316. }
  317. bool C_ReplayCamera::ShouldOverrideView( Vector& origin, QAngle& angles, float& fov )
  318. {
  319. if ( !m_bOverrideView )
  320. return false;
  321. origin = m_OverrideViewData.origin;
  322. angles = m_OverrideViewData.angles;
  323. fov = m_OverrideViewData.fov;
  324. return true;
  325. }
  326. // movement code is a copy of CGameMovement::FullNoClipMove()
  327. void C_ReplayCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta)
  328. {
  329. // only if PVS isn't locked by auto-director
  330. if ( !IsPVSLocked() )
  331. {
  332. Vector wishvel;
  333. Vector forward, right, up;
  334. Vector wishdir;
  335. float wishspeed;
  336. float factor = ShouldUseDefaultRoamingSettings() ? DEFAULT_ROAMING_SPEED : m_flRoamingSpeed;
  337. float maxspeed = sv_maxspeed.GetFloat() * factor;
  338. AngleVectors ( m_aCamAngle, &forward, &right, &up ); // Determine movement angles
  339. if ( m_LastCmd.buttons & IN_SPEED )
  340. {
  341. factor /= 2.0f;
  342. }
  343. // Check for movement
  344. float fmove = 0.0f;
  345. float smove = 0.0f;
  346. float vmove = 0.0f;
  347. if ( !enginevgui->IsGameUIVisible() && m_bInputEnabled )
  348. {
  349. // Forward/backward movement
  350. if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_FWD] ) )
  351. {
  352. fmove = factor * maxspeed;
  353. }
  354. else if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_BACK] ) )
  355. {
  356. fmove = -factor * maxspeed;
  357. }
  358. // Lateral movement
  359. if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_LEFT] ) )
  360. {
  361. smove = -factor * maxspeed;
  362. }
  363. else if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_RIGHT] ) )
  364. {
  365. smove = factor * maxspeed;
  366. }
  367. // Vertical movement
  368. if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_UP] ) )
  369. {
  370. vmove = factor * maxspeed;
  371. }
  372. else if ( vgui::input()->IsKeyDown( m_aMovementButtons[DIR_DOWN] ) )
  373. {
  374. vmove = -factor * maxspeed;
  375. }
  376. }
  377. // Normalize remainder of vectors
  378. VectorNormalize(forward);
  379. VectorNormalize(right);
  380. VectorNormalize(up);
  381. for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  382. wishvel[i] = forward[i]*fmove + right[i]*smove + up[i]*vmove;
  383. wishvel[2] += m_LastCmd.upmove * factor;
  384. VectorCopy (wishvel, wishdir); // Determine magnitude of speed of move
  385. wishspeed = VectorNormalize(wishdir);
  386. //
  387. // Clamp to server defined max speed
  388. //
  389. if (wishspeed > maxspeed )
  390. {
  391. VectorScale (wishvel, maxspeed/wishspeed, wishvel);
  392. wishspeed = maxspeed;
  393. }
  394. const float flRoamingAccel = ShouldUseDefaultRoamingSettings() ?
  395. DEFAULT_ROAMING_ACCEL : m_flRoamingAccel;
  396. if ( flRoamingAccel > 0.0 )
  397. {
  398. // Set move velocity
  399. Accelerate ( wishdir, wishspeed, flRoamingAccel, flDelta );
  400. float spd = VectorLength( m_vecVelocity );
  401. if ( CloseEnough( spd, 0.0f ) )
  402. {
  403. m_vecVelocity.Init();
  404. }
  405. else
  406. {
  407. // Bleed off some speed, but if we have less than the bleed
  408. // threshold, bleed the threshold amount.
  409. float control = spd;
  410. float friction = sv_friction.GetFloat();
  411. // Add the amount to the drop amount.
  412. float drop = control * friction * flDelta;
  413. // scale the velocity
  414. float newspeed = spd - drop;
  415. if (newspeed < 0)
  416. newspeed = 0;
  417. // Determine proportion of old speed we are using.
  418. newspeed /= spd;
  419. VectorScale( m_vecVelocity, newspeed, m_vecVelocity );
  420. }
  421. }
  422. else
  423. {
  424. VectorCopy( wishvel, m_vecVelocity );
  425. }
  426. // Just move ( don't clip or anything )
  427. VectorMA( m_vCamOrigin, flDelta, m_vecVelocity, m_vCamOrigin );
  428. // get camera angle directly from engine
  429. engine->GetViewAngles( m_aCamAngle );
  430. // Zero out velocity if in noaccel mode
  431. if ( sv_specaccelerate.GetFloat() < 0.0f )
  432. {
  433. m_vecVelocity.Init();
  434. }
  435. }
  436. // Smooth the angles
  437. float flPercent = clamp( flDelta * m_flRoamingRotFilterFactor, 0.0f, 1.0f );
  438. m_aSmoothedRoamingAngles = Lerp( flPercent, m_aSmoothedRoamingAngles, m_aCamAngle );
  439. Vector vCameraShakeOffset;
  440. vCameraShakeOffset.Init();
  441. // Add in camera shake
  442. if ( !ShouldUseDefaultRoamingSettings() && m_flRoamingShakeAmount > 0.0f )
  443. {
  444. QAngle angShake( 0.0f, 0.0f, 0.0f );
  445. m_flNoiseSample += m_flRoamingShakeSpeed * flDelta;
  446. float flNoiseX = Lerp( FractalNoise( Vector( m_flNoiseSample, 0.0f, 0.0f ), 1 ), -1.0f, 1.0f );
  447. float flNoiseY = Lerp( FractalNoise( Vector( 0.0f, 1000 + m_flNoiseSample, 0.0f ), 1 ), -1.0f, 1.0f );
  448. // Vertical shake
  449. const float flAmplitudeX = m_flRoamingShakeAmount * ( m_flRoamingShakeDir < 0.0f ? ( 1.0f + m_flRoamingShakeDir ) : 1.0f );
  450. angShake.x = flAmplitudeX * flNoiseX;
  451. // Lateral shake
  452. const float flAmplitudeY = m_flRoamingShakeAmount * ( m_flRoamingShakeDir > 0.0f ? ( 1.0f - m_flRoamingShakeDir ) : 1.0f );
  453. angShake.y = flAmplitudeY * flNoiseY;
  454. // The math below simulates a camera with length "replay_editor_camera_length," so that the camera position bounces around
  455. // as if it were on someone's shoulder. If we were to just use angShake at this point with no translation, we would get a
  456. // camera that looks around but is anchored and doesn't feel quite right. With the code below, the camera will translate,
  457. // but be centered around the same point as when camera shake is off completely, rather than actually offsetting that point
  458. // by the camera length.
  459. // Get the forward vector from the shake transform/angles
  460. Vector vShakeForward;
  461. AngleVectors( angShake, &vShakeForward );
  462. // Calculate an offset, simulating a camera length
  463. Vector vCameraOffset = vShakeForward * replay_editor_camera_length.GetFloat();
  464. // Get the global matrix without any shake
  465. matrix3x4_t mGlobal;
  466. AngleMatrix( m_aSmoothedRoamingAngles, m_vCamOrigin, mGlobal );
  467. // Convert local shake angles and offset to a matrix
  468. matrix3x4_t mShake;
  469. AngleMatrix( angShake, mShake );
  470. // Setup a translation matrix using the offset
  471. matrix3x4_t mOffset;
  472. SetIdentityMatrix( mOffset );
  473. PositionMatrix( vCameraOffset, mOffset );
  474. matrix3x4_t mOffsetInv;
  475. MatrixInvert( mOffset, mOffsetInv );
  476. // The meat
  477. matrix3x4_t mFinal = mGlobal;
  478. MatrixMultiply( mFinal, mOffsetInv, mFinal );
  479. MatrixMultiply( mFinal, mShake, mFinal );
  480. MatrixMultiply( mFinal, mOffset, mFinal );
  481. // Convert back to Vector / QAngle
  482. MatrixAngles( mFinal, eyeAngles, eyeOrigin );
  483. }
  484. else
  485. {
  486. // No shake
  487. eyeOrigin = m_vCamOrigin;
  488. eyeAngles = m_aSmoothedRoamingAngles;
  489. }
  490. fov = m_flRoamingFov[0];
  491. }
  492. void C_ReplayCamera::CalcFixedView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov, float flDelta )
  493. {
  494. eyeOrigin = m_vCamOrigin;
  495. eyeAngles = m_aCamAngle;
  496. fov = m_flFOV;
  497. if ( m_iTarget1 == 0 )
  498. return;
  499. C_BaseEntity * target = ClientEntityList().GetBaseEntity( m_iTarget1 );
  500. if ( target && target->IsAlive() )
  501. {
  502. // if we're chasing a target, change viewangles
  503. QAngle angle;
  504. VectorAngles( (target->GetAbsOrigin()+VEC_VIEW) - m_vCamOrigin, angle );
  505. SmoothCameraAngle( angle );
  506. }
  507. }
  508. void C_ReplayCamera::PostEntityPacketReceived()
  509. {
  510. m_bEntityPacketReceived = true;
  511. }
  512. void C_ReplayCamera::SmoothFov( float flDelta )
  513. {
  514. m_flRoamingFov[0] = clamp(
  515. Lerp( 7 * flDelta, m_flRoamingFov[0], m_flRoamingFov[1] ),
  516. // Approach( m_flRoamingFov[1], m_flRoamingFov[0], 40 * m_flFrameTime ),
  517. FREE_CAM_FOV_MIN,
  518. FREE_CAM_FOV_MAX
  519. );
  520. }
  521. void C_ReplayCamera::FixupMovmentParents()
  522. {
  523. // Find resource zone
  524. for ( ClientEntityHandle_t e = ClientEntityList().FirstHandle();
  525. e != ClientEntityList().InvalidHandle(); e = ClientEntityList().NextHandle( e ) )
  526. {
  527. C_BaseEntity *ent = C_BaseEntity::Instance( e );
  528. if ( !ent )
  529. continue;
  530. ent->HierarchyUpdateMoveParent();
  531. }
  532. }
  533. void C_ReplayCamera::EnableInput( bool bEnable )
  534. {
  535. m_bInputEnabled = bEnable;
  536. }
  537. void C_ReplayCamera::ClearOverrideView()
  538. {
  539. if ( m_bOverrideView )
  540. {
  541. m_vCamOrigin = m_OverrideViewData.origin;
  542. m_aCamAngle = m_aSmoothedRoamingAngles = m_OverrideViewData.angles;
  543. m_flRoamingFov[0] = m_flRoamingFov[1] = m_OverrideViewData.fov;
  544. }
  545. m_bOverrideView = false;
  546. // Set view angles in engine so that CalcRoamingView() won't pop to some stupid angle
  547. engine->SetViewAngles( m_aCamAngle );
  548. }
  549. void C_ReplayCamera::OverrideView( const Vector *pOrigin, const QAngle *pAngles, float flFov )
  550. {
  551. m_bOverrideView = true;
  552. m_OverrideViewData.origin = *pOrigin;
  553. m_OverrideViewData.angles = *pAngles;
  554. m_OverrideViewData.fov = flFov;
  555. }
  556. void C_ReplayCamera::CalcView(Vector &origin, QAngle &angles, float &fov )
  557. {
  558. // NOTE ABOUT CLOCKS: 'realtime' is used, because otherwise we can't move the camera round while
  559. // the game is paused.
  560. // Calculate elapsed time since last call to CalcView()
  561. if ( m_flOldTime == 0.0f )
  562. {
  563. m_flOldTime = gpGlobals->realtime;
  564. }
  565. const float flDelta = gpGlobals->realtime - m_flOldTime;
  566. m_flOldTime = gpGlobals->realtime;
  567. if ( m_bEntityPacketReceived )
  568. {
  569. // try to fixup movment parents
  570. FixupMovmentParents();
  571. m_bEntityPacketReceived = false;
  572. }
  573. // Completely override?
  574. if ( ShouldOverrideView( origin, angles, fov ) )
  575. return;
  576. switch ( m_nCameraMode )
  577. {
  578. case OBS_MODE_ROAMING : CalcRoamingView( origin, angles, fov, flDelta );
  579. break;
  580. case OBS_MODE_FIXED : CalcFixedView( origin, angles, fov, flDelta );
  581. break;
  582. case OBS_MODE_IN_EYE : CalcInEyeCamView( origin, angles, fov, flDelta );
  583. break;
  584. case OBS_MODE_CHASE : CalcChaseCamView( origin, angles, fov, flDelta );
  585. break;
  586. }
  587. // Cache in case we want to access this data later in the frame
  588. m_CachedView.origin = origin;
  589. m_CachedView.angles = angles;
  590. m_CachedView.fov = fov;
  591. }
  592. void C_ReplayCamera::GetCachedView( Vector &origin, QAngle &angles, float &fov )
  593. {
  594. origin = m_CachedView.origin;
  595. angles = m_CachedView.angles;
  596. fov = m_CachedView.fov;
  597. }
  598. void C_ReplayCamera::SetMode(int iMode)
  599. {
  600. if ( m_nCameraMode == iMode )
  601. return;
  602. Assert( iMode > OBS_MODE_NONE && iMode <= LAST_PLAYER_OBSERVERMODE );
  603. m_nCameraMode = iMode;
  604. if ( m_nCameraMode != OBS_MODE_ROAMING && m_nCameraMode != OBS_MODE_CHASE )
  605. {
  606. ClearOverrideView();
  607. }
  608. }
  609. void C_ReplayCamera::SetPrimaryTarget( int nEntity )
  610. {
  611. if ( m_iTarget1 == nEntity )
  612. return;
  613. m_iTarget1 = nEntity;
  614. if ( GetMode() == OBS_MODE_ROAMING )
  615. {
  616. Vector vOrigin;
  617. QAngle aAngles;
  618. float flFov;
  619. CalcChaseCamView( vOrigin, aAngles, flFov, 0.015f );
  620. }
  621. else if ( GetMode() == OBS_MODE_CHASE )
  622. {
  623. C_BaseEntity* target = ClientEntityList().GetEnt( m_iTarget1 );
  624. if ( target )
  625. {
  626. QAngle eyeAngle = target->EyeAngles();
  627. prediction->SetViewAngles( eyeAngle );
  628. }
  629. }
  630. m_flLastDistance = m_flDistance;
  631. m_flLastAngleUpdateTime = -1;
  632. }
  633. void C_ReplayCamera::SpecNextPlayer( bool bInverse )
  634. {
  635. int start = 1;
  636. if ( m_iTarget1 > 0 && m_iTarget1 <= gpGlobals->maxClients )
  637. start = m_iTarget1;
  638. int index = start;
  639. while ( true )
  640. {
  641. // got next/prev player
  642. if ( bInverse )
  643. index--;
  644. else
  645. index++;
  646. // check bounds
  647. if ( index < 1 )
  648. index = gpGlobals->maxClients;
  649. else if ( index > gpGlobals->maxClients )
  650. index = 1;
  651. if ( index == start )
  652. break; // couldn't find a new valid player
  653. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( index );
  654. if ( !pPlayer )
  655. continue;
  656. // only follow living players
  657. if ( pPlayer->IsObserver() )
  658. continue;
  659. break; // found a new player
  660. }
  661. SetPrimaryTarget( index );
  662. // turn off auto director once user tried to change view settings
  663. SetAutoDirector( false );
  664. }
  665. void C_ReplayCamera::SpecPlayerByPredicate( const char *szSearch )
  666. {
  667. CBasePlayer *pPlayer = UTIL_PlayerByCommandArg( szSearch );
  668. if ( !pPlayer )
  669. return;
  670. // only follow living players or dedicated spectators
  671. if ( pPlayer->IsObserver() && pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  672. return;
  673. SetPrimaryTarget( pPlayer->entindex() );
  674. return;
  675. }
  676. void C_ReplayCamera::FireGameEvent( IGameEvent * event)
  677. {
  678. if ( !g_pEngineClientReplay->IsPlayingReplayDemo() )
  679. return; // not in Replay mode
  680. const char *type = event->GetName();
  681. if ( Q_strcmp( "game_newmap", type ) == 0 )
  682. {
  683. // Do not reset the camera, since we reload the map when "rewinding"
  684. // and want to keep our camera settings intact.
  685. // Reset(); // reset all camera settings
  686. // show spectator UI
  687. if ( !gViewPortInterface )
  688. return;
  689. if ( g_pEngineClientReplay->IsPlayingReplayDemo() )
  690. {
  691. SetMode( OBS_MODE_IN_EYE );
  692. CReplay *pReplay = g_pReplayManager->GetPlayingReplay();
  693. SetPrimaryTarget( ( pReplay && pReplay->m_nPlayerSlot >= 0 ) ? pReplay->m_nPlayerSlot : 0 );
  694. }
  695. else
  696. {
  697. // during live broadcast only show black bars
  698. gViewPortInterface->ShowPanel( PANEL_SPECMENU, true );
  699. }
  700. return;
  701. }
  702. // after this only auto-director commands follow
  703. // don't execute them is autodirector is off and PVS is unlocked
  704. if ( !spec_autodirector.GetBool() && !IsPVSLocked() )
  705. return;
  706. }
  707. // this is a cheap version of FullNoClipMove():
  708. void C_ReplayCamera::CreateMove( CUserCmd *cmd)
  709. {
  710. if ( cmd )
  711. {
  712. m_LastCmd = *cmd;
  713. }
  714. }
  715. void C_ReplayCamera::SetCameraAngle( QAngle& targetAngle )
  716. {
  717. m_aCamAngle = targetAngle;
  718. NormalizeAngles( m_aCamAngle );
  719. m_flLastAngleUpdateTime = gpGlobals->realtime;
  720. }
  721. void C_ReplayCamera::SmoothCameraAngle( QAngle& targetAngle )
  722. {
  723. if ( m_flLastAngleUpdateTime > 0 )
  724. {
  725. float deltaTime = gpGlobals->realtime - m_flLastAngleUpdateTime;
  726. deltaTime = clamp( deltaTime*m_flInertia, 0.01f, 1.f);
  727. InterpolateAngles( m_aCamAngle, targetAngle, m_aCamAngle, deltaTime );
  728. }
  729. else
  730. {
  731. m_aCamAngle = targetAngle;
  732. }
  733. m_flLastAngleUpdateTime = gpGlobals->realtime;
  734. }
  735. bool C_ReplayCamera::IsPVSLocked()
  736. {
  737. return false;
  738. }
  739. void C_ReplayCamera::SetAutoDirector( bool bActive )
  740. {
  741. spec_autodirector.SetValue( bActive?1:0 );
  742. }
  743. #endif