void CHLTVDirector::StartBestPlayerCameraShot() { float flPlayerRanking[MAX_PLAYERS]; memset( flPlayerRanking, 0, sizeof(flPlayerRanking) ); int firstIndex = FindFirstEvent( m_nBroadcastTick ); int index = firstIndex; float flBestRank = -1.0f; int iBestCamera = -1; int iBestTarget = -1; // sum all ranking values for the cameras while( index != m_EventHistory.InvalidIndex() ) { CHLTVGameEvent &dc = m_EventHistory[index]; if ( dc.m_Tick >= m_nNextShotTick ) break; // search for camera ranking events if ( Q_strcmp( dc.m_Event->GetName(), "hltv_rank_entity") == 0 ) { int index = dc.m_Event->GetInt("index"); if ( index < MAX_PLAYERS ) { flPlayerRanking[index] += dc.m_Event->GetFloat("rank" ); // find best camera if ( flPlayerRanking[index] > flBestRank ) { iBestCamera = index; flBestRank = flPlayerRanking[index]; iBestTarget = dc.m_Event->GetInt("target"); } } } index = m_EventHistory.NextInorder( index ); } if ( iBestCamera != -1 ) { // view over shoulder, randomly left or right StartChaseCameraShot( iBestCamera, iBestTarget, 112.0f, 20, (RandomFloat()>0.5)?20:-20, false ); } else { StartBestFixedCameraShot( true ); } }
void CHLTVDirector::CreateShotFromEvent( CHLTVGameEvent *event ) { // show event at least for 2 more seconds after it occured const char *name = event->m_Event->GetName(); bool bPlayerHurt = Q_strcmp( "player_hurt", name ) == 0; bool bPlayerKilled = Q_strcmp( "player_death", name ) == 0; bool bRoundStart = Q_strcmp( "round_start", name ) == 0; bool bRoundEnd = Q_strcmp( "round_end", name ) == 0; if ( bPlayerHurt || bPlayerKilled ) { CBaseEntity *victim = UTIL_PlayerByUserId( event->m_Event->GetInt("userid") ); CBaseEntity *attacker = UTIL_PlayerByUserId( event->m_Event->GetInt("attacker") ); if ( !victim ) return; if ( attacker == victim || attacker == NULL ) { // player killed self or by WORLD StartChaseCameraShot( victim->entindex(), 0, 96, 20, 0, false ); } else // attacker != NULL { // check if we would show it from ineye view bool bInEye = (bPlayerKilled && RandomFloat(0,1) > 0.33) || (bPlayerHurt && RandomFloat(0,1) > 0.66); // if we show ineye view, show it more likely from killer if ( RandomFloat(0,1) > (bInEye?0.3f:0.7f) ) { V_swap( attacker, victim ); } // hurting a victim is shown as chase more often // view from behind over head // lower view point, dramatic // view over shoulder, randomly left or right StartChaseCameraShot( victim->entindex(), attacker->entindex(), 96, -20, (RandomFloat()>0.5)?30:-30, bInEye ); } // shot 2 seconds after death/hurt m_nNextShotTick = MIN( m_nNextShotTick, (event->m_Tick+TIME_TO_TICKS(2.0)) ); } else if ( bRoundStart || bRoundEnd ) { StartBestFixedCameraShot( false ); } else { DevMsg( "No known TV shot for event %s\n", name ); } }
void CHLTVDirector::StartRandomShot() { int toTick = m_nBroadcastTick + TIME_TO_TICKS ( DEF_SHOT_LENGTH ); m_nNextShotTick = MIN( m_nNextShotTick, toTick ); if ( RandomFloat(0,1) < 0.25 && tv_allow_static_shots.GetBool() ) { // create a static shot from a level camera StartBestFixedCameraShot( false ); } else { // follow a player StartBestPlayerCameraShot(); } }
void CHLTVDirector::StartDelayMessage() { if ( m_nNextShotTick > gpGlobals->tickcount ) return; // check the next 8 seconds for interrupts/important events m_nNextShotTick = gpGlobals->tickcount + TIME_TO_TICKS( DEF_SHOT_LENGTH ); // game hasn't started yet, we are still in the broadcast delay hole IGameEvent *msg = gameeventmanager->CreateEvent( "hltv_message", true ); if ( msg ) { msg->SetString("text", "Please wait for broadcast to start ..." ); // send spectators the HLTV director command as a game event m_pHLTVServer->BroadcastEvent( msg ); gameeventmanager->FreeEvent( msg ); } StartBestFixedCameraShot( true ); }
void CHLTVDirector::StartNewShot() { // we can remove all events the int smallestTick = MAX(0, gpGlobals->tickcount - TIME_TO_TICKS(HLTV_MAX_DELAY) ); RemoveEventsFromHistory( smallestTick ); if ( m_iCameraMan > 0 ) { // we already have an active camera man, // wait until he releases the "record" lock FinishCameraManShot(); return; } if ( StartCameraManShot() ) { // now we have an active camera man return; } // ok, no camera man active, now check how much time // we have for the next shot, if the time diff to the next // important event we have to switch to is too short (<2sec) // just extent the current shot and don't start a new one // check the next 8 seconds for interrupts/important events m_nNextShotTick = m_nBroadcastTick + TIME_TO_TICKS( MAX_SHOT_LENGTH ); if ( m_nBroadcastTick <= 0 ) { // game hasn't started yet, we are still in the broadcast delay hole IGameEvent *msg = gameeventmanager->CreateEvent( "hltv_message", true ); if ( msg ) { msg->SetString("text", "Please wait for broadcast to start ..." ); // send spectators the HLTV director command as a game event m_pHLTVServer->BroadcastEvent( msg ); gameeventmanager->FreeEvent( msg ); } StartBestFixedCameraShot( true ); return; } int index = FindFirstEvent( m_nBroadcastTick ); while( index != m_EventHistory.InvalidIndex() ) { CGameEvent &dc = m_EventHistory[index]; if ( dc.m_Tick >= m_nNextShotTick ) break; // we have searched enough // a camera man is always interrupting auto director if ( Q_strcmp( dc.m_Event->GetName(), "hltv_cameraman") == 0 ) { if ( dc.m_Event->GetInt("index") > 0 ) { // stop the next cut when this cameraman starts recording m_nNextShotTick = dc.m_Tick; break; } } index = m_EventHistory.NextInorder( index ); } float flDuration = TICKS_TO_TIME(m_nNextShotTick - m_nBroadcastTick); if ( flDuration < MIN_SHOT_LENGTH ) return; // not enough time for a new shot // find the most intesting game event for next shot CGameEvent *dc = FindBestGameEvent(); if ( dc ) { // show the game event CreateShotFromEvent( dc ); } else { // no interesting events found, start random shot StartRandomShot(); } }
void CHLTVDirector::CreateShotFromEvent( CGameEvent *event ) { // show event at least for 2 more seconds after it occured const char *name = event->m_Event->GetName(); IGameEvent *shot = NULL; bool bPlayerHurt = Q_strcmp( "player_hurt", name ) == 0; bool bPlayerKilled = Q_strcmp( "player_death", name ) == 0; bool bRoundStart = Q_strcmp( "round_start", name ) == 0; bool bRoundEnd = Q_strcmp( "round_end", name ) == 0; if ( bPlayerHurt || bPlayerKilled ) { CBaseEntity *victim = UTIL_PlayerByUserId( event->m_Event->GetInt("userid") ); CBaseEntity *attacker = UTIL_PlayerByUserId( event->m_Event->GetInt("attacker") ); if ( !victim ) return; if ( attacker == victim || attacker == NULL ) { // player killed self or by WORLD shot = gameeventmanager->CreateEvent( "hltv_chase", true ); shot->SetInt( "target1", victim->entindex() ); shot->SetInt( "target2", 0 ); shot->SetInt( "theta", 0 ); // view from behind over head shot->SetInt( "phi", 20 ); // from above shot->SetFloat( "distance", 96.0f ); } else // attacker != NULL { // check if we would show it from ineye view bool bInEye = (bPlayerKilled && RandomFloat(0,1) > 0.33) || (bPlayerHurt && RandomFloat(0,1) > 0.66); // if we show ineye view, show it more likely from killer if ( RandomFloat(0,1) > (bInEye?0.3f:0.7f) ) { swap( attacker, victim ); } // hurting a victim is shown as chase more often shot = gameeventmanager->CreateEvent( "hltv_chase", true ); shot->SetInt( "target1", victim->entindex() ); shot->SetInt( "target2", attacker->entindex() ); // view from behind over head shot->SetInt( "phi", -20 ); // lower view point, dramatic shot->SetFloat( "distance", 96.0f ); shot->SetInt( "ineye", bInEye?1:0 ); // view over shoulder, randomly left or right if ( RandomFloat(0,1) > 0.5 ) { shot->SetInt( "theta", 30 ); // swing left } else { shot->SetInt( "theta", -30 ); // swing right } } // shot 2 seconds after death/hurt m_nNextShotTick = MIN( m_nNextShotTick, (event->m_Tick+TIME_TO_TICKS(2.0)) ); m_iPVSEntity = victim->entindex(); } else if ( bRoundStart || bRoundEnd ) { StartBestFixedCameraShot( false ); } else { DevMsg( "No known TV shot for event %s\n", name ); } if ( shot ) { m_pHLTVServer->BroadcastEvent( shot ); gameeventmanager->FreeEvent( shot ); } }
void CHLTVDirector::StartBestPlayerCameraShot() { float flPlayerRanking[MAX_PLAYERS]; memset( flPlayerRanking, 0, sizeof(flPlayerRanking) ); int firstIndex = FindFirstEvent( m_nBroadcastTick ); int index = firstIndex; float flBestRank = -1.0f; int iBestCamera = -1; int iBestTarget = -1; // sum all ranking values for the cameras while( index != m_EventHistory.InvalidIndex() ) { CGameEvent &dc = m_EventHistory[index]; if ( dc.m_Tick >= m_nNextShotTick ) break; // search for camera ranking events if ( Q_strcmp( dc.m_Event->GetName(), "hltv_rank_entity") == 0 ) { int index = dc.m_Event->GetInt("index"); if ( index < MAX_PLAYERS ) { flPlayerRanking[index] += dc.m_Event->GetFloat("rank" ); // find best camera if ( flPlayerRanking[index] > flBestRank ) { iBestCamera = index; flBestRank = flPlayerRanking[index]; iBestTarget = dc.m_Event->GetInt("target"); } } } index = m_EventHistory.NextInorder( index ); } if ( iBestCamera != -1 ) { IGameEvent *shot = gameeventmanager->CreateEvent( "hltv_chase", true ); if ( shot ) { shot->SetInt("target1", iBestCamera ); shot->SetInt("target2", iBestTarget ); shot->SetInt("distance", 112 ); shot->SetInt("phi", 20 ); // view over shoulder, randomly left or right if ( RandomFloat(0,1) > 0.5 ) { shot->SetInt( "theta", 20 ); // swing left } else { shot->SetInt( "theta", -20 ); // swing right } m_iPVSEntity = iBestCamera; // send spectators the HLTV director command as a game event m_pHLTVServer->BroadcastEvent( shot ); gameeventmanager->FreeEvent( shot ); } } else { StartBestFixedCameraShot( true ); } }