summaryrefslogtreecommitdiff
path: root/game/client/tf/tf_replay.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/tf_replay.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/tf/tf_replay.cpp')
-rw-r--r--game/client/tf/tf_replay.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/game/client/tf/tf_replay.cpp b/game/client/tf/tf_replay.cpp
new file mode 100644
index 0000000..372b627
--- /dev/null
+++ b/game/client/tf/tf_replay.cpp
@@ -0,0 +1,399 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "tf_replay.h"
+#include "tf/tf_shareddefs.h"
+#include "tf/c_tf_player.h"
+#include "tf/c_tf_playerresource.h"
+#include "tf/c_tf_gamestats.h"
+#include "tf/tf_gamestats_shared.h"
+#include "tf/tf_hud_statpanel.h"
+#include "tf/c_obj_sentrygun.h"
+#include "clientmode_shared.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/ireplayfactory.h"
+#include "replay/ireplayscreenshotmanager.h"
+#include "replay/screenshot.h"
+#include <time.h>
+
+//----------------------------------------------------------------------------------------
+
+extern IReplayScreenshotManager *g_pReplayScreenshotManager;
+
+//----------------------------------------------------------------------------------------
+
+CTFReplay::CTFReplay()
+: m_flNextMedicUpdateTime( 0.0f )
+{
+}
+
+CTFReplay::~CTFReplay()
+{
+}
+
+void CTFReplay::OnBeginRecording()
+{
+ BaseClass::OnBeginRecording();
+
+ // Setup the newly created replay
+ C_TFPlayer* pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pPlayer )
+ {
+ if ( pPlayer->GetPlayerClass() )
+ {
+ SetPlayerClass( pPlayer->GetPlayerClass()->GetClassIndex() );
+ }
+
+ SetPlayerTeam( pPlayer->GetTeamNumber() );
+ }
+}
+
+void CTFReplay::OnEndRecording()
+{
+ if ( gameeventmanager )
+ {
+ gameeventmanager->RemoveListener( this );
+ }
+
+ BaseClass::OnEndRecording();
+}
+
+void CTFReplay::OnComplete()
+{
+ BaseClass::OnComplete();
+}
+
+void CTFReplay::Update()
+{
+ // If local player is medic and invuln'd someone, take a screenshot
+ MedicUpdate();
+
+ BaseClass::Update();
+}
+
+void CTFReplay::MedicUpdate()
+{
+ // Not ready for update?
+ if ( gpGlobals->curtime < m_flNextMedicUpdateTime )
+ return;
+
+ // Local player doesn't exist?
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer )
+ {
+ Assert( 0 ); // Shouldn't happen
+ return;
+ }
+
+ if ( pLocalPlayer->GetPlayerClass()->GetClassIndex() != TF_CLASS_MEDIC )
+ return;
+
+ // Releasing charge?
+ if ( pLocalPlayer->MedicIsReleasingCharge() )
+ {
+ // Take a sick screenshot
+ CaptureScreenshotParams_t params;
+ V_memset( &params, 0, sizeof( params ) );
+ params.m_flDelay = 0.25f;
+ g_pReplayScreenshotManager->CaptureScreenshot( params );
+
+ // Set next update to minimum time it would be until next recharge
+ extern ConVar weapon_medigun_chargerelease_rate;
+ m_flNextMedicUpdateTime = gpGlobals->curtime + weapon_medigun_chargerelease_rate.GetFloat();
+ }
+ else
+ {
+ // Check again in a second
+ m_flNextMedicUpdateTime = gpGlobals->curtime + 1.0f;
+ }
+}
+
+float CTFReplay::GetSentryKillScreenshotDelay()
+{
+ ConVarRef replay_screenshotsentrykilldelay( "replay_screenshotsentrykilldelay" );
+ return replay_screenshotsentrykilldelay.IsValid() ? replay_screenshotsentrykilldelay.GetFloat() : 0.5f;
+}
+
+void CTFReplay::FireGameEvent( IGameEvent *pEvent )
+{
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer )
+ return;
+
+ CaptureScreenshotParams_t params;
+ V_memset( &params, 0, sizeof( params ) );
+
+ if ( FStrEq( pEvent->GetName(), "player_death" ) )
+ {
+ ConVarRef replay_debug( "replay_debug" );
+ if ( replay_debug.IsValid() && replay_debug.GetBool() )
+ {
+ DevMsg( "%i: CTFReplay::FireGameEvent(): player_death\n", gpGlobals->tickcount );
+ }
+
+ int nKillerID = pEvent->GetInt( "attacker" );
+ int nVictimID = pEvent->GetInt( "userid" );
+ int nDeathFlags = pEvent->GetInt( "death_flags" );
+ int nAssisterID = pEvent->GetInt( "assister" );
+
+ const char *pWeaponName = pEvent->GetString( "weapon" );
+
+ // Suicide?
+ bool bSuicide = nKillerID == nVictimID;
+
+ // Try to get killer
+ C_TFPlayer *pKiller = ToTFPlayer( USERID2PLAYER( nKillerID ) );
+
+ // Try to get victim
+ C_TFPlayer *pVictim = ToTFPlayer( USERID2PLAYER( nVictimID ) );
+
+ // Is local player healing the killer?
+ bool bKillerLastHealerIsLocalPlayer = pKiller && pKiller->GetWasHealedByLocalPlayer();
+
+ // Inflictor was a sentry gun?
+ bool bSentry = V_strnicmp( pWeaponName, "obj_sentrygun", 13 ) == 0;
+ int nInflictorEntIndex = pEvent->GetInt( "inflictor_entindex" );
+ C_BaseEntity *pInflictor = ClientEntityList().GetEnt( nInflictorEntIndex );
+ C_ObjectSentrygun *pSentry = dynamic_cast< C_ObjectSentrygun * >( pInflictor );
+ bool bFeignDeath = pEvent->GetInt( "death_flags" ) & TF_DEATH_FEIGN_DEATH;
+
+ // Is the killer the local player?
+ if ( nKillerID == pLocalPlayer->GetUserID() &&
+ !bSuicide &&
+ !bSentry )
+ {
+ // Domination?
+ if ( nDeathFlags & TF_DEATH_DOMINATION )
+ {
+ AddDomination( nVictimID );
+ }
+
+ // Assister domination?
+ if ( ( nDeathFlags & TF_DEATH_ASSISTER_DOMINATION ) && ( nAssisterID > 0 ) )
+ {
+ AddAssisterDomination( nVictimID, nAssisterID );
+ }
+
+ // Revenge?
+ if ( pEvent->GetInt( "death_flags" ) & TF_DEATH_REVENGE )
+ {
+ AddRevenge( nVictimID );
+ }
+
+ // Assister revenge?
+ if ( pEvent->GetInt( "death_flags" ) & TF_DEATH_ASSISTER_REVENGE && ( nAssisterID > 0 ) )
+ {
+ AddAssisterRevenge( nVictimID, nAssisterID );
+ }
+
+ // Add victim info to kill list
+ if ( pVictim )
+ {
+ AddKill( pVictim->GetPlayerName(), pVictim->GetPlayerClass()->GetClassIndex() );
+ }
+
+ // Take a quick screenshot with some delay
+ ConVarRef replay_screenshotkilldelay( "replay_screenshotkilldelay" );
+ if ( replay_screenshotkilldelay.IsValid() )
+ {
+ params.m_flDelay = GetKillScreenshotDelay();
+ g_pReplayScreenshotManager->CaptureScreenshot( params );
+ }
+ }
+
+ // Player death?
+ else if ( pKiller &&
+ nVictimID == pLocalPlayer->GetUserID() )
+ {
+ // Record who killed the player if not a suicide
+ if ( !bSuicide && !bFeignDeath )
+ {
+ RecordPlayerDeath( pKiller->GetPlayerName(), pKiller->GetPlayerClass()->GetClassIndex() );
+ }
+
+ // Take screenshot - taking a screenshot during feign death is cool, too.
+ ConVarRef replay_deathcammaxverticaloffset( "replay_deathcammaxverticaloffset" );
+ ConVarRef replay_playerdeathscreenshotdelay( "replay_playerdeathscreenshotdelay" );
+ params.m_flDelay = replay_playerdeathscreenshotdelay.IsValid() ? replay_playerdeathscreenshotdelay.GetFloat() : 0.0f;
+ params.m_nEntity = pLocalPlayer->entindex();
+ params.m_posCamera.Init( 0,0, replay_deathcammaxverticaloffset.IsValid() ? replay_deathcammaxverticaloffset.GetFloat() : 150 );
+ params.m_angCamera.Init( 90, 0, 0 ); // Look straight down
+ params.m_bUseCameraAngles = true;
+ params.m_bIgnoreMinTimeBetweenScreenshots = true;
+ g_pReplayScreenshotManager->CaptureScreenshot( params );
+ }
+
+ // Killer is invuln/crit boosted and healer is local player?
+ else if ( bKillerLastHealerIsLocalPlayer &&
+ ( pKiller->m_Shared.IsCritBoosted() ||
+ pKiller->m_Shared.InCond( TF_COND_INVULNERABLE ) ||
+ pKiller->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) ) )
+ {
+ // Take a quick screenshot with some delay
+ params.m_flDelay = GetKillScreenshotDelay();
+ g_pReplayScreenshotManager->CaptureScreenshot( params );
+ }
+
+ // Is the inflictor a sentry belonging to the local player?
+ else if ( pLocalPlayer->IsAlive() &&
+ bSentry &&
+ pSentry &&
+ pSentry->GetOwner() == pLocalPlayer &&
+ pVictim )
+ {
+ ConVarRef replay_sentrycammaxverticaloffset( "replay_sentrycammaxverticaloffset" );
+ ConVarRef replay_sentrycamoffset_frontback( "replay_sentrycamoffset_frontback" );
+ ConVarRef replay_sentrycamoffset_leftright( "replay_sentrycamoffset_leftright" );
+ ConVarRef replay_sentrycamoffset_updown( "replay_sentrycamoffset_updown" );
+
+ // Setup screenshot params
+ params.m_flDelay = GetSentryKillScreenshotDelay();
+ params.m_nEntity = pSentry->entindex();
+ params.m_bUseCameraAngles = true;
+
+ // Calculate camera eye position
+ static float s_aSentryEyeLevels[3] = { SENTRYGUN_EYE_OFFSET_LEVEL_1[2], SENTRYGUN_EYE_OFFSET_LEVEL_2[2], SENTRYGUN_EYE_OFFSET_LEVEL_3[2] };
+ int iSentryUpgrade = clamp( pSentry->GetUpgradeLevel() - 1, 0, 2 );
+ Vector vecSentryEyeOffset = Vector( 0, 0, s_aSentryEyeLevels[ iSentryUpgrade ] );
+
+ Vector vecSentryAimDir; // Since it seems the sentry's *actual* aim direction is only available on the server, use the victim's location to calculate a general idea of one
+ Vector vecVictimUp = pVictim->WorldSpaceCenter() - pVictim->GetAbsOrigin(); // WorldSpaceCenter() seems to return player's eye level
+ Vector vecVictimCenter = pVictim->GetAbsOrigin() + 0.5f * vecVictimUp;
+ vecSentryAimDir = vecVictimCenter - pSentry->GetAbsOrigin() + vecSentryEyeOffset;
+ VectorNormalizeFast( vecSentryAimDir );
+
+ Vector vecX, vecY, vecZ; // Construct a matrix to transform the eye point
+ vecX = vecSentryAimDir;
+ vecY = CrossProduct( Vector(0,0,1), vecSentryAimDir );
+ vecZ = CrossProduct( vecX, vecY );
+ matrix3x4_t m;
+ m.Init( vecX, vecY, vecZ, vec3_origin );
+
+ Vector out; // Transform the point relative to the sentry's eye
+ Vector vecOffset;
+ if ( replay_sentrycamoffset_frontback.IsValid() &&
+ replay_sentrycamoffset_leftright.IsValid() &&
+ replay_sentrycamoffset_updown.IsValid() )
+ {
+ vecOffset.Init( replay_sentrycamoffset_frontback.GetFloat(), -replay_sentrycamoffset_leftright.GetFloat(), replay_sentrycamoffset_updown.GetFloat() );
+ }
+ else
+ {
+ vecOffset.Init( 0, 0, 0 );
+ }
+ VectorTransform( vecOffset, m, out );
+ out += Vector( 0,0, s_aSentryEyeLevels[ iSentryUpgrade ] + ( replay_sentrycammaxverticaloffset.IsValid() ? replay_sentrycammaxverticaloffset.GetFloat() : 5 ) );
+ params.m_posCamera = out;
+
+ // Use the aim matrix we constructed as the camera's orientation
+ MatrixAngles( m, params.m_angCamera );
+
+ // Take the screenshot from the sentry's point of view
+ g_pReplayScreenshotManager->CaptureScreenshot( params );
+ }
+ }
+}
+
+bool CTFReplay::IsValidClass( int nClass ) const
+{
+ return IsValidTFPlayerClass( nClass );
+}
+
+bool CTFReplay::IsValidTeam( int iTeam ) const
+{
+ return IsValidTFTeam( iTeam );
+}
+
+bool CTFReplay::GetCurrentStats( RoundStats_t &out )
+{
+ if ( !g_TF_PR )
+ return false;
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer )
+ return false;
+
+ int iLocalPlayerIndex = GetLocalPlayerIndex();
+
+ out.m_iStat[ TFSTAT_POINTSSCORED ] = g_TF_PR->GetPlayerScore( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_DEATHS ] = g_TF_PR->GetDeaths( iLocalPlayerIndex );
+
+ out.m_iStat[ TFSTAT_CAPTURES ] = pLocalPlayer->m_Shared.GetCaptures( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_DEFENSES ] = pLocalPlayer->m_Shared.GetDefenses( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_DOMINATIONS ] = pLocalPlayer->m_Shared.GetDominations( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_REVENGE ] = pLocalPlayer->m_Shared.GetRevenge( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_BUILDINGSDESTROYED ] = pLocalPlayer->m_Shared.GetBuildingsDestroyed( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_HEADSHOTS ] = pLocalPlayer->m_Shared.GetHeadshots( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_BACKSTABS ] = pLocalPlayer->m_Shared.GetBackstabs( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_HEALING ] = pLocalPlayer->m_Shared.GetHealPoints( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_INVULNS ] = pLocalPlayer->m_Shared.GetInvulns( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_TELEPORTS ] = pLocalPlayer->m_Shared.GetTeleports( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_KILLASSISTS ] = pLocalPlayer->m_Shared.GetKillAssists( iLocalPlayerIndex );
+ out.m_iStat[ TFSTAT_BONUS_POINTS ] = pLocalPlayer->m_Shared.GetBonusPoints( iLocalPlayerIndex );
+
+ return true;
+}
+
+const char *CTFReplay::GetStatString( int iStat ) const
+{
+ COMPILE_TIME_ASSERT( TFSTAT_TOTAL == ARRAYSIZE( s_pStatStrings ) );
+ Assert( iStat >= TFSTAT_UNDEFINED && iStat < TFSTAT_TOTAL );
+ return ClampedArrayElement( s_pStatStrings, iStat );
+}
+
+const char *CTFReplay::GetPlayerClass( int iClass ) const
+{
+ COMPILE_TIME_ASSERT( TF_CLASS_MENU_BUTTONS == ARRAYSIZE( g_aPlayerClassNames_NonLocalized ) );
+ Assert( iClass >= TF_CLASS_UNDEFINED && iClass < TF_CLASS_COUNT );
+ return ClampedArrayElement( g_aPlayerClassNames_NonLocalized, iClass );
+}
+
+bool CTFReplay::Read( KeyValues *pIn )
+{
+ return BaseClass::Read( pIn );
+}
+
+void CTFReplay::Write( KeyValues *pOut )
+{
+ BaseClass::Write( pOut );
+}
+
+const char *CTFReplay::GetMaterialFriendlyPlayerClass() const
+{
+ const char *pPlayerClass = BaseClass::GetMaterialFriendlyPlayerClass();
+ if ( !V_stricmp( pPlayerClass, "heavyweapons" ) )
+ return "heavy";
+
+ else if ( !V_stricmp( pPlayerClass, "demoman" ) )
+ return "demo";
+
+ return pPlayerClass;
+}
+
+void CTFReplay::DumpGameSpecificData() const
+{
+ BaseClass::DumpGameSpecificData();
+}
+
+//----------------------------------------------------------------------------------------
+
+class CTFReplayFactory : public IReplayFactory
+{
+public:
+ virtual CReplay *Create()
+ {
+ return new CTFReplay();
+ }
+};
+
+static CTFReplayFactory s_ReplayManager;
+IReplayFactory *g_pReplayFactory = &s_ReplayManager;
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CTFReplayFactory, IReplayFactory, INTERFACE_VERSION_REPLAY_FACTORY, s_ReplayManager );
+
+#endif \ No newline at end of file