//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: A camera entity that's used by the -makedevshots system to take // dev screenshots everytime the map is checked into source control. // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "tier0/icommandline.h" #include "igamesystem.h" #include "filesystem.h" #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" int g_iDevShotCameraCount = 0; #define DEVSHOT_INITIAL_WAIT 5 // Time after the level spawn before the first devshot camera takes it's shot #define DEVSHOT_INTERVAL 5 // Time between each devshot camera taking it's shot //----------------------------------------------------------------------------- // Purpose: A camera entity that's used by the -makedevshots system to take // dev screenshots everytime the map is checked into source control. //----------------------------------------------------------------------------- class CPointDevShotCamera : public CBaseEntity { DECLARE_CLASS( CPointDevShotCamera, CBaseEntity ); public: DECLARE_DATADESC(); void Spawn( void ); void DevShotThink_Setup( void ); void DevShotThink_TakeShot( void ); void DevShotThink_PostShot( void ); // Always transmit to clients so they know where to move the view to virtual int UpdateTransmitState(); private: string_t m_iszCameraName; int m_iFOV; }; BEGIN_DATADESC( CPointDevShotCamera ) DEFINE_FUNCTION( DevShotThink_Setup ), DEFINE_FUNCTION( DevShotThink_TakeShot ), DEFINE_FUNCTION( DevShotThink_PostShot ), DEFINE_KEYFIELD( m_iszCameraName, FIELD_STRING, "cameraname" ), DEFINE_KEYFIELD( m_iFOV, FIELD_INTEGER, "FOV" ), END_DATADESC() LINK_ENTITY_TO_CLASS( point_devshot_camera, CPointDevShotCamera ); //----------------------------------------------------------------------------- // Purpose: Convenience function so we don't have to make this check all over //----------------------------------------------------------------------------- static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) { if ( gpGlobals->maxClients > 1 ) { if ( engine->IsDedicatedServer() ) { return NULL; } return UTIL_GetListenServerHost(); } return UTIL_GetLocalPlayer(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointDevShotCamera::Spawn( void ) { BaseClass::Spawn(); // Remove this entity immediately if we're not making devshots if ( !CommandLine()->FindParm("-makedevshots") ) { UTIL_Remove( this ); return; } // Take a screenshot when it's my turn SetThink( &CPointDevShotCamera::DevShotThink_Setup ); SetNextThink( gpGlobals->curtime + DEVSHOT_INITIAL_WAIT + (g_iDevShotCameraCount * DEVSHOT_INTERVAL) ); g_iDevShotCameraCount++; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CPointDevShotCamera::UpdateTransmitState() { // always transmit if currently used by a monitor return SetTransmitState( FL_EDICT_ALWAYS ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointDevShotCamera::DevShotThink_Setup( void ) { // Move the player to the devshot camera CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); if ( !pPlayer ) return; // Hide stuff engine->ClientCommand( pPlayer->edict(), "developer 0" ); engine->ClientCommand( pPlayer->edict(), "cl_drawhud 0" ); engine->ClientCommand( pPlayer->edict(), "sv_cheats 1" ); engine->ClientCommand( pPlayer->edict(), "god" ); engine->ClientCommand( pPlayer->edict(), "notarget" ); pPlayer->AddSolidFlags( FSOLID_NOT_SOLID ); pPlayer->EnableControl(FALSE); pPlayer->SetViewEntity( this ); pPlayer->SetFOV( this, m_iFOV ); // Hide the player's viewmodel if ( pPlayer->GetActiveWeapon() ) { pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW ); } DispatchUpdateTransmitState(); // Now take the shot next frame SetThink( &CPointDevShotCamera::DevShotThink_TakeShot ); SetNextThink( gpGlobals->curtime ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointDevShotCamera::DevShotThink_TakeShot( void ) { // Take the screenshot CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); if ( !pPlayer ) return; engine->ClientCommand( pPlayer->edict(), "devshots_screenshot \"%s\"", STRING(m_iszCameraName) ); // Now take the shot next frame SetThink( &CPointDevShotCamera::DevShotThink_PostShot ); SetNextThink( gpGlobals->curtime + (DEVSHOT_INTERVAL - 1) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointDevShotCamera::DevShotThink_PostShot( void ) { // Take the screenshot CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); if ( !pPlayer ) return; pPlayer->SetFOV( this, 0 ); // If all cameras have taken their shots, move to the next map g_iDevShotCameraCount--; if ( !g_iDevShotCameraCount ) { engine->ClientCommand( pPlayer->edict(), "devshots_nextmap" ); } } //----------------------------------------------------------------------------- // Purpose: Game system to detect maps without cameras in them, and move on //----------------------------------------------------------------------------- class CDevShotSystem : public CAutoGameSystemPerFrame { public: CDevShotSystem( char const *name ) : CAutoGameSystemPerFrame( name ) { } virtual void LevelInitPreEntity() { m_bIssuedNextMapCommand = false; g_iDevShotCameraCount = 0; m_bParsedMapFile = false; } virtual void SafeRemoveIfDesired( void ) { // If we're not making devshots, remove this system immediately if ( !CommandLine()->FindParm("-makedevshots") ) { Remove( this ); return; } } virtual void FrameUpdatePostEntityThink( void ) { // Wait until we're all spawned in if ( gpGlobals->curtime < 5 ) return; if ( m_bIssuedNextMapCommand ) return; if ( !m_bParsedMapFile ) { m_bParsedMapFile = true; // See if we've got a camera file to import cameras from char szFullName[512]; Q_snprintf(szFullName,sizeof(szFullName), "maps/%s.txt", STRING( gpGlobals->mapname )); KeyValues *pkvMapCameras = new KeyValues( "MapCameras" ); if ( pkvMapCameras->LoadFromFile( filesystem, szFullName, "MOD" ) ) { Warning( "Devshots: Loading point_devshot_camera positions from %s. \n", szFullName ); // Get each camera, and add it to our list KeyValues *pkvCamera = pkvMapCameras->GetFirstSubKey(); while ( pkvCamera ) { // Get camera name const char *pCameraName = pkvCamera->GetName(); // Make a camera, and move it to the position specified CPointDevShotCamera *pCamera = (CPointDevShotCamera*)CreateEntityByName( "point_devshot_camera" ); Assert( pCamera ); pCamera->KeyValue( "cameraname", pCameraName ); pCamera->KeyValue( "origin", pkvCamera->GetString( "origin", "0 0 0" ) ); pCamera->KeyValue( "angles", pkvCamera->GetString( "angles", "0 0 0" ) ); pCamera->KeyValue( "FOV", pkvCamera->GetString( "FOV", "75" ) ); DispatchSpawn( pCamera ); pCamera->Activate(); // Move to next camera pkvCamera = pkvCamera->GetNextKey(); } } if ( !g_iDevShotCameraCount ) { Warning( "Devshots: No point_devshot_camera in %s. Moving to next map.\n", STRING( gpGlobals->mapname ) ); CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); if ( pPlayer ) { engine->ClientCommand( pPlayer->edict(), "devshots_nextmap" ); m_bIssuedNextMapCommand = true; return; } } } } private: bool m_bIssuedNextMapCommand; bool m_bParsedMapFile; }; CDevShotSystem DevShotSystem( "CDevShotSystem" );