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.

1858 lines
50 KiB

  1. //========= Copyright 1996-2005, 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. #include "matchmaking/imatchframework.h"
  20. #include "iloadingdisc.h"
  21. #include "view_shared.h"
  22. #include "view.h"
  23. #include "ivrenderview.h"
  24. #include "c_plantedc4.h"
  25. #include "basecsgrenade_projectile.h"
  26. #include "ivieweffects.h"
  27. #include "cs_hud_chat.h"
  28. #include "in_buttons.h"
  29. #include <vgui/IInput.h>
  30. #include "vgui_controls/Controls.h"
  31. #include "hltvreplaysystem.h"
  32. #ifdef CSTRIKE_DLL
  33. #include "c_cs_player.h"
  34. #include "cs_gamerules.h"
  35. #include "c_team.h"
  36. #endif
  37. static void Spec_Autodirector_Cameraman_Callback( IConVar *pConVar, const char *pOldString, float flOldValue )
  38. {
  39. if ( HLTVCamera() && HLTVCamera()->AutoDirectorState() != C_HLTVCamera::AUTODIRECTOR_OFF )
  40. {
  41. HLTVCamera()->SetAutoDirector(C_HLTVCamera::AUTODIRECTOR_ON);
  42. }
  43. }
  44. ConVar spec_autodirector( "spec_autodirector", "1", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director chooses best view modes while spectating" );
  45. ConVar spec_autodirector_pausetime( "spec_autodirector_pausetime", "10", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director will pause for this long if a player is selected." );
  46. ConVar spec_autodirector_cameraman( "spec_autodirector_cameraman", "-1", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Cameraman account ID. If a cameraman is active then use them when spectating and autodirector is active, 0 = no caster", Spec_Autodirector_Cameraman_Callback );
  47. ConVar spec_overwatch_skip_idle_ticks( "spec_overwatch_skip_idle_ticks", "10", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director in overwatch mode will be skipping ticks when no subject observations are played." );
  48. extern ConVar view_recoil_tracking;
  49. // memdbgon must be the last include file in a .cpp file!!!
  50. #include "tier0/memdbgon.h"
  51. #define CHASE_CAM_DISTANCE 76.0f
  52. #define WALL_OFFSET 6.0f
  53. static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
  54. static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
  55. static const ConVar *tv_transmitall = NULL;
  56. //////////////////////////////////////////////////////////////////////
  57. // Construction/Destruction
  58. //////////////////////////////////////////////////////////////////////
  59. static C_HLTVCamera s_HLTVCamera;
  60. C_HLTVCamera *HLTVCamera() { return &s_HLTVCamera; }
  61. C_HLTVCamera::C_HLTVCamera()
  62. {
  63. Reset();
  64. m_nNumSpectators = 0;
  65. m_szTitleText[0] = 0;
  66. }
  67. C_HLTVCamera::~C_HLTVCamera()
  68. {
  69. }
  70. void C_HLTVCamera::Init()
  71. {
  72. ListenForGameEvent( "game_newmap" );
  73. #ifdef CAMERAMAN_OLD_WAY
  74. ListenForGameEvent( "hltv_cameraman" );
  75. #endif
  76. ListenForGameEvent( "hltv_fixed" );
  77. ListenForGameEvent( "hltv_chase" );
  78. ListenForGameEvent( "hltv_message" );
  79. ListenForGameEvent( "hltv_title" );
  80. ListenForGameEvent( "hltv_status" );
  81. ListenForGameEvent( "player_connect" );
  82. ListenForGameEvent( "player_connect_full" );
  83. ListenForGameEvent( "player_team" );
  84. Reset();
  85. m_nNumSpectators = 0;
  86. m_szTitleText[0] = 0;
  87. // get a handle to the engine convar
  88. tv_transmitall = cvar->FindVar( "tv_transmitall" );
  89. }
  90. void C_HLTVCamera::Reset()
  91. {
  92. m_nCameraMode = OBS_MODE_FIXED;
  93. m_iCameraMan = 0;
  94. m_iTarget1 = m_iTarget2 = m_iLastTarget1 = 0;
  95. m_flFOV = 90;
  96. m_flDistance = m_flLastDistance = CHASE_CAM_DISTANCE;
  97. m_flInertia = 3.0f;
  98. m_flPhi = 0;
  99. m_flTheta = 0;
  100. m_flOffset = 0;
  101. m_bEntityPacketReceived = false;
  102. m_vCamOrigin.Zero();
  103. m_aCamAngle.Init();
  104. m_LastCmd.Reset();
  105. m_vecVelocity.Init();
  106. m_vIdealOverviewPos.Zero();
  107. m_vOldOverviewPos.Zero();
  108. m_vLastGrenadeVelocity.Zero();
  109. m_flLastGrenadeVelocityUpdate = 0;
  110. m_flLastCamZPos = 0;
  111. m_flAutodirectorPausedTime = -1.0f;
  112. m_flIdealOverviewScale = 1.0f;
  113. m_flNextIdealOverviewPosUpdate = 0;
  114. m_bIsSpecLerping = false;
  115. m_vecSpecLerpIdealPos = Vector( 0, 0, 0 );
  116. m_angSpecLerpIdealAng = QAngle( 0, 0, 0 );
  117. m_vecSpecLerpOldPos = Vector( 0, 0, 0 );
  118. m_angSpecLerpOldAng = QAngle( 0, 0, 0 );
  119. m_flSpecLerpEndTime = 0.0f;
  120. m_flSpecLerpTime = 1.0f;
  121. m_bIsFollowingGrenade = false;
  122. }
  123. void C_HLTVCamera::SetWatchingGrenade( C_BaseEntity *pGrenade, bool bWatching )
  124. {
  125. if ( bWatching )
  126. {
  127. if ( pGrenade && m_bIsFollowingGrenade == false )
  128. {
  129. m_bIsFollowingGrenade = true;
  130. SetPrimaryTarget( pGrenade->entindex() );
  131. }
  132. }
  133. else
  134. {
  135. // only change state if we get a false
  136. //if ( pGrenade->entindex() == m_iTarget1 )
  137. {
  138. if ( m_bIsFollowingGrenade == true )
  139. {
  140. C_BaseEntity *pLastTarget = ClientEntityList().GetBaseEntity( m_iLastTarget1 );
  141. if ( !pLastTarget || pLastTarget->IsDormant() || !pLastTarget->IsAlive() )
  142. {
  143. // find a player that is alive to go to
  144. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  145. {
  146. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  147. if ( pPlayer && pPlayer->IsAlive() && (pPlayer->GetTeamNumber() == TEAM_TERRORIST || pPlayer->GetTeamNumber() == TEAM_CT) )
  148. {
  149. m_iLastTarget1 = pPlayer->entindex();
  150. break;
  151. }
  152. }
  153. }
  154. m_iTarget1 = m_iLastTarget1;
  155. }
  156. m_bIsFollowingGrenade = false;
  157. }
  158. }
  159. }
  160. void C_HLTVCamera::CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  161. {
  162. Vector targetOrigin1, targetOrigin2, cameraOrigin, forward;
  163. if ( m_iTarget1 == 0 )
  164. return;
  165. // get primary target, also translates to ragdoll
  166. C_BaseEntity *target1 = GetPrimaryTarget();
  167. if ( !target1 )
  168. {
  169. bool bFoundTarget = false;
  170. if ( m_bIsFollowingGrenade )
  171. {
  172. // if we we3re following a grenade and lost our target, revert back to our previous target
  173. C_BaseEntity* oldTarget = ClientEntityList().GetEnt( m_iLastTarget1 );
  174. if ( oldTarget )
  175. {
  176. target1 = oldTarget;
  177. bFoundTarget = true;
  178. }
  179. }
  180. if ( !bFoundTarget )
  181. return;
  182. }
  183. /*#ifdef CSTRIKE_DLL
  184. // weapon gun-cam go-pro chase camera
  185. C_CSPlayer *pPlayer = ToCSPlayer( target1 );
  186. if ( pPlayer && pPlayer->ShouldDraw() )
  187. {
  188. Vector vecSrc = target1->GetObserverCamOrigin();
  189. vecSrc += (target1->GetFlags() & FL_DUCKING) ? VEC_DUCK_VIEW : VEC_VIEW;
  190. Vector vecObsForward, vecObsRight, vecObsUp;
  191. AngleVectors( m_aCamAngle, &vecObsForward, &vecObsRight, &vecObsUp );
  192. trace_t playerEyeTrace;
  193. UTIL_TraceLine( vecSrc, vecSrc - vecObsForward * 75.0f, MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &playerEyeTrace );
  194. float flDistMax = playerEyeTrace.startpos.DistTo( playerEyeTrace.endpos + vecObsForward * 4 );
  195. m_flObserverChaseApproach = (m_flObserverChaseApproach >= flDistMax) ? flDistMax : Approach( 75.0f, m_flObserverChaseApproach, gpGlobals->frametime * 20 );
  196. Vector vecIdealCamEyePos = vecSrc - vecObsForward * m_flObserverChaseApproach;
  197. Vector vecIdealCamTargetPos = vecSrc + vecObsRight * RemapValClamped( m_flObserverChaseApproach, 20, 75, 6, 16 ) * abs(DotProduct(vecObsUp,Vector(0,0,1)));
  198. VectorAngles( (vecIdealCamTargetPos - vecIdealCamEyePos ).Normalized(), eyeAngles );
  199. eyeOrigin = vecIdealCamEyePos;
  200. return;
  201. }
  202. #endif*/
  203. CBaseCSGrenadeProjectile *pGrenade = dynamic_cast< CBaseCSGrenadeProjectile* >( target1 );
  204. if ( pGrenade )
  205. {
  206. m_iTarget2 = m_iTarget1;
  207. m_bIsFollowingGrenade = true;
  208. }
  209. bool bManual = !spec_autodirector.GetBool();
  210. if ( pGrenade && pGrenade->m_nBounces <= 0 ) // chase camera controlled manually
  211. bManual = false;
  212. if ( target1->IsPlayer() && target1->IsAlive() && target1->IsDormant() )
  213. return;
  214. targetOrigin1 = target1->GetRenderOrigin();
  215. if ( target1->IsPlayer() && !target1->IsAlive() )
  216. {
  217. targetOrigin1 += VEC_DEAD_VIEWHEIGHT;
  218. }
  219. else if ( target1->IsPlayer() && target1->GetFlags() & FL_DUCKING )
  220. {
  221. targetOrigin1 += VEC_DUCK_VIEW;
  222. }
  223. else if ( pGrenade )
  224. {
  225. targetOrigin1 += 2;//(VEC_DUCK_VIEW/2);
  226. }
  227. else
  228. {
  229. targetOrigin1 += VEC_VIEW;
  230. }
  231. // get secondary target if set
  232. C_BaseEntity *target2 = NULL;
  233. if ( m_iTarget2 > 0 && (m_iTarget2 != m_iTarget1) && !bManual )
  234. {
  235. target2 = ClientEntityList().GetBaseEntity( m_iTarget2 );
  236. // if target is out PVS and not dead, it's not valid
  237. if ( target2 && target2->IsDormant() && target2->IsAlive() )
  238. target2 = NULL;
  239. if ( target2 )
  240. {
  241. targetOrigin2 = target2->GetRenderOrigin();
  242. if ( !target2->IsAlive() )
  243. {
  244. targetOrigin2 += VEC_DEAD_VIEWHEIGHT;
  245. }
  246. else if ( target2->GetFlags() & FL_DUCKING )
  247. {
  248. targetOrigin2 += VEC_DUCK_VIEW;
  249. }
  250. else
  251. {
  252. targetOrigin2 += VEC_VIEW;
  253. }
  254. }
  255. }
  256. // apply angle offset & smoothing
  257. QAngle angleOffset( m_flPhi, m_flTheta, 0 );
  258. QAngle cameraAngles = m_aCamAngle;
  259. if ( bManual )
  260. {
  261. // let spectator choose the view angles
  262. engine->GetViewAngles( cameraAngles );
  263. }
  264. else if ( target2 )
  265. {
  266. // look into direction of second target
  267. forward = targetOrigin2 - targetOrigin1;
  268. VectorAngles( forward, cameraAngles );
  269. cameraAngles.z = 0; // no ROLL
  270. }
  271. else if ( pGrenade || m_iTarget2 == 0 || m_iTarget2 == m_iTarget1 )
  272. {
  273. if ( pGrenade )
  274. {
  275. //QAngle angFacing = pGrenade->GetLocalVelocity();
  276. Vector vecVel = pGrenade->GetLocalVelocity();//pGrenade->GetLocalAngularVelocity();//targetOrigin1 - m_vLastTarget1Origin;
  277. //cameraAngles = angFacing;
  278. // engine->Con_NPrintf(30, "vel = (%f, %f, %f)", XYZ(vecVel) );
  279. //cameraAngles = angFacing;
  280. angleOffset.Init();
  281. //float flTransTime = 0.1f;
  282. float flInterp = clamp( ( gpGlobals->curtime - m_flLastGrenadeVelocityUpdate ) * 10, 0.0f, 1.0f );
  283. //float flActualinterp = Gain( flInterp, 0.6 );
  284. Vector vecActualVel = Lerp( flInterp, m_vLastGrenadeVelocity, vecVel );
  285. //cameraAngles.x = 0; // no PITCH
  286. VectorAngles( vecActualVel, cameraAngles );
  287. if ( m_vLastGrenadeVelocity != vecVel )
  288. {
  289. m_flLastGrenadeVelocityUpdate = gpGlobals->curtime;
  290. m_vLastGrenadeVelocity = vecActualVel;
  291. }
  292. // Msg( "vel = (%f, %f, %f) - flInterp = %f\n", XYZ(vecActualVel), flInterp );
  293. // set the max distance to 64 with grenades
  294. m_flDistance = 64;
  295. }
  296. else
  297. {
  298. // look into direction where primary target is looking
  299. cameraAngles = target1->EyeAngles();
  300. cameraAngles.x = 0; // no PITCH
  301. }
  302. cameraAngles.z = 0; // no ROLL
  303. }
  304. else
  305. {
  306. // target2 is missing, just keep angelsm, reset offset
  307. angleOffset.Init();
  308. }
  309. if ( !bManual && target1->IsPlayer() )
  310. {
  311. if ( !target1->IsAlive() )
  312. {
  313. angleOffset.x = 15;
  314. }
  315. cameraAngles += angleOffset;
  316. }
  317. AngleVectors( cameraAngles, &forward );
  318. VectorNormalize( forward );
  319. // calc optimal camera position
  320. VectorMA(targetOrigin1, -m_flDistance, forward, cameraOrigin );
  321. targetOrigin1.z += m_flOffset; // add offset
  322. // clip against walls
  323. trace_t trace;
  324. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  325. UTIL_TraceHull( targetOrigin1, cameraOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, target1, COLLISION_GROUP_NONE, &trace );
  326. C_BaseEntity::PopEnableAbsRecomputations();
  327. float dist = VectorLength( trace.endpos - targetOrigin1 );
  328. // grow distance by 32 unit a second
  329. m_flLastDistance += gpGlobals->frametime * 32.0f;
  330. if ( dist > m_flLastDistance )
  331. {
  332. VectorMA(targetOrigin1, -m_flLastDistance, forward, cameraOrigin );
  333. }
  334. else
  335. {
  336. cameraOrigin = trace.endpos;
  337. m_flLastDistance = dist;
  338. }
  339. if ( target2 /*|| pGrenade*/ )
  340. {
  341. if ( pGrenade )
  342. {
  343. // if we have 2 targets look at point between them
  344. forward = targetOrigin1 - cameraOrigin;
  345. QAngle angle;
  346. VectorAngles( forward, angle );
  347. cameraAngles = angle;
  348. NormalizeAngles( cameraAngles );
  349. //cameraAngles.x = clamp( cameraAngles.x, -30, 30 );
  350. }
  351. else
  352. {
  353. // if we have 2 targets look at point between them
  354. forward = ( targetOrigin1 + targetOrigin2 ) / 2 - cameraOrigin;
  355. QAngle angle;
  356. VectorAngles( forward, angle );
  357. cameraAngles.y = angle.y;
  358. NormalizeAngles( cameraAngles );
  359. cameraAngles.x = clamp( cameraAngles.x, -60, 60 );
  360. }
  361. SmoothCameraAngle( cameraAngles );
  362. }
  363. else
  364. {
  365. SetCameraAngle( cameraAngles );
  366. }
  367. // engine->Con_NPrintf(20, "pos = (%f, %f, %f)", XYZ(m_vCamOrigin) );
  368. // engine->Con_NPrintf(22, "ang = (%f, %f, %f)", XYZ(m_aCamAngle) );
  369. VectorCopy( cameraOrigin, m_vCamOrigin );
  370. VectorCopy( m_aCamAngle, eyeAngles );
  371. VectorCopy( m_vCamOrigin, eyeOrigin );
  372. }
  373. Vector C_HLTVCamera::CalcIdealOverviewPosition( Vector vecStartPos, Vector vOldOverviewPos )
  374. {
  375. Vector vecMinDist = vecStartPos + Vector( -1024.0f, -1024.0f, -256.0f );
  376. Vector vecMaxDist = vecStartPos + Vector( 1024.0f, 1024.0f, 256.0f );
  377. CBaseEntity *pEntList[128];
  378. int count = UTIL_EntitiesInBox( pEntList, ARRAYSIZE(pEntList), vecMinDist, vecMaxDist, 0 );
  379. //CBaseEntity *pFarthestEnt = NULL;
  380. float flFarthestEntDist = 0;
  381. CBaseEntity *pFocusEnts[32];
  382. int focusCount = 0;
  383. for ( int i = 0; i < count; i++ )
  384. {
  385. CBaseEntity *pOther = pEntList[i];
  386. if ( dynamic_cast<C_CSPlayer*>(pOther) || dynamic_cast<C_PlantedC4*>(pOther) || dynamic_cast<CBaseCSGrenadeProjectile*>(pOther) )
  387. {
  388. pFocusEnts[focusCount] = pOther;
  389. focusCount++;
  390. float dist = VectorLength( vecStartPos - pOther->GetAbsOrigin() );
  391. if ( dist > flFarthestEntDist )
  392. flFarthestEntDist = dist;
  393. }
  394. }
  395. Vector vecAvPos = Vector( 0, 0, 0 );
  396. for ( int i = 0; i < focusCount; i++ )
  397. {
  398. vecAvPos += pFocusEnts[i]->GetAbsOrigin();
  399. }
  400. Vector vecNewPos = vecStartPos;
  401. if ( focusCount > 0 )
  402. {
  403. vecNewPos = (vecAvPos/focusCount);
  404. }
  405. float flDist = (vOldOverviewPos-vecNewPos).Length();
  406. float flDistScaler = clamp( 1+((flDist - 200) / 500), 1, 2 );
  407. m_flIdealOverviewScale = MAX( 0.1, (MIN( flFarthestEntDist, 1024 ) / 1024) ) * (1.5 * flDistScaler);
  408. return vecNewPos;
  409. }
  410. int C_HLTVCamera::GetMode()
  411. {
  412. // hacky....
  413. if ( dynamic_cast< C_BaseCSGrenadeProjectile* >( GetPrimaryTarget() ) )
  414. {
  415. m_bIsFollowingGrenade = true;
  416. return OBS_MODE_CHASE;
  417. }
  418. if ( m_iCameraMan > 0 )
  419. {
  420. C_BasePlayer *pCameraMan = UTIL_PlayerByIndex( m_iCameraMan );
  421. if ( pCameraMan )
  422. return pCameraMan->GetObserverMode();
  423. }
  424. // to get here, our target is not a grenade, but we think we're still folowing one
  425. if ( m_bIsFollowingGrenade == true )
  426. {
  427. if ( C_CSPlayer::GetLocalCSPlayer() )
  428. {
  429. // if we're the cameraman and we're holding the shift key after a grenade has exired,
  430. // keep the camera where it is
  431. bool bHoldingGrenadeKey = C_CSPlayer::GetLocalCSPlayer()->IsHoldingSpecGrenadeKey();
  432. if ( bHoldingGrenadeKey )
  433. return OBS_MODE_ROAMING;
  434. // otherwise, we're not following a grenade anymore
  435. m_bIsFollowingGrenade = false;
  436. }
  437. }
  438. return m_nCameraMode;
  439. }
  440. C_BaseEntity* C_HLTVCamera::GetPrimaryTarget()
  441. {
  442. if ( m_iCameraMan > 0 )
  443. {
  444. C_BasePlayer *pCameraMan = UTIL_PlayerByIndex( m_iCameraMan );
  445. if ( pCameraMan )
  446. {
  447. return pCameraMan->GetObserverTarget();
  448. }
  449. }
  450. if ( m_iTarget1 <= 0 )
  451. {
  452. return NULL;
  453. }
  454. C_BaseEntity* target = ClientEntityList().GetEnt( m_iTarget1 );
  455. if ( !target || (m_bIsFollowingGrenade && dynamic_cast< CBaseCSGrenadeProjectile* >( target ) == NULL) )
  456. {
  457. C_BaseEntity* oldTarget = ClientEntityList().GetEnt( m_iLastTarget1 );
  458. if ( oldTarget )
  459. {
  460. target = oldTarget;
  461. m_iTarget1 = m_iLastTarget1;
  462. }
  463. }
  464. return target;
  465. }
  466. C_BasePlayer *C_HLTVCamera::GetCameraMan()
  467. {
  468. return m_iCameraMan ? UTIL_PlayerByIndex( m_iCameraMan ): NULL;
  469. }
  470. void C_HLTVCamera::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  471. {
  472. C_BasePlayer *pPlayer = dynamic_cast<C_CSPlayer*>(GetPrimaryTarget());
  473. if ( !pPlayer )
  474. return;
  475. if ( !pPlayer->IsAlive() )
  476. {
  477. // if dead, show from 3rd person
  478. C_CSPlayer *pCSPlayer = static_cast<C_CSPlayer*>( pPlayer );
  479. if ( pCSPlayer && pCSPlayer->GetLastKillerIndex() )
  480. m_iTarget2 = pCSPlayer->GetLastKillerIndex();
  481. CalcChaseCamView( eyeOrigin, eyeAngles, fov );
  482. return;
  483. }
  484. m_aCamAngle = pPlayer->EyeAngles();
  485. m_vCamOrigin = pPlayer->GetAbsOrigin();
  486. m_flFOV = pPlayer->GetFOV();
  487. // Apply punch angle
  488. VectorAdd( m_aCamAngle, pPlayer->GetViewPunchAngle(), m_aCamAngle );
  489. // Apply aim punch angle
  490. VectorAdd( m_aCamAngle, pPlayer->GetAimPunchAngle() * view_recoil_tracking.GetFloat(), m_aCamAngle );
  491. // Shake it up baby!
  492. GetViewEffects()->CalcShake();
  493. GetViewEffects()->ApplyShake( m_vCamOrigin, m_aCamAngle, 1.0 );
  494. if ( pPlayer->GetFlags() & FL_DUCKING )
  495. {
  496. m_vCamOrigin += VEC_DUCK_VIEW;
  497. }
  498. else
  499. {
  500. m_vCamOrigin += VEC_VIEW;
  501. }
  502. eyeOrigin = m_vCamOrigin;
  503. eyeAngles = m_aCamAngle;
  504. fov = m_flFOV;
  505. pPlayer->CalcViewModelView( eyeOrigin, eyeAngles);
  506. // Update view model visibility
  507. for ( int i = 0; i < MAX_VIEWMODELS; i++ )
  508. {
  509. CBaseViewModel *vm = pPlayer->GetViewModel( i );
  510. if ( !vm )
  511. continue;
  512. vm->UpdateVisibility();
  513. }
  514. }
  515. void C_HLTVCamera::CalcChaseOverview( CViewSetup &pSetup )
  516. {
  517. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_iTarget1 );
  518. if ( !pPlayer )
  519. return;
  520. m_aCamAngle = QAngle( 90, 90, 0 );//pPlayer->EyeAngles();
  521. float flTransTime = 0.5f;
  522. if ( m_flNextIdealOverviewPosUpdate < gpGlobals->curtime )
  523. {
  524. m_vOldOverviewPos = m_vCamOrigin;
  525. Vector vNewIdealPos = CalcIdealOverviewPosition( pPlayer->GetAbsOrigin(), m_vOldOverviewPos ) + Vector( 0, 0, 128 );
  526. float flDist = (m_vOldOverviewPos-vNewIdealPos).Length();
  527. if ( flDist > 200.0f )
  528. {
  529. m_vIdealOverviewPos.x = vNewIdealPos.x;
  530. m_vIdealOverviewPos.y = vNewIdealPos.y;
  531. float flCamZDist = abs(m_flLastCamZPos - m_vIdealOverviewPos.z);
  532. if ( m_flLastCamZPos == 0 || flCamZDist > 64 )
  533. {
  534. trace_t tr;
  535. UTIL_TraceLine( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin() + Vector( 0, 0, 512 ), MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr );
  536. m_vIdealOverviewPos.z = MAX( tr.endpos.z - 32.0f, pPlayer->GetAbsOrigin().z + 80.0f );
  537. }
  538. }
  539. m_flNextIdealOverviewPosUpdate = gpGlobals->curtime + flTransTime;
  540. }
  541. float flInterp = clamp( ( gpGlobals->curtime - (m_flNextIdealOverviewPosUpdate - flTransTime) ) / flTransTime, 0.0f, 1.0f );
  542. float flActualinterp = Gain( flInterp, 0.6 );
  543. m_vCamOrigin = m_vOldOverviewPos + ((m_vIdealOverviewPos-m_vOldOverviewPos) * flActualinterp);
  544. m_flFOV = 180.0f;
  545. pSetup.origin = m_vCamOrigin;
  546. pSetup.angles = m_aCamAngle;
  547. pSetup.fov = m_flFOV;
  548. }
  549. void C_HLTVCamera::Accelerate( Vector& wishdir, float wishspeed, float accel )
  550. {
  551. float addspeed, accelspeed, currentspeed;
  552. // See if we are changing direction a bit
  553. currentspeed =m_vecVelocity.Dot(wishdir);
  554. // Reduce wishspeed by the amount of veer.
  555. addspeed = wishspeed - currentspeed;
  556. // If not going to add any speed, done.
  557. if (addspeed <= 0)
  558. return;
  559. // Determine amount of acceleration.
  560. accelspeed = accel * gpGlobals->frametime * wishspeed;
  561. // Cap at addspeed
  562. if (accelspeed > addspeed)
  563. accelspeed = addspeed;
  564. // Adjust velocity.
  565. for (int i=0 ; i<3 ; i++)
  566. {
  567. m_vecVelocity[i] += accelspeed * wishdir[i];
  568. }
  569. }
  570. extern ConVar fov_cs_debug;
  571. // movement code is a copy of CGameMovement::FullNoClipMove()
  572. void C_HLTVCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  573. {
  574. if ( !CSGameRules() )
  575. return;
  576. if ( m_bIsSpecLerping )
  577. {
  578. eyeOrigin = m_vCamOrigin;
  579. eyeAngles = m_aCamAngle;
  580. //fov = m_flFOV;
  581. fov = fov_cs_debug.GetInt() > 0 ? m_flFOV : CSGameRules()->DefaultFOV();
  582. if ( (m_vecSpecLerpIdealPos == m_vCamOrigin && m_angSpecLerpIdealAng == m_aCamAngle) || m_flSpecLerpEndTime <= gpGlobals->curtime )
  583. {
  584. m_bIsSpecLerping = false;
  585. SpecCameraGotoPos( m_vecSpecLerpIdealPos, m_angSpecLerpIdealAng );
  586. }
  587. else
  588. {
  589. float flTransTime = m_flSpecLerpTime;
  590. float flInterp = clamp( ( gpGlobals->curtime - (m_flSpecLerpEndTime - flTransTime) ) / flTransTime, 0.0f, 1.0f );
  591. float flActualinterp = Gain( flInterp, 0.6 );
  592. Vector vCamOrigin = m_vecSpecLerpOldPos + ((m_vecSpecLerpIdealPos-m_vecSpecLerpOldPos) * flActualinterp);
  593. QAngle aCamAngles = Lerp( flActualinterp, m_angSpecLerpOldAng, m_angSpecLerpIdealAng );//m_angSpecLerpOldAng + ((m_angSpecLerpIdealAng-m_angSpecLerpOldAng) * flActualinterp);
  594. SpecCameraGotoPos( vCamOrigin, aCamAngles );
  595. }
  596. return;
  597. }
  598. // only if PVS isn't locked by auto-director
  599. if ( !IsPVSLocked() )
  600. {
  601. Vector wishvel;
  602. Vector forward, right, up;
  603. Vector wishdir;
  604. float wishspeed;
  605. float factor = sv_specspeed.GetFloat();
  606. float maxspeed = sv_maxspeed.GetFloat() * factor;
  607. AngleVectors ( m_LastCmd.viewangles, &forward, &right, &up); // Determine movement angles
  608. if ( m_LastCmd.buttons & IN_SPEED )
  609. {
  610. factor /= 2.0f;
  611. }
  612. // Copy movement amounts
  613. float fmove = m_LastCmd.forwardmove * factor;
  614. float smove = m_LastCmd.sidemove * factor;
  615. VectorNormalize (forward); // Normalize remainder of vectors
  616. VectorNormalize (right); //
  617. for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  618. wishvel[i] = forward[i]*fmove + right[i]*smove;
  619. wishvel[2] += m_LastCmd.upmove * factor;
  620. VectorCopy (wishvel, wishdir); // Determine magnitude of speed of move
  621. wishspeed = VectorNormalize(wishdir);
  622. //
  623. // Clamp to server defined max speed
  624. //
  625. if (wishspeed > maxspeed )
  626. {
  627. VectorScale (wishvel, maxspeed/wishspeed, wishvel);
  628. wishspeed = maxspeed;
  629. }
  630. if ( sv_specaccelerate.GetFloat() > 0.0 )
  631. {
  632. // Set move velocity
  633. Accelerate ( wishdir, wishspeed, sv_specaccelerate.GetFloat() );
  634. float spd = VectorLength( m_vecVelocity );
  635. if (spd < 1.0f)
  636. {
  637. m_vecVelocity.Init();
  638. }
  639. else
  640. {
  641. // Bleed off some speed, but if we have less than the bleed
  642. // threshold, bleed the threshold amount.
  643. float control = (spd < maxspeed/4.0) ? maxspeed/4.0 : spd;
  644. float friction = sv_friction.GetFloat();
  645. // Add the amount to the drop amount.
  646. float drop = control * friction * gpGlobals->frametime;
  647. // scale the velocity
  648. float newspeed = spd - drop;
  649. if (newspeed < 0)
  650. newspeed = 0;
  651. // Determine proportion of old speed we are using.
  652. newspeed /= spd;
  653. VectorScale( m_vecVelocity, newspeed, m_vecVelocity );
  654. }
  655. }
  656. else
  657. {
  658. VectorCopy( wishvel, m_vecVelocity );
  659. }
  660. // Just move ( don't clip or anything )
  661. VectorMA( m_vCamOrigin, gpGlobals->frametime, m_vecVelocity, m_vCamOrigin );
  662. // get camera angle directly from engine
  663. engine->GetViewAngles( m_aCamAngle );
  664. // Zero out velocity if in noaccel mode
  665. if ( sv_specaccelerate.GetFloat() < 0.0f )
  666. {
  667. m_vecVelocity.Init();
  668. }
  669. }
  670. eyeOrigin = m_vCamOrigin;
  671. eyeAngles = m_aCamAngle;
  672. //fov = m_flFOV;
  673. fov = fov_cs_debug.GetInt() > 0 ? m_flFOV : CSGameRules()->DefaultFOV();
  674. }
  675. void C_HLTVCamera::CalcFixedView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  676. {
  677. eyeOrigin = m_vCamOrigin;
  678. eyeAngles = m_aCamAngle;
  679. fov = m_flFOV;
  680. if ( m_bIsSpecLerping )
  681. {
  682. if ( (m_vecSpecLerpIdealPos == m_vCamOrigin && m_angSpecLerpIdealAng == m_aCamAngle) || m_flSpecLerpEndTime <= gpGlobals->curtime )
  683. {
  684. m_bIsSpecLerping = false;
  685. SpecCameraGotoPos( m_vecSpecLerpIdealPos, m_angSpecLerpIdealAng );
  686. }
  687. else
  688. {
  689. float flTransTime = m_flSpecLerpTime;
  690. float flInterp = clamp( ( gpGlobals->curtime - (m_flSpecLerpEndTime - flTransTime) ) / flTransTime, 0.0f, 1.0f );
  691. float flActualinterp = Gain( flInterp, 0.6 );
  692. Vector vCamOrigin = m_vecSpecLerpOldPos + ((m_vecSpecLerpIdealPos-m_vecSpecLerpOldPos) * flActualinterp);
  693. QAngle aCamAngles = Lerp( flActualinterp, m_angSpecLerpOldAng, m_angSpecLerpIdealAng );//m_angSpecLerpOldAng + ((m_angSpecLerpIdealAng-m_angSpecLerpOldAng) * flActualinterp);
  694. SpecCameraGotoPos( vCamOrigin, aCamAngles );
  695. }
  696. }
  697. else
  698. {
  699. int nTarget = m_iTarget1;
  700. if ( m_iTarget1 == 0 && m_iLastTarget1 == 0 )
  701. return;
  702. int nButtonBits = input->GetButtonBits( false );
  703. if (m_iTarget1 == 0 && !(nButtonBits & IN_FORWARD) )
  704. return;
  705. if ( (nButtonBits & IN_FORWARD) )
  706. nTarget = m_iLastTarget1;
  707. C_BaseEntity * target = ClientEntityList().GetBaseEntity( nTarget );
  708. if ( target && target->IsAlive() )
  709. {
  710. // if we're chasing a target, change viewangles
  711. QAngle angle;
  712. VectorAngles( (target->GetAbsOrigin()+VEC_VIEW) - m_vCamOrigin, angle );
  713. SmoothCameraAngle( angle );
  714. }
  715. }
  716. }
  717. void C_HLTVCamera::PostEntityPacketReceived()
  718. {
  719. m_bEntityPacketReceived = true;
  720. }
  721. void C_HLTVCamera::FixupMovmentParents()
  722. {
  723. // Find resource zone
  724. for ( ClientEntityHandle_t e = ClientEntityList().FirstHandle();
  725. e != ClientEntityList().InvalidHandle(); e = ClientEntityList().NextHandle( e ) )
  726. {
  727. C_BaseEntity *ent = C_BaseEntity::Instance( e );
  728. if ( !ent )
  729. continue;
  730. ent->HierarchyUpdateMoveParent();
  731. }
  732. }
  733. void C_HLTVCamera::CalcView(CViewSetup *pSetup)
  734. {
  735. //CViewSetup *pSetup
  736. if ( m_bEntityPacketReceived )
  737. {
  738. // try to fixup movment pareents
  739. FixupMovmentParents();
  740. m_bEntityPacketReceived = false;
  741. }
  742. if ( m_iCameraMan > 0 )
  743. {
  744. C_BasePlayer *pCameraMan = UTIL_PlayerByIndex( m_iCameraMan );
  745. if ( pCameraMan )
  746. {
  747. //float zNear,zFar;
  748. pCameraMan->CalcView( pSetup->origin, pSetup->angles, pSetup->zNear, pSetup->zFar, pSetup->fov );
  749. pCameraMan->CalcViewModelView( pSetup->origin, pSetup->angles );
  750. return;
  751. }
  752. }
  753. if ( input->CAM_IsThirdPersonOverview() )
  754. {
  755. CalcChaseOverview( *pSetup );
  756. }
  757. else
  758. {
  759. switch ( GetMode() )
  760. {
  761. case OBS_MODE_ROAMING : CalcRoamingView( pSetup->origin, pSetup->angles, pSetup->fov );
  762. break;
  763. case OBS_MODE_FIXED : CalcFixedView( pSetup->origin, pSetup->angles, pSetup->fov );
  764. break;
  765. case OBS_MODE_IN_EYE : CalcInEyeCamView( pSetup->origin, pSetup->angles, pSetup->fov );
  766. break;
  767. case OBS_MODE_CHASE : CalcChaseCamView( pSetup->origin, pSetup->angles, pSetup->fov );
  768. break;
  769. }
  770. }
  771. }
  772. //-----------------------------------------------------------------------------
  773. // Purpose:
  774. // Input : *pSetup -
  775. //-----------------------------------------------------------------------------
  776. // void C_HLTVCamera::CalcOverview( CViewSetup *pSetup )
  777. // {
  778. // QAngle camAngles;
  779. //
  780. // // Let the player override the view.
  781. // C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  782. // if(!pPlayer)
  783. // return;
  784. //
  785. // pPlayer->OverrideView( pSetup );
  786. //
  787. // if( false/*::input->CAM_IsThirdPerson()*/ )
  788. // {
  789. // Vector cam_ofs;
  790. //
  791. // ::input->CAM_GetCameraOffset( cam_ofs );
  792. //
  793. // camAngles[ PITCH ] = cam_ofs[ PITCH ];
  794. // camAngles[ YAW ] = cam_ofs[ YAW ];
  795. // camAngles[ ROLL ] = 0;
  796. //
  797. // Vector camForward, camRight, camUp;
  798. // AngleVectors( camAngles, &camForward, &camRight, &camUp );
  799. //
  800. // pSetup->origin = pPlayer->GetThirdPersonViewPosition();
  801. //
  802. // VectorMA( pSetup->origin, -cam_ofs[ ROLL ], camForward, pSetup->origin );
  803. //
  804. // static ConVarRef c_thirdpersonshoulder( "c_thirdpersonshoulder" );
  805. // if ( c_thirdpersonshoulder.GetBool() )
  806. // {
  807. // static ConVarRef c_thirdpersonshoulderoffset( "c_thirdpersonshoulderoffset" );
  808. // static ConVarRef c_thirdpersonshoulderheight( "c_thirdpersonshoulderheight" );
  809. // static ConVarRef c_thirdpersonshoulderaimdist( "c_thirdpersonshoulderaimdist" );
  810. //
  811. // // add the shoulder offset to the origin in the cameras right vector
  812. // VectorMA( pSetup->origin, c_thirdpersonshoulderoffset.GetFloat(), camRight, pSetup->origin );
  813. //
  814. // // add the shoulder height to the origin in the cameras up vector
  815. // VectorMA( pSetup->origin, c_thirdpersonshoulderheight.GetFloat(), camUp, pSetup->origin );
  816. //
  817. // // adjust the yaw to the aim-point
  818. // camAngles[ YAW ] += RAD2DEG( atan(c_thirdpersonshoulderoffset.GetFloat() / (c_thirdpersonshoulderaimdist.GetFloat() + cam_ofs[ ROLL ])) );
  819. //
  820. // // adjust the pitch to the aim-point
  821. // camAngles[ PITCH ] += RAD2DEG( atan(c_thirdpersonshoulderheight.GetFloat() / (c_thirdpersonshoulderaimdist.GetFloat() + cam_ofs[ ROLL ])) );
  822. // }
  823. //
  824. // // Override angles from third person camera
  825. // VectorCopy( camAngles, pSetup->angles );
  826. // }
  827. // else if ( true /*::input->CAM_IsOrthographic()*/)
  828. // {
  829. // pSetup->m_bOrtho = true;
  830. // float w, h;
  831. // ::input->CAM_OrthographicSize( w, h );
  832. // w *= 0.5f;
  833. // h *= 0.5f;
  834. // pSetup->m_OrthoLeft = -w;
  835. // pSetup->m_OrthoTop = -h;
  836. // pSetup->m_OrthoRight = w;
  837. // pSetup->m_OrthoBottom = h;
  838. // }
  839. // }
  840. void C_HLTVCamera::SetMode(int iMode)
  841. {
  842. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  843. {
  844. if ( pParameters->m_uiLockFirstPersonAccountID )
  845. {
  846. iMode = OBS_MODE_IN_EYE;
  847. }
  848. }
  849. if ( m_nCameraMode == iMode )
  850. return;
  851. Assert( iMode > OBS_MODE_NONE && iMode <= LAST_PLAYER_OBSERVERMODE );
  852. int iOldMode = m_nCameraMode;
  853. m_nCameraMode = iMode;
  854. if ( m_nCameraMode == OBS_MODE_IN_EYE || m_nCameraMode == OBS_MODE_CHASE )
  855. m_bIsSpecLerping = false;
  856. IGameEvent *event = gameeventmanager->CreateEvent( "hltv_changed_mode" );
  857. if ( event )
  858. {
  859. event->SetInt( "oldmode", iOldMode );
  860. event->SetInt( "newmode", m_nCameraMode );
  861. event->SetInt( "obs_target", m_iTarget1 );
  862. gameeventmanager->FireEventClientSide( event );
  863. }
  864. // tell the target player to update the visibility of their view and world models
  865. CBaseEntity * target = UTIL_PlayerByIndex( m_iTarget1 );
  866. if ( target && target->IsPlayer() )
  867. {
  868. CBasePlayer * player = ToBasePlayer( target );
  869. if ( player )
  870. player->OnObserverModeChange( true );
  871. }
  872. }
  873. void C_HLTVCamera::SetPrimaryTarget( int nEntity )
  874. {
  875. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  876. {
  877. if ( pParameters->m_uiLockFirstPersonAccountID )
  878. {
  879. // Make sure that the requested target is the player account
  880. // that PVS must be locked to
  881. bool bFoundValidPlayer = false;
  882. for ( int idxEntity = 1; idxEntity <= gpGlobals->maxClients; ++ idxEntity )
  883. {
  884. CBaseEntity * target = UTIL_PlayerByIndex( idxEntity );
  885. if ( target == NULL )
  886. continue;
  887. if ( !target->IsPlayer() )
  888. continue;
  889. CBasePlayer * player = ToBasePlayer( target );
  890. if ( !player )
  891. continue;
  892. CSteamID steamID;
  893. if ( player->GetSteamID( &steamID ) && steamID.IsValid() &&
  894. ( steamID.GetAccountID() == pParameters->m_uiLockFirstPersonAccountID ) )
  895. {
  896. // when playback wants to lock to a specific account force PVS lock
  897. // regardless of player states to see exactly what that player was seeing
  898. bFoundValidPlayer = true;
  899. nEntity = idxEntity; // force the only entity allowed
  900. break;
  901. }
  902. }
  903. if ( !bFoundValidPlayer )
  904. return;
  905. }
  906. }
  907. if ( m_iTarget1 == nEntity )
  908. return;
  909. m_iLastTarget1 = m_iTarget1;
  910. m_iTarget1 = nEntity;
  911. #if defined ( CSTRIKE15 )
  912. // BUG: This uses the values (mode, target, etc) of the local player, not
  913. // the hltv camera... These happen to match so it works, but could be the source
  914. // of bugs... Could turn the observer lerp code into it's own class and have hltv/replay/csplayer
  915. // have an instance.
  916. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  917. if ( pLocalPlayer && pLocalPlayer->ShouldInterpolateObserverChanges() )
  918. {
  919. pLocalPlayer->StartObserverInterpolation( m_aCamAngle );
  920. }
  921. #endif
  922. if ( GetMode() == OBS_MODE_ROAMING )
  923. {
  924. Vector vOrigin;
  925. QAngle aAngles;
  926. float flFov;
  927. CalcChaseCamView( vOrigin, aAngles, flFov );
  928. }
  929. else if ( GetMode() == OBS_MODE_CHASE )
  930. {
  931. C_BaseEntity* target = ClientEntityList().GetEnt( m_iTarget1 );
  932. if ( target && target->IsPlayer() )
  933. {
  934. QAngle eyeAngle = target->EyeAngles();
  935. prediction->SetViewAngles( eyeAngle );
  936. }
  937. }
  938. C_BasePlayer *pOldPlayer = ToBasePlayer( ClientEntityList().GetEnt( m_iLastTarget1 ) );
  939. if ( pOldPlayer )
  940. {
  941. // Update view model visibility
  942. for ( int i = 0; i < MAX_VIEWMODELS; i++ )
  943. {
  944. CBaseViewModel *vm = pOldPlayer->GetViewModel( i );
  945. if ( !vm )
  946. continue;
  947. vm->UpdateVisibility();
  948. }
  949. }
  950. m_flLastDistance = m_flDistance;
  951. m_flLastAngleUpdateTime = -1;
  952. IGameEvent *event = gameeventmanager->CreateEvent( "hltv_changed_target" );
  953. if ( event )
  954. {
  955. event->SetInt("userid", pLocalPlayer->GetUserID() );
  956. event->SetInt( "mode", m_nCameraMode );
  957. event->SetInt( "old_target", m_iLastTarget1 );
  958. event->SetInt( "obs_target", m_iTarget1 );
  959. gameeventmanager->FireEventClientSide( event );
  960. }
  961. }
  962. void C_HLTVCamera::SpecNextPlayer( bool bReverse )
  963. {
  964. // Copy of GetNextObserverSearchStartPoint
  965. int iDir = bReverse ? -1 : 1;
  966. int StartIndex = 1;
  967. if ( m_iTarget1 > 0 && m_iTarget1 <= gpGlobals->maxClients )
  968. StartIndex = m_iTarget1;
  969. // end copy
  970. int Index = StartIndex;
  971. bool bFoundValidPlayer = false;
  972. // copy of FindNextObserverTarget
  973. do
  974. {
  975. Index += iDir;
  976. // Loop through the clients
  977. if (Index > gpGlobals->maxClients)
  978. Index = 1;
  979. else if (Index < 1)
  980. Index = gpGlobals->maxClients;
  981. CBaseEntity * target = UTIL_PlayerByIndex( Index );
  982. if ( target == NULL )
  983. continue;
  984. if ( !target->IsPlayer() )
  985. continue;
  986. CBasePlayer * player = ToBasePlayer( target );
  987. if ( player->IsEffectActive( EF_NODRAW ) ) // don't watch invisible players
  988. continue;
  989. if ( player->m_lifeState == LIFE_RESPAWNABLE ) // target is dead, waiting for respawn
  990. continue;
  991. if ( player->m_lifeState == LIFE_DEAD )
  992. continue;
  993. // for HLTV replay, them local player is both IsAlive(), and also is an observer because it's HLTV. Checking for IsObserver here seems to be redundant because the dead or specating players are already filtered out
  994. //if ( player->IsObserver() )
  995. // continue;
  996. if ( player->GetTeamNumber() == TEAM_SPECTATOR )
  997. continue;
  998. bFoundValidPlayer = true;
  999. break; // found next valid player
  1000. } while ( Index != StartIndex );
  1001. // end copy
  1002. if ( bFoundValidPlayer )
  1003. {
  1004. SetPrimaryTarget( Index );
  1005. if ( AutoDirectorState() != AUTODIRECTOR_OFF )
  1006. SetAutoDirector( AUTODIRECTOR_OFF ); // SetAutoDirector( AUTODIRECTOR_PAUSED );
  1007. }
  1008. }
  1009. void C_HLTVCamera::SpecPlayerByAccountID( const char *pszSteamID )
  1010. {
  1011. for ( int index = 1; index <= gpGlobals->maxClients; ++index )
  1012. {
  1013. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( index );
  1014. if ( !pPlayer )
  1015. continue;
  1016. CSteamID steamID;
  1017. pPlayer->GetSteamID( &steamID );
  1018. if ( CSteamID( pszSteamID ).GetAccountID() != steamID.GetAccountID( ) )
  1019. continue;
  1020. // only follow living players or dedicated spectators
  1021. if ( pPlayer->IsObserver() && pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  1022. continue;
  1023. SetPrimaryTarget( index );
  1024. if ( AutoDirectorState() != AUTODIRECTOR_OFF )
  1025. SetAutoDirector( AUTODIRECTOR_OFF ); // SetAutoDirector( AUTODIRECTOR_PAUSED );
  1026. return;
  1027. }
  1028. }
  1029. void C_HLTVCamera::SpecNamedPlayer( const char *szPlayerName )
  1030. {
  1031. for ( int index = 1; index <= gpGlobals->maxClients; ++index )
  1032. {
  1033. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( index );
  1034. if ( !pPlayer )
  1035. continue;
  1036. if ( !FStrEq( szPlayerName, pPlayer->GetPlayerName( ) ) )
  1037. continue;
  1038. // only follow living players or dedicated spectators
  1039. if ( pPlayer->IsObserver( ) && pPlayer->GetTeamNumber( ) != TEAM_SPECTATOR )
  1040. continue;
  1041. SetPrimaryTarget( index );
  1042. if ( AutoDirectorState( ) != AUTODIRECTOR_OFF )
  1043. SetAutoDirector( AUTODIRECTOR_OFF ); // SetAutoDirector( AUTODIRECTOR_PAUSED );
  1044. return;
  1045. }
  1046. }
  1047. void C_HLTVCamera::SpecPlayerByIndex( int iIndex )
  1048. {
  1049. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( iIndex );
  1050. if ( !pPlayer )
  1051. return;
  1052. if ( g_HltvReplaySystem.GetHltvReplayDelay() )
  1053. {
  1054. // In HLTV Replay case, just follow the living players explicitly
  1055. if ( !pPlayer->IsAlive() && pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  1056. return;
  1057. }
  1058. else
  1059. {
  1060. // only follow living players or dedicated spectators. TODO: Fold in the HLTV Replay case; just follow the living players explicitly
  1061. if ( pPlayer->IsObserver() && pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  1062. return;
  1063. }
  1064. if ( GetMode() == OBS_MODE_ROAMING || GetMode() == OBS_MODE_FIXED )
  1065. SetMode( OBS_MODE_IN_EYE );
  1066. SetPrimaryTarget( iIndex );
  1067. if ( AutoDirectorState() != AUTODIRECTOR_OFF )
  1068. SetAutoDirector( AUTODIRECTOR_OFF ); // SetAutoDirector( AUTODIRECTOR_PAUSED );
  1069. return;
  1070. }
  1071. void C_HLTVCamera::Update()
  1072. {
  1073. bool bFastForwardNotification = false;
  1074. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  1075. {
  1076. int numFastForwardTicks = 0;
  1077. // Get the team score
  1078. if ( CSGameRules() )
  1079. {
  1080. C_Team *tTeam = GetGlobalTeam( TEAM_TERRORIST );
  1081. C_Team *ctTeam = GetGlobalTeam( TEAM_CT );
  1082. int numRoundsCompleted = ( tTeam ? tTeam->Get_Score() : 0 ) + ( ctTeam ? ctTeam->Get_Score() : 0 );
  1083. if ( ( pParameters->m_numRoundStop != ~0 ) && ( numRoundsCompleted >= ( int ) pParameters->m_numRoundStop ) )
  1084. {
  1085. // End of fraction is now reached, just end the playback
  1086. if ( g_pMatchFramework )
  1087. {
  1088. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnDemoFileEndReached" ) );
  1089. g_pMatchFramework->CloseSession();
  1090. }
  1091. return;
  1092. }
  1093. }
  1094. if ( pParameters->m_uiLockFirstPersonAccountID )
  1095. {
  1096. // Skip portions of playback that aren't locking view to the requested account ID
  1097. bool bLockedToRequestedAccount = false;
  1098. for ( int iLockAttempt = 0; iLockAttempt < 2; ++ iLockAttempt )
  1099. {
  1100. if ( m_iTarget1 >= 1 && m_iTarget1 < gpGlobals->maxClients )
  1101. {
  1102. CBaseEntity * target = UTIL_PlayerByIndex( m_iTarget1 );
  1103. if ( target && target->IsPlayer() )
  1104. {
  1105. CBasePlayer * player = ToBasePlayer( target );
  1106. CSteamID steamID;
  1107. if ( player && player->GetSteamID( &steamID ) && steamID.IsValid() &&
  1108. ( steamID.GetAccountID() == pParameters->m_uiLockFirstPersonAccountID ) )
  1109. {
  1110. // Check if the player that we are locked to is doing non-interesting stuff
  1111. if ( player->IsEffectActive( EF_NODRAW ) || // don't watch invisible players
  1112. ( player->m_lifeState == LIFE_RESPAWNABLE ) || // target is dead, waiting for respawn
  1113. ( player->m_lifeState == LIFE_DEAD ) ||
  1114. ( player->GetFlags() & FL_FROZEN ) || // skip freezetime
  1115. ( player->IsObserver() ) ||
  1116. ( player->GetTeamNumber() == TEAM_SPECTATOR ) ||
  1117. ( CSGameRules() && CSGameRules()->IsFreezePeriod() ) )
  1118. break; // break to skip some ticks
  1119. bLockedToRequestedAccount = true;
  1120. }
  1121. }
  1122. }
  1123. // If we aren't locked then try locking now and check again
  1124. if ( bLockedToRequestedAccount )
  1125. break;
  1126. // Try locking and loop one more time
  1127. if ( !iLockAttempt )
  1128. SetPrimaryTarget( 0 );
  1129. }
  1130. if ( !bLockedToRequestedAccount && ( spec_overwatch_skip_idle_ticks.GetInt() > numFastForwardTicks ) && pParameters->m_bAnonymousPlayerIdentity )
  1131. {
  1132. numFastForwardTicks = spec_overwatch_skip_idle_ticks.GetInt();
  1133. }
  1134. }
  1135. if ( ( numFastForwardTicks > 1 ) && ( engine->GetDemoPlaybackTimeScale() >= 0.999f ) )
  1136. {
  1137. // Skip some ticks if demo playback is not being slowed down by the user
  1138. engine->ClientCmd_Unrestricted( CFmtStr( "demo_gototick %u R;\n", numFastForwardTicks ) );
  1139. bFastForwardNotification = true;
  1140. }
  1141. }
  1142. if ( m_flAutodirectorPausedTime >= 0.0)
  1143. {
  1144. const float endTime = spec_autodirector_pausetime.GetFloat() + m_flAutodirectorPausedTime;
  1145. if ( gpGlobals->curtime >= endTime)
  1146. {
  1147. HLTVCamera()->SetAutoDirector( AUTODIRECTOR_ON );
  1148. m_flAutodirectorPausedTime = -1.0f;
  1149. }
  1150. }
  1151. loadingdisc->SetFastForwardVisible( bFastForwardNotification );
  1152. }
  1153. void C_HLTVCamera::SpecCameraGotoPos( Vector vecPos, QAngle angAngle, int nPlayerIndex )
  1154. {
  1155. m_iCameraMan = 0;
  1156. m_iLastTarget1 = (nPlayerIndex > 0) ? nPlayerIndex : (GetPrimaryTarget() ? GetPrimaryTarget()->entindex() : m_iLastTarget1);
  1157. SetPrimaryTarget( 0 );
  1158. QAngle oldang = m_aCamAngle;
  1159. QAngle newang;
  1160. newang.x = angAngle[0];
  1161. newang.y = angAngle[1];
  1162. newang.z = oldang.z;
  1163. m_vCamOrigin = vecPos;
  1164. SetMode( OBS_MODE_ROAMING );
  1165. SetCameraAngle( newang );
  1166. engine->SetViewAngles( newang );
  1167. m_flFOV = 90.0f;
  1168. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1169. IGameEvent *event = gameeventmanager->CreateEvent( "spec_target_updated" );
  1170. if ( pPlayer && event )
  1171. {
  1172. event->SetInt("userid", pPlayer->GetUserID() );
  1173. gameeventmanager->FireEventClientSide( event );
  1174. }
  1175. }
  1176. void C_HLTVCamera::SpecCameraLerptoPos( const Vector &origin, const QAngle &angles, int nPlayerIndex, float flTime )
  1177. {
  1178. m_iCameraMan = 0;
  1179. m_iLastTarget1 = (nPlayerIndex > 0) ? nPlayerIndex : (GetPrimaryTarget() ? GetPrimaryTarget()->entindex() : m_iLastTarget1);
  1180. SetPrimaryTarget( 0 );
  1181. SetMode( OBS_MODE_ROAMING );
  1182. m_flFOV = 90.0f;
  1183. QAngle oldang = m_aCamAngle;
  1184. QAngle newang;
  1185. newang.x = angles[0];
  1186. newang.y = angles[1];
  1187. newang.z = oldang.z;
  1188. m_bIsSpecLerping = true;
  1189. m_vecSpecLerpOldPos = m_vCamOrigin;
  1190. m_angSpecLerpOldAng = m_aCamAngle;
  1191. m_vecSpecLerpIdealPos = origin;
  1192. m_angSpecLerpIdealAng = angles;
  1193. m_flSpecLerpTime = flTime;
  1194. m_flSpecLerpEndTime = gpGlobals->curtime + flTime;
  1195. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1196. IGameEvent *event = gameeventmanager->CreateEvent( "spec_target_updated" );
  1197. if ( pPlayer && event )
  1198. {
  1199. event->SetInt("userid", pPlayer->GetUserID() );
  1200. gameeventmanager->FireEventClientSide( event );
  1201. }
  1202. }
  1203. void C_HLTVCamera::FireGameEvent( IGameEvent * event)
  1204. {
  1205. const char *type = event->GetName();
  1206. if ( !engine->IsHLTV() )
  1207. {
  1208. #ifdef CAMERAMAN_OLD_WAY
  1209. if ( Q_strcmp( "hltv_cameraman", type ) == 0 )
  1210. {
  1211. m_iCameraMan = event->GetInt( "index" );
  1212. }
  1213. #endif
  1214. return; // not in HLTV mode
  1215. }
  1216. // in HLTV mode
  1217. if ( Q_strcmp( "game_newmap", type ) == 0 )
  1218. {
  1219. Reset(); // reset all camera settings
  1220. // show spectator UI
  1221. if ( !GetViewPortInterface() )
  1222. return;
  1223. if ( !engine->IsPlayingDemo() )
  1224. {
  1225. // during live broadcast only show black bars
  1226. GetViewPortInterface()->ShowPanel( PANEL_SPECGUI, true );
  1227. }
  1228. SetMode( OBS_MODE_ROAMING );
  1229. SetAutoDirector( C_HLTVCamera::AUTODIRECTOR_ON );
  1230. return;
  1231. }
  1232. if ( Q_strcmp( "hltv_message", type ) == 0 )
  1233. {
  1234. wchar_t outputBuf[1024];
  1235. const char *pszText = event->GetString( "text", "" );
  1236. char *tmpStr = hudtextmessage->LookupString( pszText );
  1237. const wchar_t *pBuf = g_pVGuiLocalize->Find( tmpStr );
  1238. if ( pBuf )
  1239. {
  1240. // Copy pBuf into szBuf[i].
  1241. int nMaxChars = sizeof( outputBuf ) / sizeof( wchar_t );
  1242. wcsncpy( outputBuf, pBuf, nMaxChars );
  1243. outputBuf[nMaxChars-1] = 0;
  1244. }
  1245. else
  1246. {
  1247. g_pVGuiLocalize->ConvertANSIToUnicode( tmpStr, outputBuf, sizeof(outputBuf) );
  1248. }
  1249. GetCenterPrint()->Print( ConvertCRtoNL( outputBuf ) );
  1250. return ;
  1251. }
  1252. if ( Q_strcmp( "hltv_title", type ) == 0 )
  1253. {
  1254. Q_strncpy( m_szTitleText, event->GetString( "text", "" ), sizeof(m_szTitleText) );
  1255. return;
  1256. }
  1257. if ( Q_strcmp( "hltv_status", type ) == 0 )
  1258. {
  1259. int nNumProxies = event->GetInt( "proxies" );
  1260. m_nNumSpectators = event->GetInt( "clients" ) - nNumProxies;
  1261. return;
  1262. }
  1263. // after this only auto-director commands follow
  1264. // don't execute them if the autodirector is off and PVS is unlocked
  1265. //( hltv player is controlling the camera )
  1266. if ( !spec_autodirector.GetBool() && !IsPVSLocked() )
  1267. return;
  1268. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  1269. {
  1270. if ( pParameters->m_uiLockFirstPersonAccountID )
  1271. {
  1272. // Whenever a player connects we will force lookup of the required player
  1273. if ( !Q_strcmp( "player_connect", type ) ||
  1274. !Q_strcmp( "player_connect_full", type ) ||
  1275. !Q_strcmp( "player_team", type ) )
  1276. {
  1277. SetPrimaryTarget( 0 );
  1278. return;
  1279. }
  1280. return; // don't execute further commands if locked to a specific account forced PVS
  1281. }
  1282. }
  1283. if ( m_iCameraMan > 0 )
  1284. {
  1285. return;
  1286. }
  1287. #ifdef CAMERAMAN_OLD_WAY
  1288. if ( Q_strcmp( "hltv_cameraman", type ) == 0 )
  1289. {
  1290. Reset();
  1291. m_nCameraMode = OBS_MODE_ROAMING;
  1292. m_iCameraMan = event->GetInt( "index" );
  1293. return;
  1294. }
  1295. #endif
  1296. if ( Q_strcmp( "hltv_fixed", type ) == 0 )
  1297. {
  1298. m_iCameraMan = 0;
  1299. m_vCamOrigin.x = event->GetInt( "posx" );
  1300. m_vCamOrigin.y = event->GetInt( "posy" );
  1301. m_vCamOrigin.z = event->GetInt( "posz" );
  1302. QAngle angle;
  1303. angle.x = event->GetInt( "theta" );
  1304. angle.y = event->GetInt( "phi" );
  1305. angle.z = 0; // no roll yet
  1306. if ( m_nCameraMode != OBS_MODE_FIXED )
  1307. {
  1308. SetMode( OBS_MODE_FIXED );
  1309. SetCameraAngle( angle );
  1310. m_flFOV = event->GetFloat( "fov", 90 );
  1311. }
  1312. SetPrimaryTarget( event->GetInt( "target" ) );
  1313. if ( m_iTarget1 == 0 )
  1314. {
  1315. SetCameraAngle( angle );
  1316. }
  1317. return;
  1318. }
  1319. if ( Q_strcmp( "hltv_chase", type ) == 0 )
  1320. {
  1321. bool bInEye = event->GetBool( "ineye" );
  1322. // check if we are already in a player chase mode
  1323. bool bIsInChaseMode = (GetMode()==OBS_MODE_IN_EYE)|| (GetMode()==OBS_MODE_CHASE);
  1324. // if we are in auto director or not in a valid chase mode, set new mode now
  1325. if ( spec_autodirector.GetBool() || !bIsInChaseMode )
  1326. {
  1327. SetMode( bInEye?OBS_MODE_IN_EYE:OBS_MODE_CHASE );
  1328. }
  1329. m_iCameraMan = 0;
  1330. m_iTarget2 = event->GetInt( "target2" );
  1331. m_flDistance = event->GetFloat( "distance", m_flDistance );
  1332. m_flOffset = event->GetFloat( "offset", m_flOffset );
  1333. m_flTheta = event->GetFloat( "theta", m_flTheta );
  1334. m_flPhi = event->GetFloat( "phi", m_flPhi );
  1335. m_flFOV = event->GetFloat( "fov", 90 );
  1336. m_flInertia = event->GetFloat( "inertia", 30.f ) / 10.f;
  1337. // if inertia is not set use standard value
  1338. if ( m_flInertia <= 0 )
  1339. m_flInertia = 3.0f;
  1340. SetPrimaryTarget( event->GetInt( "target1" ) );
  1341. return;
  1342. }
  1343. }
  1344. // this is a cheap version of FullNoClipMove():
  1345. void C_HLTVCamera::CreateMove( CUserCmd *cmd)
  1346. {
  1347. if ( cmd )
  1348. {
  1349. m_LastCmd = *cmd;
  1350. }
  1351. }
  1352. void C_HLTVCamera::SetCameraAngle( QAngle& targetAngle )
  1353. {
  1354. m_aCamAngle = targetAngle;
  1355. NormalizeAngles( m_aCamAngle );
  1356. m_flLastAngleUpdateTime = gpGlobals->realtime;
  1357. }
  1358. void C_HLTVCamera::SmoothCameraAngle( QAngle& targetAngle )
  1359. {
  1360. if ( m_flLastAngleUpdateTime > 0 )
  1361. {
  1362. float deltaTime = gpGlobals->realtime - m_flLastAngleUpdateTime;
  1363. deltaTime = clamp( deltaTime*m_flInertia, 0.01, 1);
  1364. InterpolateAngles( m_aCamAngle, targetAngle, m_aCamAngle, deltaTime );
  1365. }
  1366. else
  1367. {
  1368. m_aCamAngle = targetAngle;
  1369. }
  1370. m_flLastAngleUpdateTime = gpGlobals->realtime;
  1371. }
  1372. void C_HLTVCamera::ToggleChaseAsFirstPerson()
  1373. {
  1374. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  1375. {
  1376. if ( pParameters->m_uiLockFirstPersonAccountID )
  1377. {
  1378. if ( GetMode() != OBS_MODE_IN_EYE )
  1379. SetMode( OBS_MODE_IN_EYE );
  1380. SetPrimaryTarget( 0 ); // will force correct computation of the target
  1381. return; // when playback wants to lock to a specific account force PVS lock to IN_EYE
  1382. }
  1383. }
  1384. if ( GetMode() == OBS_MODE_CHASE )
  1385. {
  1386. SetMode( OBS_MODE_IN_EYE );
  1387. }
  1388. else if ( GetMode() == OBS_MODE_IN_EYE )
  1389. {
  1390. SetMode( OBS_MODE_CHASE );
  1391. }
  1392. if ( AutoDirectorState() != AUTODIRECTOR_OFF )
  1393. SetAutoDirector( AUTODIRECTOR_OFF ); //SetAutoDirector( AUTODIRECTOR_PAUSED );
  1394. }
  1395. bool C_HLTVCamera::IsPVSLocked()
  1396. {
  1397. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  1398. {
  1399. if ( pParameters->m_uiLockFirstPersonAccountID )
  1400. return true; // when playback wants to lock to a specific account force PVS lock
  1401. }
  1402. if ( tv_transmitall != NULL )
  1403. {
  1404. return !tv_transmitall->GetBool();
  1405. }
  1406. else
  1407. {
  1408. //old style, assume locked unless we playback a demo
  1409. return !engine->IsPlayingDemo();
  1410. }
  1411. }
  1412. void C_HLTVCamera::SetAutoDirector( AutodirectorState_t eState )
  1413. {
  1414. //m_iCameraMan = 0;
  1415. if ( eState != AUTODIRECTOR_OFF && spec_autodirector_cameraman.GetInt() > 0 )
  1416. {
  1417. // find a cameraman and set m_iCameraMan
  1418. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1419. {
  1420. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1421. CSteamID compareSteamID;
  1422. if ( pPlayer && pPlayer->GetSteamID( &compareSteamID ) )
  1423. {
  1424. // is this played the selected cameraman
  1425. if ( ( uint32 )( spec_autodirector_cameraman.GetInt() ) == compareSteamID.GetAccountID() )
  1426. {
  1427. // validate that they are a tournament caster
  1428. for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; j++ )
  1429. {
  1430. if ( compareSteamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
  1431. {
  1432. if ( pPlayer->IsActiveCameraMan() )
  1433. {
  1434. m_iCameraMan = i;
  1435. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  1436. CBaseHudChat *hudChat = ( CBaseHudChat * )GET_HUDELEMENT( CHudChat );
  1437. if ( hudChat && pLocalPlayer && !IsAutoDirectorOn() )
  1438. {
  1439. hudChat->ChatPrintfW( pLocalPlayer->entindex(), CHAT_FILTER_SERVERMSG, g_pVGuiLocalize->Find( "#CSGO_Scoreboard_CasterControl_Camera_On" ) );
  1440. pLocalPlayer->EmitSound("Vote.Passed");
  1441. }
  1442. break;
  1443. }
  1444. }
  1445. }
  1446. }
  1447. if ( m_iCameraMan )
  1448. break;
  1449. }
  1450. }
  1451. }
  1452. if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
  1453. {
  1454. if ( pParameters->m_uiLockFirstPersonAccountID )
  1455. {
  1456. eState = AUTODIRECTOR_ON; // force auto-director first person mode
  1457. m_iCameraMan = 0; // force no cameraman
  1458. }
  1459. }
  1460. if ( eState == AUTODIRECTOR_ON)
  1461. {
  1462. spec_autodirector.SetValue( 1 );
  1463. }
  1464. else // OFF or PAUSED
  1465. {
  1466. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  1467. CBaseHudChat *hudChat = ( CBaseHudChat * )GET_HUDELEMENT( CHudChat );
  1468. if ( hudChat && pLocalPlayer && m_iCameraMan != 0 && IsAutoDirectorOn() )
  1469. {
  1470. hudChat->ChatPrintfW( pLocalPlayer->entindex(), CHAT_FILTER_SERVERMSG, g_pVGuiLocalize->Find( "#CSGO_Scoreboard_CasterControl_Camera_Off" ) );
  1471. pLocalPlayer->EmitSound("UI.ButtonRolloverLarge");
  1472. }
  1473. spec_autodirector.SetValue( 0 );
  1474. m_iCameraMan = 0;
  1475. }
  1476. if ( eState == AUTODIRECTOR_PAUSED )
  1477. {
  1478. m_flAutodirectorPausedTime = gpGlobals->curtime;
  1479. }
  1480. else
  1481. {
  1482. m_flAutodirectorPausedTime = -1.0f;
  1483. }
  1484. }
  1485. C_HLTVCamera::AutodirectorState_t C_HLTVCamera::AutoDirectorState() const
  1486. {
  1487. if ( IsAutoDirectorOn() )
  1488. return AUTODIRECTOR_ON;
  1489. if ( m_flAutodirectorPausedTime < 0.0f )
  1490. return AUTODIRECTOR_OFF;
  1491. else
  1492. return AUTODIRECTOR_PAUSED;
  1493. }
  1494. bool C_HLTVCamera::IsAutoDirectorOn() const
  1495. {
  1496. return spec_autodirector.GetBool();
  1497. }
  1498. #ifdef CSTRIKE_DLL
  1499. CON_COMMAND_F( list_active_casters, "List currently active casters.", FCVAR_CLIENTDLL | FCVAR_RELEASE |FCVAR_HIDDEN )
  1500. {
  1501. if ( !CSGameRules() )
  1502. {
  1503. Msg( "You need to be watching a game!\n" );
  1504. return;
  1505. }
  1506. Msg( "Active Casters:\n" );
  1507. CSteamID cameraManSteamID;
  1508. if ( CBasePlayer *pPlayer = HLTVCamera()->GetCameraMan() )
  1509. {
  1510. pPlayer->GetSteamID( &cameraManSteamID );
  1511. }
  1512. int nActiveCasters = 0;
  1513. for (int i = 0; i < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; i++ )
  1514. {
  1515. if ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ i ] )
  1516. {
  1517. nActiveCasters++;
  1518. if ( steamapicontext->SteamUser() && steamapicontext->SteamFriends() )
  1519. {
  1520. CSteamID steamID( CSGameRules()->m_arrTournamentActiveCasterAccounts[ i ], steamapicontext->SteamUser()->GetSteamID().GetEUniverse(), k_EAccountTypeIndividual );
  1521. const char *pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID );
  1522. Msg( "%d, ID: %d Name: %s %s\n", i, CSGameRules()->m_arrTournamentActiveCasterAccounts[ i ], pszName,
  1523. ( cameraManSteamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ i ] ) ? "*Camera Man*" : "" );
  1524. }
  1525. else
  1526. {
  1527. Msg( "%d, ID: %d %s\n", i, CSGameRules()->m_arrTournamentActiveCasterAccounts[ i ],
  1528. ( cameraManSteamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ i ] ) ? "*Camera Man*" : "" );
  1529. }
  1530. }
  1531. }
  1532. if ( nActiveCasters == 0 )
  1533. {
  1534. Msg( "None.\n" );
  1535. }
  1536. }
  1537. #endif