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.

1208 lines
28 KiB

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