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.

1228 lines
28 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // hltvdirector.cpp: implementation of the CHLTVDirector class.
  9. //
  10. //////////////////////////////////////////////////////////////////////
  11. #include "cbase.h"
  12. #include "hltvdirector.h"
  13. #include "keyvalues.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. //////////////////////////////////////////////////////////////////////
  17. // Construction/Destruction
  18. //////////////////////////////////////////////////////////////////////
  19. static ConVar tv_delay( "tv_delay", "10", FCVAR_RELEASE, "GOTV broadcast delay in seconds", true, 0, true, HLTV_MAX_DELAY );
  20. static ConVar tv_allow_static_shots( "tv_allow_static_shots", "1", FCVAR_RELEASE, "Auto director uses fixed level cameras for shots" );
  21. #ifdef CAMERAMAN_OLD_WAY
  22. static ConVar tv_allow_camera_man( "tv_allow_camera_man", "1", FCVAR_RELEASE, "Auto director allows spectators to become camera man" );
  23. #endif
  24. static bool GameEventLessFunc( CHLTVGameEvent const &e1, CHLTVGameEvent const &e2 )
  25. {
  26. return e1.m_Tick < e2.m_Tick;
  27. }
  28. #define RANDOM_MAX_ELEMENTS 256
  29. static int s_RndOrder[RANDOM_MAX_ELEMENTS];
  30. static void InitRandomOrder(int nFields)
  31. {
  32. if ( nFields > RANDOM_MAX_ELEMENTS )
  33. {
  34. Assert( nFields > RANDOM_MAX_ELEMENTS );
  35. nFields = RANDOM_MAX_ELEMENTS;
  36. }
  37. for ( int i=0; i<nFields; i++ )
  38. {
  39. s_RndOrder[i]=i;
  40. }
  41. for ( int i=0; i<(nFields/2); i++ )
  42. {
  43. int pos1 = RandomInt( 0, nFields-1 );
  44. int pos2 = RandomInt( 0, nFields-1 );
  45. int temp = s_RndOrder[pos1];
  46. s_RndOrder[pos1] = s_RndOrder[pos2];
  47. s_RndOrder[pos2] = temp;
  48. }
  49. };
  50. static float WeightedAngle( Vector vec1, Vector vec2)
  51. {
  52. VectorNormalize( vec1 );
  53. VectorNormalize( vec2 );
  54. float a = DotProduct( vec1, vec2 ); // a = [-1,1]
  55. a = (a + 1.0f) / 2.0f;
  56. Assert ( a <= 1 && a >= 0 );
  57. return a*a; // vectors are facing opposite direction
  58. }
  59. #if !defined( CSTRIKE_DLL ) && !defined( TF_DLL )// add your mod here if you use your own director
  60. static CHLTVDirector s_HLTVDirector; // singleton
  61. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CHLTVDirector, IHLTVDirector, INTERFACEVERSION_HLTVDIRECTOR, s_HLTVDirector );
  62. CHLTVDirector* HLTVDirector()
  63. {
  64. return &s_HLTVDirector;
  65. }
  66. IGameSystem* HLTVDirectorSystem()
  67. {
  68. return &s_HLTVDirector;
  69. }
  70. #endif // MODs
  71. CHLTVDirector::CHLTVDirector()
  72. {
  73. m_iPVSEntity = 0;
  74. m_fDelay = 30.0;
  75. m_iLastPlayer = 1;
  76. m_iCameraMan = 0;
  77. m_nNumFixedCameras = 0;
  78. m_EventHistory.SetLessFunc( GameEventLessFunc );
  79. m_nNextAnalyzeTick = 0;
  80. m_iCameraManIndex = 0;
  81. }
  82. CHLTVDirector::~CHLTVDirector()
  83. {
  84. }
  85. bool CHLTVDirector::Init()
  86. {
  87. return gameeventmanager->LoadEventsFromFile( "resource/hltvevents.res" ) > 0;
  88. }
  89. void CHLTVDirector::Shutdown()
  90. {
  91. RemoveEventsFromHistory(-1); // all
  92. }
  93. void CHLTVDirector::FireGameEvent( IGameEvent * event )
  94. {
  95. if ( m_HltvServers.IsEmpty() )
  96. return; // don't do anything
  97. CHLTVGameEvent gameevent;
  98. gameevent.m_Event = gameeventmanager->DuplicateEvent( event );
  99. gameevent.m_Priority = event->GetInt( "priority", -1 ); // priorities are leveled between 0..10, -1 means ignore
  100. gameevent.m_Tick = gpGlobals->tickcount;
  101. m_EventHistory.Insert( gameevent );
  102. }
  103. void CHLTVDirector::AddHLTVServer( IHLTVServer *hltv )
  104. {
  105. RemoveEventsFromHistory(-1); // all
  106. if ( hltv )
  107. {
  108. RemoveHLTVServer( hltv );
  109. HltvServerRecord_t rec;
  110. rec.m_pHLTVServer = hltv;
  111. rec.m_pHLTVClient = UTIL_PlayerByIndex( hltv->GetHLTVSlot() + 1 );
  112. if ( rec.m_pHLTVClient && rec.m_pHLTVClient->IsHLTV() )
  113. {
  114. rec.m_pHLTVServer = hltv;
  115. }
  116. else
  117. {
  118. rec.m_pHLTVServer = NULL;
  119. Error( "Couldn't find GOTV client player." );
  120. }
  121. if ( m_HltvServers.IsEmpty() )
  122. {
  123. // register for events the director needs to know
  124. ListenForGameEvent( "player_hurt" );
  125. ListenForGameEvent( "player_death" );
  126. ListenForGameEvent( "round_end" );
  127. ListenForGameEvent( "round_start" );
  128. #ifdef CAMERAMAN_OLD_WAY
  129. ListenForGameEvent( "hltv_cameraman" );
  130. #endif
  131. ListenForGameEvent( "hltv_rank_entity" );
  132. ListenForGameEvent( "hltv_rank_camera" );
  133. }
  134. m_HltvServers.AddToTail( rec );
  135. }
  136. }
  137. void CHLTVDirector::RemoveHLTVServer( IHLTVServer *hltv )
  138. {
  139. RemoveEventsFromHistory( -1 );
  140. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  141. {
  142. if ( m_HltvServers[ i ].m_pHLTVServer == hltv )
  143. {
  144. m_HltvServers.FastRemove( i );
  145. break;
  146. }
  147. }
  148. }
  149. bool CHLTVDirector::IsActive( void )
  150. {
  151. return !m_HltvServers.IsEmpty();
  152. }
  153. float CHLTVDirector::GetDelay( void )
  154. {
  155. return m_fDelay;
  156. }
  157. int CHLTVDirector::GetDirectorTick( void )
  158. {
  159. // just simple delay it
  160. return m_nBroadcastTick;
  161. }
  162. int CHLTVDirector::GetPVSEntity( void )
  163. {
  164. return m_iPVSEntity;
  165. }
  166. Vector CHLTVDirector::GetPVSOrigin( void )
  167. {
  168. return m_vPVSOrigin;
  169. }
  170. void CHLTVDirector::UpdateSettings()
  171. {
  172. // set delay
  173. m_fDelay = tv_delay.GetFloat();
  174. int newBroadcastTick = gpGlobals->tickcount;
  175. if ( m_fDelay < HLTV_MIN_DIRECTOR_DELAY )
  176. {
  177. // instant broadcast, no delay
  178. m_fDelay = 0.0;
  179. }
  180. else
  181. {
  182. // broadcast time is current time - delay time
  183. newBroadcastTick -= TIME_TO_TICKS( m_fDelay );
  184. }
  185. if( (m_nBroadcastTick == 0) && (newBroadcastTick > 0) )
  186. {
  187. // we start broadcasting right now, reset NextShotTimer
  188. m_nNextShotTick = 0;
  189. }
  190. // check if camera man is still valid
  191. if ( m_iCameraManIndex > 0 )
  192. {
  193. CBasePlayer *pPlayer = UTIL_PlayerByIndex( m_iCameraManIndex );
  194. if ( !pPlayer || pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  195. {
  196. SetCameraMan( 0 );
  197. }
  198. }
  199. m_nBroadcastTick = MAX( 0, newBroadcastTick );
  200. }
  201. const char** CHLTVDirector::GetModEvents()
  202. {
  203. static const char *s_modevents[] =
  204. {
  205. "hltv_status",
  206. "hltv_chat",
  207. "player_connect",
  208. "player_disconnect",
  209. "player_team",
  210. "player_info",
  211. "server_cvar",
  212. "player_death",
  213. "player_chat",
  214. "round_start",
  215. "round_end",
  216. NULL
  217. };
  218. return s_modevents;
  219. }
  220. void CHLTVDirector::StartAutoRecording()
  221. {
  222. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  223. {
  224. if ( !m_HltvServers[ i ].m_pHLTVServer->IsRecording() )
  225. {
  226. m_HltvServers[ i ].m_pHLTVServer->StartAutoRecording();
  227. }
  228. }
  229. }
  230. void CHLTVDirector::BuildCameraList( void )
  231. {
  232. m_nNumFixedCameras = 0;
  233. memset( m_pFixedCameras, 0, sizeof ( m_pFixedCameras ) );
  234. CBaseEntity *pCamera = gEntList.FindEntityByClassname( NULL, GetFixedCameraEntityName() );
  235. while ( pCamera && m_nNumFixedCameras < MAX_NUM_CAMERAS)
  236. {
  237. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, STRING(pCamera->m_target) );
  238. if ( pTarget )
  239. {
  240. // look at target if any given
  241. QAngle angles;
  242. VectorAngles( pTarget->GetAbsOrigin() - pCamera->GetAbsOrigin(), angles );
  243. pCamera->SetAbsAngles( angles );
  244. }
  245. m_pFixedCameras[m_nNumFixedCameras] = pCamera;
  246. m_nNumFixedCameras++;
  247. pCamera = gEntList.FindEntityByClassname( pCamera, GetFixedCameraEntityName() );
  248. }
  249. }
  250. // this is called with every new map
  251. void CHLTVDirector::LevelInitPostEntity( void )
  252. {
  253. BuildCameraList();
  254. m_vPVSOrigin.Init();
  255. m_iPVSEntity = 0;
  256. m_nNextShotTick = 0;
  257. m_nNextAnalyzeTick = 0;
  258. m_iCameraManIndex = 0;
  259. RemoveEventsFromHistory(-1); // all
  260. // DevMsg("HLTV Director: found %i fixed cameras.\n", m_nNumFixedCameras );
  261. }
  262. void CHLTVDirector::FrameUpdatePostEntityThink( void )
  263. {
  264. if ( m_HltvServers.IsEmpty() )
  265. return; // don't do anything
  266. // This function is called each tick
  267. UpdateSettings(); // update settings from cvars
  268. if ( (m_nNextAnalyzeTick < gpGlobals->tickcount) &&
  269. (m_fDelay >= HLTV_MIN_DIRECTOR_DELAY) )
  270. {
  271. m_nNextAnalyzeTick = gpGlobals->tickcount + TIME_TO_TICKS( 0.5f );
  272. AnalyzePlayers();
  273. AnalyzeCameras();
  274. }
  275. if ( m_nBroadcastTick <= 0 )
  276. {
  277. // game start is still in delay loop
  278. StartDelayMessage();
  279. }
  280. else if ( m_nNextShotTick <= m_nBroadcastTick )
  281. {
  282. // game is being broadcasted, generate camera shots
  283. StartNewShot();
  284. }
  285. }
  286. void CHLTVDirector::StartDelayMessage()
  287. {
  288. if ( m_nNextShotTick > gpGlobals->tickcount )
  289. return;
  290. // check the next 8 seconds for interrupts/important events
  291. m_nNextShotTick = gpGlobals->tickcount + TIME_TO_TICKS( DEF_SHOT_LENGTH );
  292. // game hasn't started yet, we are still in the broadcast delay hole
  293. IGameEvent *msg = gameeventmanager->CreateEvent( "hltv_message", true );
  294. if ( msg )
  295. {
  296. msg->SetString("text", "#GOTV_Please_Wait_for_Broadcast_to_Start" );
  297. // send spectators the HLTV director command as a game event
  298. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  299. {
  300. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( msg );
  301. }
  302. gameeventmanager->FreeEvent( msg );
  303. }
  304. StartBestFixedCameraShot( true );
  305. }
  306. void CHLTVDirector::StartBestPlayerCameraShot()
  307. {
  308. float flPlayerRanking[MAX_PLAYERS];
  309. memset( flPlayerRanking, 0, sizeof(flPlayerRanking) );
  310. int firstIndex = FindFirstEvent( m_nBroadcastTick );
  311. int index = firstIndex;
  312. float flBestRank = -1.0f;
  313. int iBestCamera = -1;
  314. int iBestTarget = -1;
  315. // sum all ranking values for the cameras
  316. while( index != m_EventHistory.InvalidIndex() )
  317. {
  318. CHLTVGameEvent &dc = m_EventHistory[index];
  319. if ( dc.m_Tick >= m_nNextShotTick )
  320. break;
  321. // search for camera ranking events
  322. if ( Q_strcmp( dc.m_Event->GetName(), "hltv_rank_entity") == 0 )
  323. {
  324. int index = dc.m_Event->GetInt("index");
  325. if ( index < MAX_PLAYERS )
  326. {
  327. flPlayerRanking[index] += dc.m_Event->GetFloat("rank" );
  328. // find best camera
  329. if ( flPlayerRanking[index] > flBestRank )
  330. {
  331. iBestCamera = index;
  332. flBestRank = flPlayerRanking[index];
  333. iBestTarget = dc.m_Event->GetInt("target");
  334. }
  335. }
  336. }
  337. index = m_EventHistory.NextInorder( index );
  338. }
  339. if ( iBestCamera != -1 )
  340. {
  341. // view over shoulder, randomly left or right
  342. StartChaseCameraShot( iBestCamera, iBestTarget, 112.0f, 20, (RandomFloat()>0.5)?20:-20, true );
  343. }
  344. else
  345. {
  346. StartBestFixedCameraShot( true );
  347. }
  348. }
  349. void CHLTVDirector::StartFixedCameraShot(int iCamera, int iTarget)
  350. {
  351. CBaseEntity *pCamera = m_pFixedCameras[iCamera];
  352. Vector vCamPos = pCamera->GetAbsOrigin();
  353. QAngle aViewAngle = pCamera->GetAbsAngles();
  354. m_iPVSEntity = 0; // don't use camera entity, since it may not been transmitted
  355. m_vPVSOrigin = vCamPos;
  356. IGameEvent *shot = gameeventmanager->CreateEvent( "hltv_fixed", true );
  357. if ( shot )
  358. {
  359. shot->SetInt("posx", vCamPos.x );
  360. shot->SetInt("posy", vCamPos.y );
  361. shot->SetInt("posz", vCamPos.z );
  362. shot->SetInt("theta", aViewAngle.x );
  363. shot->SetInt("phi", aViewAngle.y );
  364. shot->SetInt("target", iTarget );
  365. shot->SetFloat("fov", RandomFloat(50,110) );
  366. // send spectators the HLTV director command as a game event
  367. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  368. {
  369. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( shot );
  370. }
  371. gameeventmanager->FreeEvent( shot );
  372. }
  373. }
  374. void CHLTVDirector::StartChaseCameraShot(int iTarget1, int iTarget2, int distance, int phi, int theta, bool bInEye)
  375. {
  376. IGameEvent *shot = gameeventmanager->CreateEvent( "hltv_chase", true );
  377. if ( !shot )
  378. return;
  379. shot->SetInt("target1", iTarget1 );
  380. shot->SetInt("target2", iTarget2 );
  381. shot->SetInt("distance", distance );
  382. shot->SetInt("phi", phi ); // hi/low
  383. shot->SetInt( "theta", theta ); // left/right
  384. shot->SetInt( "ineye", bInEye?1:0 );
  385. m_iPVSEntity = iTarget1;
  386. // send spectators the HLTV director command as a game event
  387. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  388. {
  389. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( shot );
  390. }
  391. gameeventmanager->FreeEvent( shot );
  392. }
  393. void CHLTVDirector::StartBestFixedCameraShot( bool bForce )
  394. {
  395. float flCameraRanking[MAX_NUM_CAMERAS];
  396. if ( m_nNumFixedCameras <= 0 )
  397. return;
  398. memset( flCameraRanking, 0, sizeof(flCameraRanking) );
  399. int firstIndex = FindFirstEvent( m_nBroadcastTick );
  400. int index = firstIndex;
  401. float flBestRank = -1.0f;
  402. int iBestCamera = -1;
  403. int iBestTarget = -1;
  404. // sum all ranking values for the cameras
  405. while( index != m_EventHistory.InvalidIndex() )
  406. {
  407. CHLTVGameEvent &dc = m_EventHistory[index];
  408. if ( dc.m_Tick >= m_nNextShotTick )
  409. break;
  410. // search for camera ranking events
  411. if ( Q_strcmp( dc.m_Event->GetName(), "hltv_rank_camera") == 0 )
  412. {
  413. int index = dc.m_Event->GetInt("index");
  414. flCameraRanking[index] += dc.m_Event->GetFloat("rank" );
  415. // find best camera
  416. if ( flCameraRanking[index] > flBestRank )
  417. {
  418. iBestCamera = index;
  419. flBestRank = flCameraRanking[index];
  420. iBestTarget = dc.m_Event->GetInt("target");
  421. }
  422. }
  423. index = m_EventHistory.NextInorder( index );
  424. }
  425. if ( !bForce && flBestRank == 0 )
  426. {
  427. // if we are not forcing a fixed camera shot, switch to player chase came
  428. // if no camera shows any players
  429. StartBestPlayerCameraShot();
  430. }
  431. else if ( iBestCamera != -1 )
  432. {
  433. StartFixedCameraShot( iBestCamera, iBestTarget );
  434. }
  435. }
  436. void CHLTVDirector::StartRandomShot()
  437. {
  438. int toTick = m_nBroadcastTick + TIME_TO_TICKS ( DEF_SHOT_LENGTH );
  439. m_nNextShotTick = MIN( m_nNextShotTick, toTick );
  440. // follow a player
  441. StartBestPlayerCameraShot();
  442. }
  443. void CHLTVDirector::CreateShotFromEvent( CHLTVGameEvent *event )
  444. {
  445. const char *name = event->m_Event->GetName();
  446. bool bPlayerHurt = Q_strcmp( "player_hurt", name ) == 0;
  447. bool bPlayerKilled = Q_strcmp( "player_death", name ) == 0;
  448. bool bRoundStart = Q_strcmp( "round_start", name ) == 0;
  449. bool bRoundEnd = Q_strcmp( "round_end", name ) == 0;
  450. if ( bPlayerHurt || bPlayerKilled )
  451. {
  452. CBaseEntity *victim = UTIL_PlayerByUserId( event->m_Event->GetInt("userid") );
  453. CBaseEntity *attacker = UTIL_PlayerByUserId( event->m_Event->GetInt("attacker") );
  454. if ( !victim )
  455. return;
  456. // if killed self
  457. if ( bPlayerKilled && ( attacker == victim || attacker == NULL ) )
  458. {
  459. // player killed self or by WORLD
  460. StartChaseCameraShot( victim->entindex(), 0, 96, 20, 0, false );
  461. }
  462. // otherwise a hurt or kill
  463. else if ( attacker != victim && attacker != NULL )
  464. {
  465. // don't randomize POV if it's a multikill
  466. if ( event->m_Event->GetInt("priority") < 9 )
  467. {
  468. if ( RandomFloat(0,1) > 0.75 )
  469. {
  470. V_swap( attacker, victim );
  471. }
  472. }
  473. StartChaseCameraShot( attacker->entindex(), victim->entindex(), 96, -20, (RandomFloat()>0.5)?30:-30, true );
  474. }
  475. // stay on shot for 2 seconds after death/hurt
  476. m_nNextShotTick = MIN( m_nNextShotTick, (event->m_Tick+TIME_TO_TICKS(2.0)) );
  477. }
  478. else if ( bRoundStart || bRoundEnd )
  479. {
  480. StartBestPlayerCameraShot();
  481. }
  482. else
  483. {
  484. DevMsg( "No known TV shot for event %s\n", name );
  485. }
  486. }
  487. void CHLTVDirector::CheckHistory()
  488. {
  489. int index = m_EventHistory.FirstInorder();
  490. int lastTick = -1;
  491. while ( index != m_EventHistory.InvalidIndex() )
  492. {
  493. CHLTVGameEvent &dc = m_EventHistory[index];
  494. Assert( lastTick <= dc.m_Tick );
  495. lastTick = dc.m_Tick;
  496. index = m_EventHistory.NextInorder( index );
  497. }
  498. }
  499. void CHLTVDirector::RemoveEventsFromHistory(int tick)
  500. {
  501. int index = m_EventHistory.FirstInorder();
  502. while ( index != m_EventHistory.InvalidIndex() )
  503. {
  504. CHLTVGameEvent &dc = m_EventHistory[index];
  505. if ( (dc.m_Tick < tick) || (tick == -1) )
  506. {
  507. gameeventmanager->FreeEvent( dc.m_Event );
  508. dc.m_Event = NULL;
  509. m_EventHistory.RemoveAt( index );
  510. index = m_EventHistory.FirstInorder(); // start again
  511. }
  512. else
  513. {
  514. index = m_EventHistory.NextInorder( index );
  515. }
  516. }
  517. #ifdef _DEBUG
  518. CheckHistory();
  519. #endif
  520. }
  521. int CHLTVDirector::FindFirstEvent( int tick )
  522. {
  523. // TODO cache last queried ticks
  524. int index = m_EventHistory.FirstInorder();
  525. if ( index == m_EventHistory.InvalidIndex() )
  526. return index; // no commands in list
  527. CHLTVGameEvent *event = &m_EventHistory[index];
  528. while ( event->m_Tick < tick )
  529. {
  530. index = m_EventHistory.NextInorder( index );
  531. if ( index == m_EventHistory.InvalidIndex() )
  532. break;
  533. event = &m_EventHistory[index];
  534. }
  535. return index;
  536. }
  537. bool CHLTVDirector::SetCameraMan( int iPlayerIndex )
  538. {
  539. #ifdef CAMERAMAN_OLD_WAY
  540. if ( !tv_allow_camera_man.GetBool() )
  541. return false;
  542. if ( m_iCameraManIndex == iPlayerIndex )
  543. return true;
  544. // check if somebody else is already the camera man
  545. if ( m_iCameraManIndex != 0 && iPlayerIndex != 0 )
  546. return false;
  547. CBasePlayer *pPlayer = NULL;
  548. if ( iPlayerIndex > 0 )
  549. {
  550. pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
  551. if ( !pPlayer || pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  552. return false;
  553. }
  554. m_iCameraManIndex = iPlayerIndex;
  555. // create event for director event history
  556. IGameEvent *event = gameeventmanager->CreateEvent( "hltv_cameraman" );
  557. if ( event )
  558. {
  559. event->SetInt("index", iPlayerIndex );
  560. gameeventmanager->FireEvent( event );
  561. }
  562. CRecipientFilter filter;
  563. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  564. {
  565. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  566. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_SPECTATOR && !pPlayer->IsFakeClient() )
  567. {
  568. filter.AddRecipient( pPlayer );
  569. }
  570. }
  571. filter.MakeReliable();
  572. if ( iPlayerIndex > 0 )
  573. {
  574. // tell all spectators that the camera is in use.
  575. char szText[200];
  576. Q_snprintf( szText, sizeof(szText), "GOTV camera is now controlled by %s.", pPlayer->GetPlayerName() );
  577. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, szText );
  578. }
  579. else
  580. {
  581. // tell all spectators that the camera is available again.
  582. UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "GOTV camera switched to auto-director mode." );
  583. }
  584. #endif
  585. return true;
  586. }
  587. void CHLTVDirector::FinishCameraManShot()
  588. {
  589. #ifdef CAMERAMAN_OLD_WAY
  590. Assert( m_iCameraMan == m_iPVSEntity );
  591. int index = FindFirstEvent( m_nBroadcastTick );
  592. if ( index == m_EventHistory.InvalidIndex() )
  593. {
  594. // check next frame again if event history is empty
  595. m_nNextShotTick = m_nBroadcastTick+1;
  596. return;
  597. }
  598. m_nNextShotTick = m_nBroadcastTick + TIME_TO_TICKS( MIN_SHOT_LENGTH );
  599. //check if camera turns camera off within broadcast time and game time
  600. while( index != m_EventHistory.InvalidIndex() )
  601. {
  602. CHLTVGameEvent &dc = m_EventHistory[index];
  603. if ( dc.m_Tick >= m_nNextShotTick )
  604. break;
  605. if ( Q_strcmp( dc.m_Event->GetName(), "hltv_cameraman") == 0 )
  606. {
  607. int iNewCameraMan = dc.m_Event->GetInt("index");
  608. if ( iNewCameraMan == 0 )
  609. {
  610. // camera man switched camera off
  611. m_nNextShotTick = dc.m_Tick+1;
  612. m_iCameraMan = 0;
  613. return;
  614. }
  615. }
  616. index = m_EventHistory.NextInorder( index );
  617. }
  618. // camera man is still recording and live, resend camera man message
  619. IGameEvent *msg = gameeventmanager->CreateEvent( "hltv_cameraman", true );
  620. if ( msg )
  621. {
  622. msg->SetInt("index", m_iCameraMan );
  623. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  624. {
  625. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( msg );
  626. }
  627. gameeventmanager->FreeEvent( msg );
  628. }
  629. #endif
  630. }
  631. bool CHLTVDirector::StartCameraManShot()
  632. {
  633. #ifdef CAMERAMAN_OLD_WAY
  634. Assert( m_nNextShotTick <= m_nBroadcastTick );
  635. int index = FindFirstEvent( m_nNextShotTick );
  636. // check for cameraman mode
  637. while( index != m_EventHistory.InvalidIndex() )
  638. {
  639. CHLTVGameEvent &dc = m_EventHistory[index];
  640. // only check if this is the current tick
  641. if ( dc.m_Tick > m_nBroadcastTick )
  642. break;
  643. if ( Q_strcmp( dc.m_Event->GetName(), "hltv_cameraman") == 0 )
  644. {
  645. if ( dc.m_Event->GetInt("index") > 0 )
  646. {
  647. // ok, this guy is now the active camera man
  648. m_iCameraMan = dc.m_Event->GetInt("index");
  649. m_iPVSEntity = m_iCameraMan;
  650. m_nNextShotTick = m_nBroadcastTick+1; // check setting right on next frame
  651. // send camera man command to client
  652. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  653. {
  654. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( dc.m_Event );
  655. }
  656. return true;
  657. }
  658. }
  659. index = m_EventHistory.NextInorder( index );
  660. }
  661. #endif
  662. return false; // no camera man found
  663. }
  664. void CHLTVDirector::StartInstantBroadcastShot()
  665. {
  666. m_nNextShotTick = m_nBroadcastTick + TIME_TO_TICKS( MAX_SHOT_LENGTH );
  667. #ifdef CAMERAMAN_OLD_WAY
  668. if ( m_iCameraManIndex > 0 )
  669. {
  670. // camera man is still recording and live, resend camera man message
  671. IGameEvent *msg = gameeventmanager->CreateEvent( "hltv_cameraman", true );
  672. if ( msg )
  673. {
  674. msg->SetInt("index", m_iCameraManIndex );
  675. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  676. {
  677. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( msg );
  678. }
  679. gameeventmanager->FreeEvent( msg );
  680. m_iPVSEntity = m_iCameraManIndex;
  681. m_nNextShotTick = m_nBroadcastTick+TIME_TO_TICKS( MIN_SHOT_LENGTH );
  682. }
  683. }
  684. else
  685. #endif
  686. {
  687. RemoveEventsFromHistory(-1); // all
  688. AnalyzePlayers();
  689. AnalyzeCameras();
  690. StartRandomShot();
  691. }
  692. }
  693. void CHLTVDirector::StartNewShot()
  694. {
  695. // we can remove all events the
  696. int smallestTick = MAX(0, gpGlobals->tickcount - TIME_TO_TICKS(HLTV_MAX_DELAY) );
  697. RemoveEventsFromHistory( smallestTick );
  698. // if the delay time is too short for autodirector, just show next best thing
  699. if ( m_fDelay < HLTV_MIN_DIRECTOR_DELAY )
  700. {
  701. StartInstantBroadcastShot();
  702. return;
  703. }
  704. #ifdef CAMERAMAN_OLD_WAY
  705. if ( m_iCameraMan > 0 )
  706. {
  707. // we already have an active camera man,
  708. // wait until he releases the "record" lock
  709. FinishCameraManShot();
  710. return;
  711. }
  712. if ( StartCameraManShot() )
  713. {
  714. // now we have an active camera man
  715. return;
  716. }
  717. #endif
  718. // ok, no camera man active, now check how much time
  719. // we have for the next shot, if the time diff to the next
  720. // important event we have to switch to is too short (<2sec)
  721. // just extent the current shot and don't start a new one
  722. // check the next 8 seconds for interrupts/important events
  723. m_nNextShotTick = m_nBroadcastTick + TIME_TO_TICKS( MAX_SHOT_LENGTH );
  724. if ( m_nBroadcastTick <= 0 )
  725. {
  726. // game hasn't started yet, we are still in the broadcast delay hole
  727. IGameEvent *msg = gameeventmanager->CreateEvent( "hltv_message", true );
  728. if ( msg )
  729. {
  730. msg->SetString("text", "Please wait for broadcast to start ..." );
  731. // send spectators the HLTV director command as a game event
  732. for ( int i = 0; i < m_HltvServers.Count(); ++i )
  733. {
  734. m_HltvServers[ i ].m_pHLTVServer->BroadcastEvent( msg );
  735. }
  736. gameeventmanager->FreeEvent( msg );
  737. }
  738. StartBestFixedCameraShot( true );
  739. return;
  740. }
  741. #ifdef CAMERAMAN_OLD_WAY
  742. int index = FindFirstEvent( m_nBroadcastTick );
  743. while( index != m_EventHistory.InvalidIndex() )
  744. {
  745. CHLTVGameEvent &dc = m_EventHistory[index];
  746. if ( dc.m_Tick >= m_nNextShotTick )
  747. break; // we have searched enough
  748. // a camera man is always interrupting auto director
  749. if ( Q_strcmp( dc.m_Event->GetName(), "hltv_cameraman") == 0 )
  750. {
  751. if ( dc.m_Event->GetInt("index") > 0 )
  752. {
  753. // stop the next cut when this cameraman starts recording
  754. m_nNextShotTick = dc.m_Tick;
  755. break;
  756. }
  757. }
  758. index = m_EventHistory.NextInorder( index );
  759. }
  760. #endif
  761. float flDuration = TICKS_TO_TIME(m_nNextShotTick - m_nBroadcastTick);
  762. if ( flDuration < MIN_SHOT_LENGTH )
  763. return; // not enough time for a new shot
  764. // find the most interesting game event for next shot
  765. CHLTVGameEvent *dc = FindBestGameEvent();
  766. if ( dc )
  767. {
  768. // show the game event
  769. CreateShotFromEvent( dc );
  770. }
  771. else
  772. {
  773. // no interesting events found, start random shot
  774. StartRandomShot();
  775. }
  776. }
  777. CHLTVGameEvent *CHLTVDirector::FindBestGameEvent()
  778. {
  779. int bestEvent[4];
  780. int bestEventPrio[4];
  781. Q_memset( bestEvent, 0, sizeof(bestEvent) );
  782. Q_memset( bestEventPrio, 0, sizeof(bestEventPrio) );
  783. int index = FindFirstEvent( m_nBroadcastTick );
  784. // search for next 4 best events within next 8 seconds
  785. for (int i = 0; i<4; i ++)
  786. {
  787. bestEventPrio[i] = 0;
  788. bestEvent[i] = 0;
  789. int tillTick = m_nBroadcastTick + TIME_TO_TICKS( 2.0f*(1.0f+i) );
  790. if ( tillTick > m_nNextShotTick )
  791. break;
  792. // sum all action for the next time
  793. while ( index != m_EventHistory.InvalidIndex() )
  794. {
  795. CHLTVGameEvent &event = m_EventHistory[index];
  796. if ( event.m_Tick > tillTick )
  797. break;
  798. int priority = event.m_Priority;
  799. if ( priority > bestEventPrio[i] )
  800. {
  801. bestEvent[i] = index;
  802. bestEventPrio[i] = priority;
  803. }
  804. index = m_EventHistory.NextInorder( index );
  805. }
  806. }
  807. if ( !( bestEventPrio[0] || bestEventPrio[1] || bestEventPrio[2] ) )
  808. return NULL; // no event found at all, give generic algorithm a chance
  809. // camera cut rules :
  810. if ( bestEventPrio[1] >= bestEventPrio[0] &&
  811. bestEventPrio[1] >= bestEventPrio[2] &&
  812. bestEventPrio[1] >= bestEventPrio[3] )
  813. {
  814. return &m_EventHistory[ bestEvent[1] ]; // best case
  815. }
  816. else if ( bestEventPrio[0] > bestEventPrio[1] &&
  817. bestEventPrio[0] > bestEventPrio[2] )
  818. {
  819. return &m_EventHistory[ bestEvent[0] ]; // event 0 is very important
  820. }
  821. else if ( bestEventPrio[2] > bestEventPrio[3] )
  822. {
  823. return &m_EventHistory[ bestEvent[2] ];
  824. }
  825. else
  826. {
  827. // event 4 is the best but too far away, so show event 1
  828. if ( bestEvent[0] )
  829. return &m_EventHistory[ bestEvent[0] ];
  830. else
  831. return NULL;
  832. }
  833. }
  834. void CHLTVDirector::AnalyzeCameras()
  835. {
  836. InitRandomOrder( m_nNumFixedCameras );
  837. for ( int i = 0; i<m_nNumFixedCameras; i++ )
  838. {
  839. int iCameraIndex = s_RndOrder[i];
  840. CBaseEntity *pCamera = m_pFixedCameras[ iCameraIndex ];
  841. float flRank = 0.0f;
  842. int iClosestPlayer = 0;
  843. float flClosestPlayerDist = 100000.0f;
  844. int nCount = 0; // Number of visible targets
  845. Vector vDistribution; vDistribution.Init(); // distribution of targets
  846. Vector vCamPos = pCamera->GetAbsOrigin();
  847. for ( int j=0; j<m_nNumActivePlayers; j++ )
  848. {
  849. CBasePlayer *pPlayer = m_pActivePlayers[j];
  850. Vector vPlayerPos = pPlayer->GetAbsOrigin();
  851. float dist = VectorLength( vPlayerPos - vCamPos );
  852. if ( dist > 1024.0f || dist < 4.0f )
  853. continue; // too colse or far away
  854. // check visibility
  855. trace_t tr;
  856. UTIL_TraceLine( vCamPos, pPlayer->GetAbsOrigin(), MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
  857. if ( tr.fraction < 1.0 )
  858. continue; // not visible for camera
  859. nCount++;
  860. // remember closest player
  861. if ( dist < flClosestPlayerDist )
  862. {
  863. iClosestPlayer = pPlayer->entindex();
  864. flClosestPlayerDist = dist;
  865. }
  866. Vector v1; AngleVectors( pPlayer->EyeAngles(), &v1 );
  867. // check players orientation towards camera
  868. Vector v2 = vCamPos - vPlayerPos;
  869. VectorNormalize( v2 );
  870. // player/camera cost function:
  871. flRank += ( 1.0f/sqrt(dist) ) * WeightedAngle( v1, v2 );
  872. vDistribution += v2;
  873. }
  874. if ( nCount > 0 )
  875. {
  876. // normalize distribution
  877. flRank *= VectorLength( vDistribution ) / nCount;
  878. }
  879. IGameEvent *event = gameeventmanager->CreateEvent("hltv_rank_camera");
  880. if ( event )
  881. {
  882. event->SetFloat("rank", flRank );
  883. event->SetInt("index", iCameraIndex ); // index in m_pFixedCameras
  884. event->SetInt("target", iClosestPlayer ); // ent index
  885. gameeventmanager->FireEvent( event );
  886. }
  887. }
  888. }
  889. void CHLTVDirector::BuildActivePlayerList()
  890. {
  891. // first build list of all active players
  892. m_nNumActivePlayers = 0;
  893. for ( int i =1; i <= gpGlobals->maxClients; i++ )
  894. {
  895. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  896. if ( !pPlayer )
  897. continue;
  898. if ( !pPlayer->IsAlive() )
  899. continue;
  900. if ( pPlayer->IsObserver() )
  901. continue;
  902. if ( pPlayer->GetTeamNumber() <= TEAM_SPECTATOR )
  903. continue;
  904. m_pActivePlayers[m_nNumActivePlayers] = pPlayer;
  905. m_nNumActivePlayers++;
  906. }
  907. }
  908. void CHLTVDirector::AnalyzePlayers()
  909. {
  910. // build list of current active players
  911. BuildActivePlayerList();
  912. // analyzes every active player
  913. InitRandomOrder( m_nNumActivePlayers );
  914. for ( int i = 0; i<m_nNumActivePlayers; i++ )
  915. {
  916. int iPlayerIndex = s_RndOrder[i];
  917. CBasePlayer *pPlayer = m_pActivePlayers[ iPlayerIndex ];
  918. float flRank = 0.0f;
  919. int iBestFacingPlayer = 0;
  920. float flBestFacingPlayer = 0.0f;
  921. int nCount = 0; // Number of visible targets
  922. Vector vDistribution; vDistribution.Init(); // distribution of targets
  923. Vector vCamPos = pPlayer->GetAbsOrigin();
  924. Vector v1; AngleVectors( pPlayer->EyeAngles(), &v1 );
  925. v1 *= -1; // inverted
  926. for ( int j=0; j<m_nNumActivePlayers; j++ )
  927. {
  928. if ( iPlayerIndex == j )
  929. continue; // don't check against itself
  930. CBasePlayer *pOtherPlayer = m_pActivePlayers[j];
  931. Vector vPlayerPos = pOtherPlayer->GetAbsOrigin();
  932. float dist = VectorLength( vPlayerPos - vCamPos );
  933. if ( dist > 1024.0f || dist < 4.0f )
  934. continue; // too close or far away
  935. // check visibility
  936. trace_t tr;
  937. UTIL_TraceLine( vCamPos, pOtherPlayer->GetAbsOrigin(), MASK_SOLID, pOtherPlayer, COLLISION_GROUP_NONE, &tr );
  938. if ( tr.fraction < 1.0 )
  939. continue; // not visible for camera
  940. nCount++;
  941. // check players orientation towards camera
  942. Vector v2; AngleVectors( pOtherPlayer->EyeAngles(), &v2 );
  943. float facing = WeightedAngle( v1, v2 );
  944. // remember closest player
  945. if ( facing > flBestFacingPlayer )
  946. {
  947. iBestFacingPlayer = pOtherPlayer->entindex();
  948. flBestFacingPlayer = facing;
  949. }
  950. // player/camera cost function:
  951. flRank += ( 1.0f/sqrt(dist) ) * facing;
  952. vDistribution += v2;
  953. }
  954. if ( nCount > 0 )
  955. {
  956. float flDistribution = VectorLength( vDistribution ) / nCount; // normalize distribution
  957. flRank *= flDistribution;
  958. }
  959. IGameEvent *event = gameeventmanager->CreateEvent("hltv_rank_entity");
  960. if ( event )
  961. {
  962. event->SetInt("index", pPlayer->entindex() );
  963. event->SetFloat("rank", flRank );
  964. event->SetInt("target", iBestFacingPlayer ); // ent index
  965. gameeventmanager->FireEvent( event );
  966. }
  967. }
  968. }