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.

857 lines
19 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #if defined( REPLAY_ENABLED )
  8. #include "replaycamera.h"
  9. #include "cdll_client_int.h"
  10. #include "util_shared.h"
  11. #include "prediction.h"
  12. #include "movevars_shared.h"
  13. #include "in_buttons.h"
  14. #include "text_message.h"
  15. #include "vgui_controls/Controls.h"
  16. #include "vgui/ILocalize.h"
  17. #include "vguicenterprint.h"
  18. #include "game/client/iviewport.h"
  19. #include <keyvalues.h>
  20. #ifdef CSTRIKE_DLL
  21. #include "c_cs_player.h"
  22. #endif
  23. //ConVar spec_autodirector( "spec_autodirector", "1", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director chooses best view modes while spectating" );
  24. extern ConVar spec_autodirector;
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. #define CHASE_CAM_DISTANCE 76.0f
  28. #define WALL_OFFSET 6.0f
  29. static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
  30. static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
  31. static const ConVar *replay_transmitall = NULL;
  32. //////////////////////////////////////////////////////////////////////
  33. // Construction/Destruction
  34. //////////////////////////////////////////////////////////////////////
  35. // converts all '\r' characters to '\n', so that the engine can deal with the properly
  36. // returns a pointer to str
  37. static wchar_t* ConvertCRtoNL( wchar_t *str )
  38. {
  39. for ( wchar_t *ch = str; *ch != 0; ch++ )
  40. if ( *ch == L'\r' )
  41. *ch = L'\n';
  42. return str;
  43. }
  44. static C_ReplayCamera s_ReplayCamera;
  45. C_ReplayCamera *ReplayCamera() { return &s_ReplayCamera; }
  46. C_ReplayCamera::C_ReplayCamera()
  47. {
  48. Reset();
  49. m_nNumSpectators = 0;
  50. m_szTitleText[0] = 0;
  51. }
  52. C_ReplayCamera::~C_ReplayCamera()
  53. {
  54. }
  55. void C_ReplayCamera::Init()
  56. {
  57. ListenForGameEvent( "game_newmap" );
  58. ListenForGameEvent( "replay_cameraman" );
  59. ListenForGameEvent( "replay_fixed" );
  60. ListenForGameEvent( "replay_chase" );
  61. ListenForGameEvent( "replay_message" );
  62. ListenForGameEvent( "replay_title" );
  63. ListenForGameEvent( "replay_status" );
  64. Reset();
  65. m_nNumSpectators = 0;
  66. m_szTitleText[0] = 0;
  67. // get a handle to the engine convar
  68. replay_transmitall = cvar->FindVar( "replay_transmitall" );
  69. }
  70. void C_ReplayCamera::Reset()
  71. {
  72. m_nCameraMode = OBS_MODE_FIXED;
  73. m_iCameraMan = 0;
  74. m_iTraget1 = m_iTraget2 = 0;
  75. m_flFOV = 90;
  76. m_flDistance = m_flLastDistance = 96.0f;
  77. m_flInertia = 3.0f;
  78. m_flPhi = 0;
  79. m_flTheta = 0;
  80. m_flOffset = 0;
  81. m_bEntityPacketReceived = false;
  82. m_vCamOrigin.Init();
  83. m_aCamAngle.Init();
  84. m_LastCmd.Reset();
  85. m_vecVelocity.Init();
  86. }
  87. void C_ReplayCamera::CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  88. {
  89. bool bManual = !spec_autodirector.GetBool(); // chase camera controlled manually
  90. Vector targetOrigin1, targetOrigin2, cameraOrigin, forward;
  91. if ( m_iTraget1 == 0 )
  92. return;
  93. // get primary target, also translates to ragdoll
  94. C_BaseEntity *target1 = GetPrimaryTarget();
  95. if ( !target1 )
  96. return;
  97. if ( target1->IsAlive() && target1->IsDormant() )
  98. return;
  99. targetOrigin1 = target1->GetRenderOrigin();
  100. if ( !target1->IsAlive() )
  101. {
  102. targetOrigin1 += VEC_DEAD_VIEWHEIGHT;
  103. }
  104. else if ( target1->GetFlags() & FL_DUCKING )
  105. {
  106. targetOrigin1 += VEC_DUCK_VIEW;
  107. }
  108. else
  109. {
  110. targetOrigin1 += VEC_VIEW;
  111. }
  112. // get secondary target if set
  113. C_BaseEntity *target2 = NULL;
  114. if ( m_iTraget2 > 0 && (m_iTraget2 != m_iTraget1) && !bManual )
  115. {
  116. target2 = ClientEntityList().GetBaseEntity( m_iTraget2 );
  117. // if target is out PVS and not dead, it's not valid
  118. if ( target2 && target2->IsDormant() && target2->IsAlive() )
  119. target2 = NULL;
  120. if ( target2 )
  121. {
  122. targetOrigin2 = target2->GetRenderOrigin();
  123. if ( !target2->IsAlive() )
  124. {
  125. targetOrigin2 += VEC_DEAD_VIEWHEIGHT;
  126. }
  127. else if ( target2->GetFlags() & FL_DUCKING )
  128. {
  129. targetOrigin2 += VEC_DUCK_VIEW;
  130. }
  131. else
  132. {
  133. targetOrigin2 += VEC_VIEW;
  134. }
  135. }
  136. }
  137. // apply angle offset & smoothing
  138. QAngle angleOffset( m_flPhi, m_flTheta, 0 );
  139. QAngle cameraAngles = m_aCamAngle;
  140. if ( bManual )
  141. {
  142. // let spectator choose the view angles
  143. engine->GetViewAngles( cameraAngles );
  144. }
  145. else if ( target2 )
  146. {
  147. // look into direction of second target
  148. forward = targetOrigin2 - targetOrigin1;
  149. VectorAngles( forward, cameraAngles );
  150. cameraAngles.z = 0; // no ROLL
  151. }
  152. else if ( m_iTraget2 == 0 || m_iTraget2 == m_iTraget1)
  153. {
  154. // look into direction where primary target is looking
  155. cameraAngles = target1->EyeAngles();
  156. cameraAngles.x = 0; // no PITCH
  157. cameraAngles.z = 0; // no ROLL
  158. }
  159. else
  160. {
  161. // target2 is missing, just keep angelsm, reset offset
  162. angleOffset.Init();
  163. }
  164. if ( !bManual )
  165. {
  166. if ( !target1->IsAlive() )
  167. {
  168. angleOffset.x = 15;
  169. }
  170. cameraAngles += angleOffset;
  171. }
  172. AngleVectors( cameraAngles, &forward );
  173. VectorNormalize( forward );
  174. // calc optimal camera position
  175. VectorMA(targetOrigin1, -m_flDistance, forward, cameraOrigin );
  176. targetOrigin1.z += m_flOffset; // add offset
  177. // clip against walls
  178. trace_t trace;
  179. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  180. UTIL_TraceHull( targetOrigin1, cameraOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, target1, COLLISION_GROUP_NONE, &trace );
  181. C_BaseEntity::PopEnableAbsRecomputations();
  182. float dist = VectorLength( trace.endpos - targetOrigin1 );
  183. // grow distance by 32 unit a second
  184. m_flLastDistance += gpGlobals->frametime * 32.0f;
  185. if ( dist > m_flLastDistance )
  186. {
  187. VectorMA(targetOrigin1, -m_flLastDistance, forward, cameraOrigin );
  188. }
  189. else
  190. {
  191. cameraOrigin = trace.endpos;
  192. m_flLastDistance = dist;
  193. }
  194. if ( target2 )
  195. {
  196. // if we have 2 targets look at point between them
  197. forward = (targetOrigin1+targetOrigin2)/2 - cameraOrigin;
  198. QAngle angle;
  199. VectorAngles( forward, angle );
  200. cameraAngles.y = angle.y;
  201. NormalizeAngles( cameraAngles );
  202. cameraAngles.x = clamp( cameraAngles.x, -60, 60 );
  203. SmoothCameraAngle( cameraAngles );
  204. }
  205. else
  206. {
  207. SetCameraAngle( cameraAngles );
  208. }
  209. VectorCopy( cameraOrigin, m_vCamOrigin );
  210. VectorCopy( m_aCamAngle, eyeAngles );
  211. VectorCopy( m_vCamOrigin, eyeOrigin );
  212. }
  213. int C_ReplayCamera::GetMode()
  214. {
  215. if ( m_iCameraMan > 0 )
  216. {
  217. C_BasePlayer *pCameraMan = UTIL_PlayerByIndex( m_iCameraMan );
  218. if ( pCameraMan )
  219. return pCameraMan->GetObserverMode();
  220. }
  221. return m_nCameraMode;
  222. }
  223. C_BaseEntity* C_ReplayCamera::GetPrimaryTarget()
  224. {
  225. if ( m_iCameraMan > 0 )
  226. {
  227. C_BasePlayer *pCameraMan = UTIL_PlayerByIndex( m_iCameraMan );
  228. if ( pCameraMan )
  229. {
  230. return pCameraMan->GetObserverTarget();
  231. }
  232. }
  233. if ( m_iTraget1 <= 0 )
  234. return NULL;
  235. C_BaseEntity* target = ClientEntityList().GetEnt( m_iTraget1 );
  236. return target;
  237. }
  238. C_BaseEntity *C_ReplayCamera::GetCameraMan()
  239. {
  240. return ClientEntityList().GetEnt( m_iCameraMan );
  241. }
  242. void C_ReplayCamera::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  243. {
  244. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_iTraget1 );
  245. if ( !pPlayer )
  246. return;
  247. if ( !pPlayer->IsAlive() )
  248. {
  249. // if dead, show from 3rd person
  250. CalcChaseCamView( eyeOrigin, eyeAngles, fov );
  251. return;
  252. }
  253. m_aCamAngle = pPlayer->EyeAngles();
  254. m_vCamOrigin = pPlayer->GetAbsOrigin();
  255. m_flFOV = pPlayer->GetFOV();
  256. if ( pPlayer->GetFlags() & FL_DUCKING )
  257. {
  258. m_vCamOrigin += VEC_DUCK_VIEW;
  259. }
  260. else
  261. {
  262. m_vCamOrigin += VEC_VIEW;
  263. }
  264. eyeOrigin = m_vCamOrigin;
  265. eyeAngles = m_aCamAngle;
  266. fov = m_flFOV;
  267. pPlayer->CalcViewModelView( eyeOrigin, eyeAngles);
  268. UpdateViewmodelVisibility( pPlayer );
  269. }
  270. void C_ReplayCamera::Accelerate( Vector& wishdir, float wishspeed, float accel )
  271. {
  272. float addspeed, accelspeed, currentspeed;
  273. // See if we are changing direction a bit
  274. currentspeed =m_vecVelocity.Dot(wishdir);
  275. // Reduce wishspeed by the amount of veer.
  276. addspeed = wishspeed - currentspeed;
  277. // If not going to add any speed, done.
  278. if (addspeed <= 0)
  279. return;
  280. // Determine amount of acceleration.
  281. accelspeed = accel * gpGlobals->frametime * wishspeed;
  282. // Cap at addspeed
  283. if (accelspeed > addspeed)
  284. accelspeed = addspeed;
  285. // Adjust velocity.
  286. for (int i=0 ; i<3 ; i++)
  287. {
  288. m_vecVelocity[i] += accelspeed * wishdir[i];
  289. }
  290. }
  291. // movement code is a copy of CGameMovement::FullNoClipMove()
  292. void C_ReplayCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  293. {
  294. // only if PVS isn't locked by auto-director
  295. if ( !IsPVSLocked() )
  296. {
  297. Vector wishvel;
  298. Vector forward, right, up;
  299. Vector wishdir;
  300. float wishspeed;
  301. float factor = sv_specspeed.GetFloat();
  302. float maxspeed = sv_maxspeed.GetFloat() * factor;
  303. AngleVectors ( m_LastCmd.viewangles, &forward, &right, &up); // Determine movement angles
  304. if ( m_LastCmd.buttons & IN_SPEED )
  305. {
  306. factor /= 2.0f;
  307. }
  308. // Copy movement amounts
  309. float fmove = m_LastCmd.forwardmove * factor;
  310. float smove = m_LastCmd.sidemove * factor;
  311. VectorNormalize (forward); // Normalize remainder of vectors
  312. VectorNormalize (right); //
  313. for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  314. wishvel[i] = forward[i]*fmove + right[i]*smove;
  315. wishvel[2] += m_LastCmd.upmove * factor;
  316. VectorCopy (wishvel, wishdir); // Determine magnitude of speed of move
  317. wishspeed = VectorNormalize(wishdir);
  318. //
  319. // Clamp to server defined max speed
  320. //
  321. if (wishspeed > maxspeed )
  322. {
  323. VectorScale (wishvel, maxspeed/wishspeed, wishvel);
  324. wishspeed = maxspeed;
  325. }
  326. if ( sv_specaccelerate.GetFloat() > 0.0 )
  327. {
  328. // Set move velocity
  329. Accelerate ( wishdir, wishspeed, sv_specaccelerate.GetFloat() );
  330. float spd = VectorLength( m_vecVelocity );
  331. if (spd < 1.0f)
  332. {
  333. m_vecVelocity.Init();
  334. }
  335. else
  336. {
  337. // Bleed off some speed, but if we have less than the bleed
  338. // threshold, bleed the threshold amount.
  339. float control = (spd < maxspeed/4.0) ? maxspeed/4.0 : spd;
  340. float friction = sv_friction.GetFloat();
  341. // Add the amount to the drop amount.
  342. float drop = control * friction * gpGlobals->frametime;
  343. // scale the velocity
  344. float newspeed = spd - drop;
  345. if (newspeed < 0)
  346. newspeed = 0;
  347. // Determine proportion of old speed we are using.
  348. newspeed /= spd;
  349. VectorScale( m_vecVelocity, newspeed, m_vecVelocity );
  350. }
  351. }
  352. else
  353. {
  354. VectorCopy( wishvel, m_vecVelocity );
  355. }
  356. // Just move ( don't clip or anything )
  357. VectorMA( m_vCamOrigin, gpGlobals->frametime, m_vecVelocity, m_vCamOrigin );
  358. // get camera angle directly from engine
  359. engine->GetViewAngles( m_aCamAngle );
  360. // Zero out velocity if in noaccel mode
  361. if ( sv_specaccelerate.GetFloat() < 0.0f )
  362. {
  363. m_vecVelocity.Init();
  364. }
  365. }
  366. eyeOrigin = m_vCamOrigin;
  367. eyeAngles = m_aCamAngle;
  368. fov = m_flFOV;
  369. }
  370. void C_ReplayCamera::CalcFixedView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  371. {
  372. eyeOrigin = m_vCamOrigin;
  373. eyeAngles = m_aCamAngle;
  374. fov = m_flFOV;
  375. if ( m_iTraget1 == 0 )
  376. return;
  377. C_BaseEntity * target = ClientEntityList().GetBaseEntity( m_iTraget1 );
  378. if ( target && target->IsAlive() )
  379. {
  380. // if we're chasing a target, change viewangles
  381. QAngle angle;
  382. VectorAngles( (target->GetAbsOrigin()+VEC_VIEW) - m_vCamOrigin, angle );
  383. SmoothCameraAngle( angle );
  384. }
  385. }
  386. void C_ReplayCamera::PostEntityPacketReceived()
  387. {
  388. m_bEntityPacketReceived = true;
  389. }
  390. void C_ReplayCamera::FixupMovmentParents()
  391. {
  392. // Find resource zone
  393. for ( ClientEntityHandle_t e = ClientEntityList().FirstHandle();
  394. e != ClientEntityList().InvalidHandle(); e = ClientEntityList().NextHandle( e ) )
  395. {
  396. C_BaseEntity *ent = C_BaseEntity::Instance( e );
  397. if ( !ent )
  398. continue;
  399. ent->HierarchyUpdateMoveParent();
  400. }
  401. }
  402. void C_ReplayCamera::CalcView(Vector& origin, QAngle& angles, float& fov)
  403. {
  404. if ( m_bEntityPacketReceived )
  405. {
  406. // try to fixup movment pareents
  407. FixupMovmentParents();
  408. m_bEntityPacketReceived = false;
  409. }
  410. if ( m_iCameraMan > 0 )
  411. {
  412. C_BasePlayer *pCameraMan = UTIL_PlayerByIndex( m_iCameraMan );
  413. if ( pCameraMan )
  414. {
  415. float zNear,zFar;
  416. pCameraMan->CalcView( origin, angles, zNear, zFar, fov );
  417. pCameraMan->CalcViewModelView( origin, angles );
  418. return;
  419. }
  420. }
  421. switch ( m_nCameraMode )
  422. {
  423. case OBS_MODE_ROAMING : CalcRoamingView( origin, angles, fov );
  424. break;
  425. case OBS_MODE_FIXED : CalcFixedView( origin, angles, fov );
  426. break;
  427. case OBS_MODE_IN_EYE : CalcInEyeCamView( origin, angles, fov );
  428. break;
  429. case OBS_MODE_CHASE : CalcChaseCamView( origin, angles, fov );
  430. break;
  431. }
  432. }
  433. void C_ReplayCamera::SetMode(int iMode)
  434. {
  435. if ( m_nCameraMode == iMode )
  436. return;
  437. Assert( iMode > OBS_MODE_NONE && iMode <= LAST_PLAYER_OBSERVERMODE );
  438. m_nCameraMode = iMode;
  439. }
  440. void C_ReplayCamera::SetPrimaryTarget( int nEntity )
  441. {
  442. if ( m_iTraget1 == nEntity )
  443. return;
  444. m_iTraget1 = nEntity;
  445. if ( GetMode() == OBS_MODE_ROAMING )
  446. {
  447. Vector vOrigin;
  448. QAngle aAngles;
  449. float flFov;
  450. CalcChaseCamView( vOrigin, aAngles, flFov );
  451. }
  452. else if ( GetMode() == OBS_MODE_CHASE )
  453. {
  454. C_BaseEntity* target = ClientEntityList().GetEnt( m_iTraget1 );
  455. if ( target )
  456. {
  457. QAngle eyeAngle = target->EyeAngles();
  458. prediction->SetViewAngles( eyeAngle );
  459. }
  460. }
  461. m_flLastDistance = m_flDistance;
  462. m_flLastAngleUpdateTime = -1;
  463. }
  464. void C_ReplayCamera::SpecNextPlayer( bool bInverse )
  465. {
  466. int start = 1;
  467. if ( m_iTraget1 > 0 && m_iTraget1 <= gpGlobals->maxClients )
  468. start = m_iTraget1;
  469. int index = start;
  470. while ( true )
  471. {
  472. // got next/prev player
  473. if ( bInverse )
  474. index--;
  475. else
  476. index++;
  477. // check bounds
  478. if ( index < 1 )
  479. index = gpGlobals->maxClients;
  480. else if ( index > gpGlobals->maxClients )
  481. index = 1;
  482. if ( index == start )
  483. break; // couldn't find a new valid player
  484. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( index );
  485. if ( !pPlayer )
  486. continue;
  487. // only follow living players
  488. if ( pPlayer->IsObserver() )
  489. continue;
  490. break; // found a new player
  491. }
  492. SetPrimaryTarget( index );
  493. // turn off auto director once user tried to change view settings
  494. SetAutoDirector( false );
  495. }
  496. void C_ReplayCamera::SpecNamedPlayer( const char *szPlayerName )
  497. {
  498. for ( int index = 1; index <= gpGlobals->maxClients; ++index )
  499. {
  500. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( index );
  501. if ( !pPlayer )
  502. continue;
  503. if ( !FStrEq( szPlayerName, pPlayer->GetPlayerName() ) )
  504. continue;
  505. // only follow living players or dedicated spectators
  506. if ( pPlayer->IsObserver() && pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  507. continue;
  508. SetPrimaryTarget( index );
  509. return;
  510. }
  511. }
  512. void C_ReplayCamera::FireGameEvent( IGameEvent * event)
  513. {
  514. if ( !engine->IsReplay() )
  515. return; // not in Replay mode
  516. const char *type = event->GetName();
  517. if ( Q_strcmp( "game_newmap", type ) == 0 )
  518. {
  519. Reset(); // reset all camera settings
  520. // show spectator UI
  521. if ( !GetViewPortInterface() )
  522. return;
  523. if ( engine->IsPlayingDemo() )
  524. {
  525. // for demo playback show full menu
  526. GetViewPortInterface()->ShowPanel( PANEL_SPECMENU, true );
  527. SetMode( OBS_MODE_ROAMING );
  528. }
  529. else
  530. {
  531. // during live broadcast only show black bars
  532. GetViewPortInterface()->ShowPanel( PANEL_SPECGUI, true );
  533. }
  534. return;
  535. }
  536. if ( Q_strcmp( "replay_message", type ) == 0 )
  537. {
  538. wchar_t outputBuf[1024];
  539. const char *pszText = event->GetString( "text", "" );
  540. char *tmpStr = hudtextmessage->LookupString( pszText );
  541. const wchar_t *pBuf = g_pVGuiLocalize->Find( tmpStr );
  542. if ( pBuf )
  543. {
  544. // Copy pBuf into szBuf[i].
  545. int nMaxChars = sizeof( outputBuf ) / sizeof( wchar_t );
  546. wcsncpy( outputBuf, pBuf, nMaxChars );
  547. outputBuf[nMaxChars-1] = 0;
  548. }
  549. else
  550. {
  551. g_pVGuiLocalize->ConvertANSIToUnicode( tmpStr, outputBuf, sizeof(outputBuf) );
  552. }
  553. GetCenterPrint()->Print( ConvertCRtoNL( outputBuf ) );
  554. return ;
  555. }
  556. if ( Q_strcmp( "replay_title", type ) == 0 )
  557. {
  558. Q_strncpy( m_szTitleText, event->GetString( "text", "" ), sizeof(m_szTitleText) );
  559. return;
  560. }
  561. if ( Q_strcmp( "replay_status", type ) == 0 )
  562. {
  563. int nNumProxies = event->GetInt( "proxies" );
  564. m_nNumSpectators = event->GetInt( "clients" ) - nNumProxies;
  565. return;
  566. }
  567. // after this only auto-director commands follow
  568. // don't execute them is autodirector is off and PVS is unlocked
  569. if ( !spec_autodirector.GetBool() && !IsPVSLocked() )
  570. return;
  571. if ( Q_strcmp( "replay_cameraman", type ) == 0 )
  572. {
  573. Reset();
  574. m_nCameraMode = OBS_MODE_ROAMING;
  575. m_iCameraMan = event->GetInt( "index" );
  576. return;
  577. }
  578. if ( Q_strcmp( "replay_fixed", type ) == 0 )
  579. {
  580. m_iCameraMan = 0;
  581. m_vCamOrigin.x = event->GetInt( "posx" );
  582. m_vCamOrigin.y = event->GetInt( "posy" );
  583. m_vCamOrigin.z = event->GetInt( "posz" );
  584. QAngle angle;
  585. angle.x = event->GetInt( "theta" );
  586. angle.y = event->GetInt( "phi" );
  587. angle.z = 0; // no roll yet
  588. if ( m_nCameraMode != OBS_MODE_FIXED )
  589. {
  590. SetMode( OBS_MODE_FIXED );
  591. SetCameraAngle( angle );
  592. m_flFOV = event->GetFloat( "fov", 90 );
  593. }
  594. SetPrimaryTarget( event->GetInt( "target" ) );
  595. if ( m_iTraget1 == 0 )
  596. {
  597. SetCameraAngle( angle );
  598. }
  599. return;
  600. }
  601. if ( Q_strcmp( "replay_chase", type ) == 0 )
  602. {
  603. bool bInEye = event->GetInt( "ineye" );
  604. // check if we are already in a player chase mode
  605. bool bIsInChaseMode = (m_nCameraMode==OBS_MODE_IN_EYE)|| (m_nCameraMode==OBS_MODE_CHASE);
  606. // if we are in auto director or not in a valid chase mode, set new mode now
  607. if ( spec_autodirector.GetBool() || !bIsInChaseMode )
  608. {
  609. SetMode( bInEye?OBS_MODE_IN_EYE:OBS_MODE_CHASE );
  610. }
  611. m_iCameraMan = 0;
  612. m_iTraget2 = event->GetInt( "target2" );
  613. m_flDistance = event->GetFloat( "distance", m_flDistance );
  614. m_flOffset = event->GetFloat( "offset", m_flOffset );
  615. m_flTheta = event->GetFloat( "theta", m_flTheta );
  616. m_flPhi = event->GetFloat( "phi", m_flPhi );
  617. m_flFOV = event->GetFloat( "fov", 90 );
  618. m_flInertia = event->GetFloat( "inertia", 30.f ) / 10.f;
  619. // if inertia is not set use standard value
  620. if ( m_flInertia <= 0 )
  621. m_flInertia = 3.0f;
  622. SetPrimaryTarget( event->GetInt( "target1" ) );
  623. return;
  624. }
  625. }
  626. // this is a cheap version of FullNoClipMove():
  627. void C_ReplayCamera::CreateMove( CUserCmd *cmd)
  628. {
  629. if ( cmd )
  630. {
  631. m_LastCmd = *cmd;
  632. }
  633. }
  634. void C_ReplayCamera::SetCameraAngle( QAngle& targetAngle )
  635. {
  636. m_aCamAngle = targetAngle;
  637. NormalizeAngles( m_aCamAngle );
  638. m_flLastAngleUpdateTime = gpGlobals->realtime;
  639. }
  640. void C_ReplayCamera::SmoothCameraAngle( QAngle& targetAngle )
  641. {
  642. if ( m_flLastAngleUpdateTime > 0 )
  643. {
  644. float deltaTime = gpGlobals->realtime - m_flLastAngleUpdateTime;
  645. deltaTime = clamp( deltaTime*m_flInertia, 0.01, 1);
  646. InterpolateAngles( m_aCamAngle, targetAngle, m_aCamAngle, deltaTime );
  647. }
  648. else
  649. {
  650. m_aCamAngle = targetAngle;
  651. }
  652. m_flLastAngleUpdateTime = gpGlobals->realtime;
  653. }
  654. void C_ReplayCamera::ToggleChaseAsFirstPerson()
  655. {
  656. if ( GetMode() == OBS_MODE_CHASE )
  657. {
  658. SetMode( OBS_MODE_IN_EYE );
  659. }
  660. else if ( GetMode() == OBS_MODE_IN_EYE )
  661. {
  662. SetMode( OBS_MODE_CHASE );
  663. }
  664. }
  665. bool C_ReplayCamera::IsPVSLocked()
  666. {
  667. if ( replay_transmitall != NULL )
  668. {
  669. return !replay_transmitall->GetBool();
  670. }
  671. else
  672. {
  673. //old style, assume locked unless we playback a demo
  674. return !engine->IsPlayingDemo();
  675. }
  676. }
  677. void C_ReplayCamera::SetAutoDirector( bool bActive )
  678. {
  679. spec_autodirector.SetValue( bActive?1:0 );
  680. }
  681. #endif