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.

871 lines
19 KiB

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