//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// 4-23-98  
// JOHN:  implementation of interface between client-side DLL and game engine.
//  The cdll shouldn't have to know anything about networking or file formats.
//  This file is Win32-dependant
//
//=============================================================================//

#include "client_pch.h"
#include "getintersectingsurfaces_struct.h"
#include "gl_model_private.h"
#include "surfinfo.h"
#include "vstdlib/random.h"
#include "cdll_int.h"
#include "cmodel_engine.h"
#include "tmessage.h"
#include "console.h"
#include "snd_audio_source.h"
#include <vgui_controls/Controls.h>
#include <vgui/IInput.h>
#include "iengine.h"
#include "keys.h"
#include "con_nprint.h"
#include "tier0/vprof.h"
#include "sound.h"
#include "gl_rmain.h"
#include "proto_version.h"
#include "client_class.h"
#include "gl_rsurf.h"
#include "server.h"
#include "r_local.h"
#include "lightcache.h"
#include "gl_matsysiface.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/materialsystem_config.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/itexture.h"
#include "istudiorender.h"
#include "l_studio.h"
#include "voice.h"
#include "enginestats.h"
#include "testscriptmgr.h"
#include "r_areaportal.h"
#include "host.h"
#include "host_cmd.h"
#include "vox.h"
#include "iprediction.h"
#include "icliententitylist.h"
#include "eiface.h"
#include "ivguicenterprint.h"
#include "engine/IClientLeafSystem.h"
#include "dt_recv_eng.h"
#include <vgui/IVGui.h>
#include "sys_dll.h"
#include "vphysics_interface.h"
#include "materialsystem/imesh.h"
#include "IOcclusionSystem.h"
#include "filesystem_engine.h"
#include "tier0/icommandline.h"
#include "client_textmessage.h"
#include "host_saverestore.h"
#include "cl_main.h"
#include "demo.h"
#include "appframework/ilaunchermgr.h"
#include "vgui_baseui_interface.h"
#include "LocalNetworkBackdoor.h"
#include "lightcache.h"
#include "vgui/ISystem.h"
#include "ivideomode.h"
#include "icolorcorrectiontools.h"
#include "toolframework/itoolframework.h"
#include "engine/view_sharedv1.h"
#include "view.h"
#include "game/client/iclientrendertargets.h"
#include "tier2/tier2.h"
#include "matchmaking.h"
#include "inputsystem/iinputsystem.h"
#include "iachievementmgr.h"
#include "profile.h"
#include "cl_steamauth.h"
#include "download.h"
#include "replay/iclientreplay.h"
#include "demofile.h"
#include "igame.h"
#include "iclientvirtualreality.h"
#include "sourcevr/isourcevirtualreality.h"
#include "cl_check_process.h"
#include "enginethreads.h"
#include "world.h"

#if defined( REPLAY_ENABLED )
#include "replay_internal.h"
#include "replay/replaylib.h"
#endif



// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
IMaterial* BrushModel_GetLightingAndMaterial( const Vector &start, 
	const Vector &end, Vector &diffuseLightColor, Vector &baseColor );
const char *Key_NameForBinding( const char *pBinding );
void CL_GetBackgroundLevelName( char *pszBackgroundName, int bufSize, bool bMapName );
CreateInterfaceFn g_ClientFactory = NULL;
extern	CGlobalVars g_ServerGlobalVariables;

//-----------------------------------------------------------------------------
// globals
//-----------------------------------------------------------------------------
CSysModule		*g_ClientDLLModule = NULL; // also used by materialproxyfactory.cpp
bool g_bClientGameDLLGreaterThanV13;

void AddIntersectingLeafSurfaces( mleaf_t *pLeaf, GetIntersectingSurfaces_Struct *pStruct )
{
	SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
	for ( int iSurf=0; iSurf < pLeaf->nummarksurfaces; iSurf++ )
	{
		SurfaceHandle_t surfID = pHandle[iSurf];
		ASSERT_SURF_VALID( surfID );
		
		if ( MSurf_Flags(surfID) & SURFDRAW_SKY )
			continue;

		// Make sure we haven't already processed this one.
		bool foundSurf = false;
		for(int iTest=0; iTest < pStruct->m_nSetInfos; iTest++)
		{
			if(pStruct->m_pInfos[iTest].m_pEngineData == (void *)surfID)
			{
				foundSurf = true;
				break;
			}
		}
		if ( foundSurf )
			continue;

		// Make sure there's output room.
		if(pStruct->m_nSetInfos >= pStruct->m_nMaxInfos)
			return;
		SurfInfo *pOut = &pStruct->m_pInfos[pStruct->m_nSetInfos];
		pOut->m_nVerts = 0;
		pOut->m_pEngineData = (void *)surfID;

		// Build vertex list and bounding box.			
		Vector vMin( 1000000.0f,  1000000.0f,  1000000.0f);
		Vector vMax(-1000000.0f, -1000000.0f, -1000000.0f);
		for(int iVert=0; iVert < MSurf_VertCount( surfID ); iVert++)
		{
			int vertIndex = pStruct->m_pModel->brush.pShared->vertindices[MSurf_FirstVertIndex( surfID ) + iVert];

			pOut->m_Verts[pOut->m_nVerts] = pStruct->m_pModel->brush.pShared->vertexes[vertIndex].position;
			vMin = vMin.Min(pOut->m_Verts[pOut->m_nVerts]);
			vMax = vMax.Max(pOut->m_Verts[pOut->m_nVerts]);

			++pOut->m_nVerts;
			if(pOut->m_nVerts >= MAX_SURFINFO_VERTS)
				break;
		}

		// See if the sphere intersects the box.
		int iDim=0;
		for(; iDim < 3; iDim++)
		{
			if(((*pStruct->m_pCenter)[iDim]+pStruct->m_Radius) < vMin[iDim] || 
				((*pStruct->m_pCenter)[iDim]-pStruct->m_Radius) > vMax[iDim])
			{
				break;
			}
		}
		
		if(iDim == 3)
		{
			// (Couldn't reject the sphere in the loop above).
			pOut->m_Plane = MSurf_GetForwardFacingPlane( surfID );
			++pStruct->m_nSetInfos;
		}
	}
}

void GetIntersectingSurfaces_R(
	GetIntersectingSurfaces_Struct *pStruct,
	mnode_t *pNode
	)
{
	if(pStruct->m_nSetInfos >= pStruct->m_nMaxInfos)
		return;

	// Ok, this is a leaf. Check its surfaces.
	if(pNode->contents >= 0)
	{
		mleaf_t *pLeaf = (mleaf_t*)pNode;

		if(pStruct->m_bOnlyVisible && pStruct->m_pCenterPVS)
		{
			if(pLeaf->cluster < 0)
				return;

			if(!(pStruct->m_pCenterPVS[pLeaf->cluster>>3] & (1 << (pLeaf->cluster&7))))
				return;
		}

		// First, add tris from displacements.
		for ( int i = 0; i < pLeaf->dispCount; i++ )
		{
			IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
			pDispInfo->GetIntersectingSurfaces( pStruct );
		}

		// Next, add brush tris.
		AddIntersectingLeafSurfaces( pLeaf, pStruct );
		return;
	}
	
	// Recurse.
	float dot;
	cplane_t *plane = pNode->plane;
	if ( plane->type < 3 )
	{
		dot = (*pStruct->m_pCenter)[plane->type] - plane->dist;
	}
	else
	{
		dot = pStruct->m_pCenter->Dot(plane->normal) - plane->dist;
	}

	// Recurse into child nodes.
	if(dot > -pStruct->m_Radius)
		GetIntersectingSurfaces_R(pStruct, pNode->children[SIDE_FRONT]);
	
	if(dot < pStruct->m_Radius)
		GetIntersectingSurfaces_R(pStruct, pNode->children[SIDE_BACK]);
}


//-----------------------------------------------------------------------------
// slow routine to draw a physics model
// NOTE: very slow code!!! just for debugging!
//-----------------------------------------------------------------------------
void DebugDrawPhysCollide( const CPhysCollide *pCollide, IMaterial *pMaterial, matrix3x4_t& transform, const color32 &color, bool drawAxes )
{
	if ( !pMaterial )
	{
		pMaterial = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER);
	}

	CMatRenderContextPtr pRenderContext( materials );

	Vector *outVerts;
	int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
	if ( vertCount )
	{
		IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );

		CMeshBuilder meshBuilder;
		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertCount/3 );

		for ( int j = 0; j < vertCount; j++ )
		{
			Vector out;
			VectorTransform( outVerts[j].Base(), transform, out.Base() );
			meshBuilder.Position3fv( out.Base() );
			meshBuilder.Color4ub( color.r, color.g, color.b, color.a );
			meshBuilder.TexCoord2f( 0, 0, 0 );
			meshBuilder.AdvanceVertex();
		}
		meshBuilder.End();
		pMesh->Draw();
	}
	physcollision->DestroyDebugMesh( vertCount, outVerts );

	// draw the axes
	if ( drawAxes )
	{
		Vector xaxis(10,0,0), yaxis(0,10,0), zaxis(0,0,10);
		Vector center;
		Vector out;

		MatrixGetColumn( transform, 3, center );
		IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
		CMeshBuilder meshBuilder;
		meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );

		// X
		meshBuilder.Position3fv( center.Base() );
		meshBuilder.Color4ub( 255, 0, 0, 255 );
		meshBuilder.TexCoord2f( 0, 0, 0 );
		meshBuilder.AdvanceVertex();
		VectorTransform( xaxis.Base(), transform, out.Base() );
		meshBuilder.Position3fv( out.Base() );
		meshBuilder.Color4ub( 255, 0, 0, 255 );
		meshBuilder.TexCoord2f( 0, 0, 0 );
		meshBuilder.AdvanceVertex();

		// Y
		meshBuilder.Position3fv( center.Base() );
		meshBuilder.Color4ub( 0, 255, 0, 255 );
		meshBuilder.TexCoord2f( 0, 0, 0 );
		meshBuilder.AdvanceVertex();
		VectorTransform( yaxis.Base(), transform, out.Base() );
		meshBuilder.Position3fv( out.Base() );
		meshBuilder.Color4ub( 0, 255, 0, 255 );
		meshBuilder.TexCoord2f( 0, 0, 0 );
		meshBuilder.AdvanceVertex();

		// Z
		meshBuilder.Position3fv( center.Base() );
		meshBuilder.Color4ub( 0, 0, 255, 255 );
		meshBuilder.TexCoord2f( 0, 0, 0 );
		meshBuilder.AdvanceVertex();
		VectorTransform( zaxis.Base(), transform, out.Base() );
		meshBuilder.Position3fv( out.Base() );
		meshBuilder.Color4ub( 0, 0, 255, 255 );
		meshBuilder.TexCoord2f( 0, 0, 0 );
		meshBuilder.AdvanceVertex();
		meshBuilder.End();
		pMesh->Draw();
	}
}

//-----------------------------------------------------------------------------
//
// implementation of IVEngineHud
//
//-----------------------------------------------------------------------------

// UNDONE: Move this to hud export code, subsume previous functions
class CEngineClient : public IVEngineClient
{
public:
	CEngineClient();

	int		GetIntersectingSurfaces(
		const model_t *model,
		const Vector &vCenter, 
		const float radius,
		const bool bOnlyVisible,
		SurfInfo *pInfos, 
		const int nMaxInfos);

	Vector	GetLightForPoint(const Vector &pos, bool bClamp);
	Vector	GetLightForPointFast(const Vector &pos, bool bClamp);
	const char *ParseFile( const char *data, char *token, int maxlen );
	virtual bool CopyLocalFile( const char *source, const char *destination );
	void GetScreenSize( int& w, int &h );
	void ServerCmd( const char *szCmdString, bool bReliable );
	void ClientCmd( const char *szCmdString );
	void ClientCmd_Unrestricted( const char *szCmdString );
	void SetRestrictServerCommands( bool bRestrict );
	void SetRestrictClientCommands( bool bRestrict );
	bool GetPlayerInfo( int ent_num, player_info_t *pinfo );
	client_textmessage_t *TextMessageGet( const char *pName );
	bool Con_IsVisible( void );
	int GetLocalPlayer( void );
	float GetLastTimeStamp( void );
	const model_t *LoadModel( const char *pName, bool bProp );
	void UnloadModel( const model_t *model, bool bProp );
	CSentence *GetSentence( CAudioSource *pAudioSource );
	float GetSentenceLength( CAudioSource *pAudioSource );
	bool IsStreaming( CAudioSource *pAudioSource ) const;
	void AddPhonemeFile( const char *pszPhonemeFile );

	// FIXME, move entirely to client .dll
	void GetViewAngles( QAngle& va );
	void SetViewAngles( QAngle& va );
	int GetMaxClients( void );
	void Key_Event( ButtonCode_t key, int down );
	const char *Key_LookupBinding( const char *pBinding );
	const char *Key_LookupBindingExact( const char *pBinding );
	const char *Key_BindingForKey( ButtonCode_t code );
	void StartKeyTrapMode( void );
	bool CheckDoneKeyTrapping( ButtonCode_t &key );
	bool IsInGame( void );
	bool IsConnected( void );
	bool IsDrawingLoadingImage( void );
	void Con_NPrintf( int pos, const char *fmt, ... );
	void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... );
	IMaterial *TraceLineMaterialAndLighting( const Vector &start, const Vector &end, 
		                                     Vector &diffuseLightColor, Vector &baseColor );
	int		IsBoxVisible( const Vector& mins, const Vector& maxs );
	int		IsBoxInViewCluster( const Vector& mins, const Vector& maxs );

	float Time();
	void Sound_ExtraUpdate( void );

	bool CullBox ( const Vector& mins, const Vector& maxs );
	const char *GetGameDirectory( void );
	const VMatrix& WorldToScreenMatrix();
	const VMatrix& WorldToViewMatrix();

	// Loads a game lump off disk
	int		GameLumpVersion( int lumpId ) const;
	int		GameLumpSize( int lumpId ) const;
	bool	LoadGameLump( int lumpId, void* pBuffer, int size );

	// Returns the number of leaves in the level
	int		LevelLeafCount() const;
	virtual ISpatialQuery* GetBSPTreeQuery();

	// Convert texlight to gamma...
	virtual void LinearToGamma( float* linear, float* gamma );

	// Get the lightstyle value
	virtual float LightStyleValue( int style );
	virtual void DrawPortals();

	// Computes light due to dynamic lighting at a point
	// If the normal isn't specified, then it'll return the maximum lighting
	virtual void ComputeDynamicLighting( Vector const& pt, Vector const* pNormal, Vector& color );

	// Computes light due to dynamic lighting at a point
	// If the normal isn't specified, then it'll return the maximum lighting
	// If pBoxColors is specified (it's an array of 6), then it'll copy the light contribution at each box side.
	virtual void ComputeLighting( const Vector& pt, const Vector* pNormal, bool bClamp, Vector& color, Vector *pBoxColors );

	// Returns the color of the ambient light
	virtual void GetAmbientLightColor( Vector& color );

	// Returns the dx support level
	virtual int	GetDXSupportLevel();
	
	virtual bool SupportsHDR();
	virtual void Mat_Stub( IMaterialSystem *pMatSys );

	// menu display
	virtual void GetChapterName( char *pchBuff, int iMaxLength );
	virtual char const *GetLevelName( void );
	virtual int GetLevelVersion( void );
	virtual bool IsLevelMainMenuBackground( void );
	virtual void GetMainMenuBackgroundName( char *dest, int destlen );

	// Occlusion system control
	virtual void SetOcclusionParameters( const OcclusionParams_t &params );

	//-----------------------------------------------------------------------------
	// Purpose: Takes a trackerID and returns which player slot that user is in
	//			returns 0 if no player found with that ID
	//-----------------------------------------------------------------------------
	virtual int	GetPlayerForUserID(int userID);
#if !defined( NO_VOICE )
	virtual struct IVoiceTweak_s *GetVoiceTweakAPI( void );
#endif
	virtual void EngineStats_BeginFrame( void );
	virtual void EngineStats_EndFrame( void );
	virtual void FireEvents();
	virtual void CheckPoint( const char *pName );
	virtual int GetLeavesArea( int *pLeaves, int nLeaves );
	virtual bool DoesBoxTouchAreaFrustum( const Vector &mins, const Vector &maxs, int iArea );

	// Sets the hearing origin
	virtual void SetAudioState( const AudioState_t &audioState );

	//-----------------------------------------------------------------------------
	//
	// Sentence API
	//
	//-----------------------------------------------------------------------------

	virtual int SentenceGroupPick( int groupIndex, char *name, int nameLen );
	virtual int SentenceGroupPickSequential( int groupIndex, char *name, int nameLen, int sentenceIndex, int reset );
	virtual int SentenceIndexFromName( const char *pSentenceName );
	virtual const char *SentenceNameFromIndex( int sentenceIndex );
	virtual int SentenceGroupIndexFromName( const char *pGroupName );
	virtual const char *SentenceGroupNameFromIndex( int groupIndex );
	virtual float SentenceLength( int sentenceIndex );
	virtual void DebugDrawPhysCollide( const CPhysCollide *pCollide, IMaterial *pMaterial, matrix3x4_t& transform, const color32 &color );

	// Activates/deactivates an occluder...
	virtual void ActivateOccluder( int nOccluderIndex, bool bActive );
	virtual bool IsOccluded( const Vector &vecAbsMins, const Vector &vecAbsMaxs );
	virtual void *SaveAllocMemory( size_t num, size_t size );
	virtual void SaveFreeMemory( void *pSaveMem );
	virtual INetChannelInfo *GetNetChannelInfo( void );
	virtual bool IsPlayingDemo( void );
	virtual bool IsRecordingDemo( void );
	virtual bool IsPlayingTimeDemo( void );
	virtual int  GetDemoRecordingTick( void );
	virtual int  GetDemoPlaybackTick( void );
	virtual int  GetDemoPlaybackStartTick( void );
	virtual float GetDemoPlaybackTimeScale( void );
	virtual int  GetDemoPlaybackTotalTicks( void );
	virtual bool IsPaused( void );
	virtual bool IsTakingScreenshot( void );
	virtual bool IsHLTV( void );
	virtual void GetVideoModes( int &nCount, vmode_s *&pModes );
	virtual void GetUILanguage( char *dest, int destlen );

	// Can skybox be seen from a particular point?
	virtual SkyboxVisibility_t IsSkyboxVisibleFromPoint( const Vector &vecPoint );

	virtual const char* GetMapEntitiesString();
	virtual bool		IsInEditMode( void );
	virtual bool		IsInCommentaryMode( void );
	virtual float		GetScreenAspectRatio();

	virtual unsigned int		GetEngineBuildNumber() { return PROTOCOL_VERSION; }
	virtual const char *		GetProductVersionString() { return GetSteamInfIDVersionInfo().szVersionString; }
	virtual void				GrabPreColorCorrectedFrame( int x, int y, int width, int height );
	virtual bool				IsHammerRunning( ) const;

	// Stuffs the cmd into the buffer & executes it immediately (vs ClientCmd() which executes it next frame)
	virtual void				ExecuteClientCmd( const char *szCmdString );

	virtual bool MapHasHDRLighting( void) ;
	virtual int GetAppID();

	virtual void				SetOverlayBindProxy( int iOverlayID, void *pBindProxy );

	virtual bool				CopyFrameBufferToMaterial( const char *pMaterialName );

	// Matchmaking
	void						ChangeTeam( const char *pTeamName );
	virtual void				ReadConfiguration( const bool readDefault = false );

	virtual void SetAchievementMgr( IAchievementMgr *pAchievementMgr );
	virtual IAchievementMgr *GetAchievementMgr();

	virtual bool				MapLoadFailed( void );
	virtual void				SetMapLoadFailed( bool bState );

	virtual bool				IsLowViolence();
	virtual const char			*GetMostRecentSaveGame( void );
	virtual void				SetMostRecentSaveGame( const char *lpszFilename );

	virtual void				StartXboxExitingProcess();
	virtual bool				IsSaveInProgress();
	
	virtual uint				OnStorageDeviceAttached( void );
	virtual void				OnStorageDeviceDetached( void );

	virtual void				ResetDemoInterpolation( void );

	virtual bool		REMOVED_SteamRefreshLogin( const char *password, bool isSecure ) { return false; }
	virtual bool		REMOVED_SteamProcessCall( bool & finished ) { return false; }

	virtual void SetGamestatsData( CGamestatsData *pGamestatsData );
	virtual CGamestatsData *GetGamestatsData();

#if defined( USE_SDL )
	virtual void GetMouseDelta( int &x, int &y, bool bIgnoreNextMouseDelta );
#endif

	virtual void ServerCmdKeyValues( KeyValues *pKeyValues );

	virtual bool IsSkippingPlayback( void );
	virtual bool IsLoadingDemo( void );

	virtual bool IsPlayingDemoALocallyRecordedDemo();

	virtual uint GetProtocolVersion( void );

	virtual bool IsWindowedMode( void );

	virtual void	FlashWindow();
	virtual int		GetClientVersion() const; // engines build
	virtual bool	IsActiveApp();
	virtual void	DisconnectInternal();

	virtual int		GetInstancesRunningCount( );

	virtual float	GetPausedExpireTime( void ) OVERRIDE;

	virtual bool	StartDemoRecording( const char *pszFilename, const char *pszFolder = NULL );
	virtual void	StopDemoRecording( void );
	virtual void	TakeScreenshot( const char *pszFilename, const char *pszFolder = NULL );

	virtual int 	GetServerTick( void );
	virtual void SolidMoved( IClientEntity *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks );
	virtual void TriggerMoved( IClientEntity *pTriggerEnt, bool accurateBboxTriggerChecks );
};


//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
static CEngineClient s_VEngineClient;
IVEngineClient *engineClient = &s_VEngineClient;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineClient, IVEngineClient, VENGINE_CLIENT_INTERFACE_VERSION, s_VEngineClient );
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineClient, IVEngineClient013, VENGINE_CLIENT_INTERFACE_VERSION_13, s_VEngineClient );


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CEngineClient::CEngineClient()
{
}

int	CEngineClient::GetIntersectingSurfaces(
	const model_t *model,
	const Vector &vCenter, 
	const float radius,
	const bool bOnlyVisible,
	SurfInfo *pInfos, 
	const int nMaxInfos)
{
	if ( !model )
		return 0;

	byte pvs[MAX_MAP_LEAFS/8];
	GetIntersectingSurfaces_Struct theStruct;
	theStruct.m_pModel = ( model_t * )model;
	theStruct.m_pCenter = &vCenter;
	theStruct.m_pCenterPVS = CM_Vis( pvs, sizeof(pvs), CM_LeafCluster( CM_PointLeafnum( vCenter ) ), DVIS_PVS );
	theStruct.m_Radius = radius;
	theStruct.m_bOnlyVisible = bOnlyVisible;
	theStruct.m_pInfos = pInfos;
	theStruct.m_nMaxInfos = nMaxInfos;
	theStruct.m_nSetInfos = 0;		

	// Go down the BSP.
	GetIntersectingSurfaces_R(
		&theStruct,
		&model->brush.pShared->nodes[ model->brush.firstnode ] );

	return theStruct.m_nSetInfos;
}

Vector	CEngineClient::GetLightForPoint(const Vector &pos, bool bClamp)
{
	Vector vRet;
	ComputeLighting( pos, NULL, bClamp, vRet, NULL );
	return vRet;
}

Vector CEngineClient::GetLightForPointFast(const Vector &pos, bool bClamp)
{
	Vector vRet;
	int leafIndex = CM_PointLeafnum(pos);
	vRet.Init();
	Vector cube[6];
	Mod_LeafAmbientColorAtPos( cube, pos, leafIndex );
	for ( int i = 0; i < 6; i++ )
	{
		vRet.x = fpmax(vRet.x, cube[i].x );
		vRet.y = fpmax(vRet.y, cube[i].y );
		vRet.z = fpmax(vRet.z, cube[i].z );
	}
	if ( bClamp )
	{
		if ( vRet.x > 1.0f )
			vRet.x = 1.0f;
		if ( vRet.y > 1.0f )
			vRet.y = 1.0f;
		if ( vRet.z > 1.0f )
			vRet.z = 1.0f;
	}
	return vRet;
}

const char *CEngineClient::ParseFile( const char *data, char *token, int maxlen )
{
	return ::COM_ParseFile( data, token, maxlen );
}

bool CEngineClient::CopyLocalFile( const char *source, const char *destination )
{
	return ::COM_CopyFile( source, destination );
}

void CEngineClient::GetScreenSize( int& w, int &h )
{
	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->GetWindowSize( w, h );
}

void CEngineClient::ServerCmd( const char *szCmdString, bool bReliable )
{
	// info handling
	char buf[255];
	Q_snprintf( buf, sizeof( buf ), "cmd %s", szCmdString );

	CCommand args;
	args.Tokenize( buf );
	Cmd_ForwardToServer( args, bReliable );
}

void CEngineClient::ClientCmd( const char *szCmdString )
{
	// Check that we can add the two execution markers
	if ( cl.m_bRestrictClientCommands && !Cbuf_HasRoomForExecutionMarkers( 2 ) )
	{
		AssertMsg( false, "CEngineClient::ClientCmd called but there is no room for the execution markers. Ignoring command." );
		return;
	}

	// Only let them run commands marked with FCVAR_CLIENTCMD_CAN_EXECUTE.
	if ( cl.m_bRestrictClientCommands )
		Cbuf_AddTextWithMarkers( eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE, szCmdString, eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE );
	else
		Cbuf_AddText( szCmdString );
}

void CEngineClient::ClientCmd_Unrestricted( const char *szCmdString )
{
	Cbuf_AddText( szCmdString );
}

void CEngineClient::SetRestrictServerCommands( bool bRestrict )
{
	cl.m_bRestrictServerCommands = bRestrict;
}

void CEngineClient::SetRestrictClientCommands( bool bRestrict )
{
	cl.m_bRestrictClientCommands = bRestrict;
}

void CEngineClient::ExecuteClientCmd( const char *szCmdString )
{
	Cbuf_AddText( szCmdString );
	Cbuf_Execute();
}

bool CEngineClient::GetPlayerInfo( int ent_num, player_info_t *pinfo )
{
	ent_num--; // player list if offset by 1 from ents

	if ( ent_num >= cl.m_nMaxClients || ent_num < 0 )
	{
		Q_memset( pinfo, 0, sizeof( player_info_t ) );
		return false;
	}

	Assert( cl.m_pUserInfoTable );
	if ( !cl.m_pUserInfoTable )
	{
		Q_memset( pinfo, 0, sizeof( player_info_t ) );
		return false;
	}

	Assert( ent_num < cl.m_pUserInfoTable->GetNumStrings() );
	if ( ent_num >= cl.m_pUserInfoTable->GetNumStrings() )
	{
		Q_memset( pinfo, 0, sizeof( player_info_t ) );
		return false;
	}

	int cubPlayerInfo;
	player_info_t *pi = (player_info_t*) cl.m_pUserInfoTable->GetStringUserData( ent_num, &cubPlayerInfo );

	if ( !pi || cubPlayerInfo < sizeof( player_info_t ) )
	{
		Q_memset( pinfo, 0, sizeof( player_info_t ) );
		return false;
	}

	Q_memcpy( pinfo, pi, sizeof( player_info_t ) );

	// Fixup from network order (little endian)
	CByteswap byteswap;
	byteswap.SetTargetBigEndian( false );
	byteswap.SwapFieldsToTargetEndian( pinfo );

	return true;
}

client_textmessage_t *CEngineClient::TextMessageGet( const char *pName )
{
	return ::TextMessageGet( pName );
}

bool CEngineClient::Con_IsVisible( void )
{
	return ::Con_IsVisible();
}

int CEngineClient::GetLocalPlayer( void )
{
	return cl.m_nPlayerSlot + 1;
}

float CEngineClient::GetLastTimeStamp( void )
{
	return cl.m_flLastServerTickTime;
}

bool CEngineClient::MapHasHDRLighting( void)
{
	return modelloader->LastLoadedMapHasHDRLighting();
}

const model_t *CEngineClient::LoadModel( const char *pName, bool bProp )
{
	return modelloader->GetModelForName( pName, bProp ? IModelLoader::FMODELLOADER_DETAILPROP : IModelLoader::FMODELLOADER_CLIENTDLL );
}

CSentence *CEngineClient::GetSentence( CAudioSource *pAudioSource )
{
	if (pAudioSource)
	{
		return pAudioSource->GetSentence();
	}
	return NULL;
}

void AddPhonemesFromFile( const char *pszFileName );

void CEngineClient::AddPhonemeFile( const char *pszPhonemeFile )
{
	AddPhonemesFromFile( pszPhonemeFile );
}

float CEngineClient::GetSentenceLength( CAudioSource *pAudioSource )
{
	if (pAudioSource && pAudioSource->SampleRate() > 0 )
	{
		float length = (float)pAudioSource->SampleCount() / (float)pAudioSource->SampleRate();
		return length;
	}
	return 0.0f;
}

bool CEngineClient::IsStreaming( CAudioSource *pAudioSource ) const
{
	if ( pAudioSource )
	{
		return pAudioSource->IsStreaming();
	}
	return false;
}

// FIXME, move entirely to client .dll
void CEngineClient::GetViewAngles( QAngle& va )
{
	VectorCopy( cl.viewangles, va );
}

void CEngineClient::SetViewAngles( QAngle& va )
{
	cl.viewangles.x = AngleNormalize( va.x );
	cl.viewangles.y = AngleNormalize( va.y );
	cl.viewangles.z = AngleNormalize( va.z );
	Assert( !IS_NAN(cl.viewangles.x ) );
	Assert( !IS_NAN(cl.viewangles.y ) );
	Assert( !IS_NAN(cl.viewangles.z ) );
}

int CEngineClient::GetMaxClients( void )
{
	return cl.m_nMaxClients;
}

void CEngineClient::SetMapLoadFailed( bool bState )
{
	g_ServerGlobalVariables.bMapLoadFailed = bState;
}

bool CEngineClient::MapLoadFailed( void )
{
	return g_ServerGlobalVariables.bMapLoadFailed;
}

void CEngineClient::ReadConfiguration( const bool readDefault /*= false*/ )
{
	Host_ReadConfiguration();
}

const char *CEngineClient::Key_LookupBinding( const char *pBinding )
{
	return ::Key_NameForBinding( pBinding );
}

const char *CEngineClient::Key_LookupBindingExact( const char *pBinding )
{
	return ::Key_NameForBindingExact( pBinding );
}

const char *CEngineClient::Key_BindingForKey( ButtonCode_t code )
{
	return ::Key_BindingForKey( code );
}

void CEngineClient::StartKeyTrapMode( void )
{
	Key_StartTrapMode();
}

bool CEngineClient::CheckDoneKeyTrapping( ButtonCode_t &code )
{
	return Key_CheckDoneTrapping( code );
}

bool CEngineClient::IsInGame( void )
{
	return cl.IsActive();
}

bool CEngineClient::IsConnected( void )
{
	return cl.IsConnected();
}

bool CEngineClient::IsDrawingLoadingImage( void )
{
	return scr_drawloading;
}

void CEngineClient::Con_NPrintf( int pos, const char *fmt, ... )
{
	va_list		argptr;
	char		text[4096];
	va_start (argptr, fmt);
	Q_vsnprintf(text, sizeof( text ), fmt, argptr);
	va_end (argptr);

	::Con_NPrintf( pos, "%s", text );
}

void CEngineClient::Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... )
{
	va_list		argptr;
	char		text[4096];
	va_start (argptr, fmt);
	Q_vsnprintf(text, sizeof( text ), fmt, argptr);
	va_end (argptr);

	::Con_NXPrintf( info, "%s", text );
}

IMaterial *CEngineClient::TraceLineMaterialAndLighting( const Vector &start, const Vector &end, 
		                                 Vector &diffuseLightColor, Vector &baseColor )
{
	return BrushModel_GetLightingAndMaterial( start, end, diffuseLightColor, baseColor );
}

int	CEngineClient::IsBoxVisible( const Vector& mins, const Vector& maxs ) 
{
	return CM_BoxVisible( mins, maxs, Map_VisCurrent(), CM_ClusterPVSSize() );
}

int	CEngineClient::IsBoxInViewCluster( const Vector& mins, const Vector& maxs )
{
	// See comments in Map_VisCurrentCluster for why we might get a negative number.
	int curCluster = Map_VisCurrentCluster();
	if ( curCluster < 0 )
		return false;

	byte pvs[MAX_MAP_LEAFS/8];
	const byte *ppvs = CM_Vis( pvs, sizeof(pvs), curCluster, DVIS_PVS );
	return CM_BoxVisible(mins, maxs, ppvs, sizeof(pvs) );
}

float CEngineClient::Time()
{
	return Sys_FloatTime();
}

void CEngineClient::Sound_ExtraUpdate( void )
{
	// On xbox this is not necessary except for long pauses, so unhook this one
	if ( IsX360() )
		return;

	VPROF_BUDGET( "CEngineClient::Sound_ExtraUpdate()", VPROF_BUDGETGROUP_OTHER_SOUND );

	S_ExtraUpdate();
}

bool CEngineClient::CullBox ( const Vector& mins, const Vector& maxs )
{
	return R_CullBoxSkipNear( mins, maxs, g_Frustum );
}

const char *CEngineClient::GetGameDirectory( void )
{
	return com_gamedir;
}

const VMatrix& CEngineClient::WorldToScreenMatrix()
{
	// FIXME: this is only valid if we're currently rendering.  If not, it should use the player, or it really should pass one in.
	return g_EngineRenderer->WorldToScreenMatrix();
}

const VMatrix& CEngineClient::WorldToViewMatrix()
{
	// FIXME: this is only valid if we're currently rendering.  If not, it should use the player, or it really should pass one in.
	return g_EngineRenderer->ViewMatrix();
}

// Loads a game lump off disk
int	CEngineClient::GameLumpVersion( int lumpId ) const
{
	return Mod_GameLumpVersion( lumpId ); 
}

int	CEngineClient::GameLumpSize( int lumpId ) const 
{ 
	return Mod_GameLumpSize( lumpId ); 
}

bool CEngineClient::LoadGameLump( int lumpId, void* pBuffer, int size ) 
{ 
	return Mod_LoadGameLump( lumpId, pBuffer, size ); 
}

// Returns the number of leaves in the level
int	CEngineClient::LevelLeafCount() const
{
	return host_state.worldbrush->numleafs;
}

ISpatialQuery* CEngineClient::GetBSPTreeQuery()
{
	return g_pToolBSPTree;
}

// Convert texlight to gamma...
void CEngineClient::LinearToGamma( float* linear, float* gamma )
{
	gamma[0] = LinearToTexture( linear[0] ) / 255.0f;
	gamma[1] = LinearToTexture( linear[1] ) / 255.0f;
	gamma[2] = LinearToTexture( linear[2] ) / 255.0f;
}

// Get the lightstyle value
float CEngineClient::LightStyleValue( int style )
{
	return ::LightStyleValue( style );
}


void CEngineClient::DrawPortals()
{
	R_DrawPortals();
}

// Computes light due to dynamic lighting at a point
// If the normal isn't specified, then it'll return the maximum lighting
void CEngineClient::ComputeDynamicLighting( Vector const& pt, Vector const* pNormal, Vector& color )
{
	::ComputeDynamicLighting( pt, pNormal, color );
}

// Computes light due to dynamic lighting at a point
// If the normal isn't specified, then it'll return the maximum lighting
void CEngineClient::ComputeLighting( const Vector& pt, const Vector* pNormal, bool bClamp, Vector& color, Vector *pBoxColors )
{
	::ComputeLighting( pt, pNormal, bClamp, color, pBoxColors );
}

// Returns the color of the ambient light
void CEngineClient::GetAmbientLightColor( Vector& color )
{
	dworldlight_t* pWorldLight = FindAmbientLight();
	if (!pWorldLight)
		color.Init( 0, 0, 0 );
	else
		VectorCopy( pWorldLight->intensity, color );
}

// Returns the dx support level
int	CEngineClient::GetDXSupportLevel()
{
	return g_pMaterialSystemHardwareConfig->GetDXSupportLevel();
}

bool CEngineClient::SupportsHDR()
{
	// deprecated.
//	Assert( 0 );
	return false;
}

void CEngineClient::Mat_Stub( IMaterialSystem *pMatSys )
{
	materials = pMatSys;
	
	// Pass the call to the model renderer.
	if ( g_pStudioRender )
		g_pStudioRender->Mat_Stub( pMatSys );
}

void CEngineClient::GetChapterName( char *pchBuff, int iMaxLength )
{
	serverGameDLL->GetSaveComment( pchBuff, iMaxLength, 0.0f, 0.0f, true );
}

char const *CEngineClient::GetLevelName( void )
{
	if ( sv.IsDedicated() )
	{
		return "Dedicated Server";
	}
	else if ( !cl.IsConnected() )
	{
		return "";
	}

	return cl.m_szLevelFileName;
}

int CEngineClient::GetLevelVersion( void )
{
	return g_ServerGlobalVariables.mapversion;
}

bool CEngineClient::IsLevelMainMenuBackground( void )
{
	return sv.IsLevelMainMenuBackground();
}

void CEngineClient::GetMainMenuBackgroundName( char *dest, int destlen )
{
	CL_GetBackgroundLevelName( dest, destlen, false );
}

// Occlusion system control
void CEngineClient::SetOcclusionParameters( const OcclusionParams_t &params )
{
	OcclusionSystem()->SetOcclusionParameters( params.m_flMaxOccludeeArea, params.m_flMinOccluderArea );
}

//-----------------------------------------------------------------------------
// Purpose: Takes a trackerID and returns which player slot that user is in
//			returns 0 if no player found with that ID
//-----------------------------------------------------------------------------
int	CEngineClient::GetPlayerForUserID(int userID)
{
	if ( !cl.m_pUserInfoTable )
		return 0;

	// We are occasionally getting crashes here where it looks like cl.m_nMaxClients
	//  is larger than the number of items in cl.m_pUserInfoTable. Callstack:
	//   engine.dll	CEngineClient::GetPlayerForUserID
	//   client.dll	CTFHudDeathNotice::OnGameEvent
	//   client.dll	CHudBaseDeathNotice::FireGameEvent
	//   client.dll	CTFHudDeathNotice::FireGameEvent
	//   engine.dll	CGameEventManager::FireEventIntern
	//   engine.dll	CGameEventManager::FireEventClientSide
	//   engine.dll	CClientState::ProcessGameEvent
	// So add check to make sure we don't go over m_pUserInfoTable size.
	int nMaxClients = Min( cl.m_nMaxClients, cl.m_pUserInfoTable->GetNumStrings() );
	for ( int i = 0; i < nMaxClients; i++ )
	{
		player_info_t *pi = (player_info_t*) cl.m_pUserInfoTable->GetStringUserData( i, NULL );

		if ( !pi )
			continue;

		// Fixup from network order (little endian)
		if ( LittleLong( pi->userID ) == userID )
		{
			// return as entity index
			return (i+1);
		}
	}

	return 0;
}

#if !defined( NO_VOICE )
struct IVoiceTweak_s *CEngineClient::GetVoiceTweakAPI( void )
{
	return &g_VoiceTweakAPI;
}
#endif

void CEngineClient::EngineStats_BeginFrame( void )
{
	g_EngineStats.BeginFrame();
}

void CEngineClient::EngineStats_EndFrame( void )
{
	g_EngineStats.EndFrame();
}

void CEngineClient::FireEvents()
{
	// Run any events queued up for this frame
	CL_FireEvents();
}

void CEngineClient::CheckPoint( const char *pName )
{
	GetTestScriptMgr()->CheckPoint( pName );
}

int CEngineClient::GetLeavesArea( int *pLeaves, int nLeaves )
{
	if ( nLeaves == 0 )
		return -1;

	int iArea = host_state.worldbrush->leafs[pLeaves[0]].area;
	for ( int i=1; i < nLeaves; i++ )
	{
		int iTestArea = host_state.worldbrush->leafs[pLeaves[i]].area;
		if ( iTestArea != iArea )
			return -1;
	}

	return iArea;
}

bool CEngineClient::DoesBoxTouchAreaFrustum( const Vector &mins, const Vector &maxs, int iArea )
{
	const Frustum_t *pFrustum = GetAreaFrustum( iArea );
	return !R_CullBoxSkipNear( mins, maxs, *pFrustum );
}

//-----------------------------------------------------------------------------
// Sets the hearing origin
//-----------------------------------------------------------------------------
void CEngineClient::SetAudioState( const AudioState_t &audioState )
{
	Host_SetAudioState( audioState );
}


//-----------------------------------------------------------------------------
//
// Sentence API
//
//-----------------------------------------------------------------------------

int CEngineClient::SentenceGroupPick( int groupIndex, char *name, int nameLen )
{
	return VOX_GroupPick( groupIndex, name, nameLen );
}


int CEngineClient::SentenceGroupPickSequential( int groupIndex, char *name, int nameLen, int sentenceIndex, int reset )
{
	return VOX_GroupPickSequential( groupIndex, name, nameLen, sentenceIndex, reset );
}

int CEngineClient::SentenceIndexFromName( const char *pSentenceName )
{
	int sentenceIndex = -1;
	
	VOX_LookupString( pSentenceName, &sentenceIndex );
	
	return sentenceIndex;
}

const char *CEngineClient::SentenceNameFromIndex( int sentenceIndex )
{
	return VOX_SentenceNameFromIndex( sentenceIndex );
}


int CEngineClient::SentenceGroupIndexFromName( const char *pGroupName )
{
	return VOX_GroupIndexFromName( pGroupName );
}

const char *CEngineClient::SentenceGroupNameFromIndex( int groupIndex )
{
	return VOX_GroupNameFromIndex( groupIndex );
}


float CEngineClient::SentenceLength( int sentenceIndex )
{
	return VOX_SentenceLength( sentenceIndex );
}

void CEngineClient::DebugDrawPhysCollide( const CPhysCollide *pCollide, IMaterial *pMaterial, matrix3x4_t& transform, const color32 &color )
{
	::DebugDrawPhysCollide( pCollide, pMaterial, transform, color, false );
}

// Activates/deactivates an occluder...
void CEngineClient::ActivateOccluder( int nOccluderIndex, bool bActive )
{
	OcclusionSystem()->ActivateOccluder( nOccluderIndex, bActive );
}

bool CEngineClient::IsOccluded( const Vector &vecAbsMins, const Vector &vecAbsMaxs )
{
	return OcclusionSystem()->IsOccluded( vecAbsMins, vecAbsMaxs );
}

void *CEngineClient::SaveAllocMemory( size_t num, size_t size )
{
	return ::SaveAllocMemory( num, size );
}

void CEngineClient::SaveFreeMemory( void *pSaveMem )
{
	::SaveFreeMemory( pSaveMem );
}

INetChannelInfo *CEngineClient::GetNetChannelInfo( void )
{
	return (INetChannelInfo*)cl.m_NetChannel;
}

bool CEngineClient::IsPlayingDemo( void )
{
	return demoplayer->IsPlayingBack();
}

bool CEngineClient::IsRecordingDemo( void )
{
	return demorecorder->IsRecording();
}

bool CEngineClient::IsPlayingTimeDemo( void )
{
	return demoplayer->IsPlayingTimeDemo();
}

bool CEngineClient::IsPaused( void )
{
	return cl.IsPaused();
}

bool CEngineClient::IsTakingScreenshot( void )
{
	return cl_takesnapshot;
}

int CEngineClient::GetDemoRecordingTick( void )	
{
	return demorecorder->GetRecordingTick();
}

int CEngineClient::GetDemoPlaybackTick( void )
{
	return demoplayer->GetPlaybackTick();
}

int CEngineClient::GetDemoPlaybackStartTick( void )
{
	return demoplayer->GetPlaybackStartTick();
}

float CEngineClient::GetDemoPlaybackTimeScale( void )
{
	return demoplayer->GetPlaybackTimeScale();
}

int CEngineClient::GetDemoPlaybackTotalTicks( void )
{
	return demoplayer->GetTotalTicks();
}

bool CEngineClient::IsHLTV( void )
{
	return cl.ishltv;
}

void CEngineClient::GetVideoModes( int &nCount, vmode_s *&pModes )
{
	nCount = videomode->GetModeCount();
	pModes = videomode->GetMode( 0 );
}

void CEngineClient::GetUILanguage( char *dest, int destlen )
{
	const char *pStr = cl_language.GetString();
	if ( pStr )
	{
		V_strncpy( dest, pStr, destlen );
	}
	else if ( IsX360() )
	{
		dest[0] = 0;
	}
}

//-----------------------------------------------------------------------------
// Can skybox be seen from a particular point?
//-----------------------------------------------------------------------------
SkyboxVisibility_t CEngineClient::IsSkyboxVisibleFromPoint( const Vector &vecPoint )
{
	// In the mat_fullbright 1 case, it's always visible 
	// (we may have no lighting in the level, and vrad is where LEAF_FLAGS_SKY is computed)
	if ( g_pMaterialSystemConfig->nFullbright == 1 )
		return SKYBOX_3DSKYBOX_VISIBLE;

	int nLeaf = CM_PointLeafnum( vecPoint );
	int nFlags = GetCollisionBSPData()->map_leafs[nLeaf].flags;
	if ( nFlags & LEAF_FLAGS_SKY )
		return SKYBOX_3DSKYBOX_VISIBLE;
	return ( nFlags & LEAF_FLAGS_SKY2D ) ? SKYBOX_2DSKYBOX_VISIBLE : SKYBOX_NOT_VISIBLE;
}

const char* CEngineClient::GetMapEntitiesString()
{
	return CM_EntityString();
}

bool CEngineClient::IsInEditMode( void )
{
	return g_bInEditMode;
}

bool CEngineClient::IsInCommentaryMode( void )
{
	return g_bInCommentaryMode;
}

float CEngineClient::GetScreenAspectRatio()
{
	return GetScreenAspect( );
}

int CEngineClient::GetAppID()
{
	return GetSteamAppID();
}

void CEngineClient::SetOverlayBindProxy( int iOverlayID, void *pBindProxy )
{
	OverlayMgr()->SetOverlayBindProxy( iOverlayID, pBindProxy );
}

void CEngineClient::ChangeTeam( const char *pTeamName )
{
	g_pMatchmaking->ChangeTeam( pTeamName );
}

//-----------------------------------------------------------------------------
// Returns true if copy occured
//-----------------------------------------------------------------------------
bool CEngineClient::CopyFrameBufferToMaterial( const char *pMaterialName )
{
	if ( !IsX360() )
	{
		// not for PC
		Assert( 0 );
		return false;
	}

	IMaterial *pMaterial = materials->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER );
	if ( pMaterial->IsErrorMaterial() )
	{
		// unknown material
		return false;
	}

	bool bFound;
	IMaterialVar *pMaterialVar = pMaterial->FindVar( "$baseTexture", &bFound, false );
	if ( !bFound || pMaterialVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
	{
		// lack of expected $basetexture
		return false;
	}

	ITexture *pTexture = pMaterialVar->GetTextureValue();
	if ( !pTexture || !pTexture->IsRenderTarget() )
	{
		// base texture is not a render target
		return false;
	}

	CMatRenderContextPtr pRenderContext( materials );

	int width, height;
	pRenderContext->GetRenderTargetDimensions( width, height );
	if ( width != pTexture->GetActualWidth() || height != pTexture->GetActualHeight() )
	{
		// better be matched, not supporting a disparate blit in this context
		// disparate blit may very well use same RT we are trying to copy into
		return false;
	}

	pRenderContext->CopyRenderTargetToTexture( pTexture );
	return true;
}


//-----------------------------------------------------------------------------
// Used by the color correction UI
//-----------------------------------------------------------------------------
void CEngineClient::GrabPreColorCorrectedFrame( int x, int y, int width, int height )
{
	colorcorrectiontools->GrabPreColorCorrectedFrame( x, y, width, height );
}

//-----------------------------------------------------------------------------
// Is hammer running?
//-----------------------------------------------------------------------------
bool CEngineClient::IsHammerRunning( ) const
{
	return IsPC() ? InEditMode() : false;
}

extern IAchievementMgr *g_pAchievementMgr;

//-----------------------------------------------------------------------------
// Sets achievement mgr
//-----------------------------------------------------------------------------
void CEngineClient::SetAchievementMgr( IAchievementMgr *pAchievementMgr )
{
	g_pAchievementMgr = pAchievementMgr;
}

//-----------------------------------------------------------------------------
// Gets achievement mgr
//-----------------------------------------------------------------------------
IAchievementMgr *CEngineClient::GetAchievementMgr() 
{
	return g_pAchievementMgr;
}


//-----------------------------------------------------------------------------
// Called by the client to determine violence settings for things like ragdoll
// fading.
//-----------------------------------------------------------------------------
bool CEngineClient::IsLowViolence()
{
	return g_bLowViolence;
}

const char *CEngineClient::GetMostRecentSaveGame( void )
{
	return saverestore->GetMostRecentlyLoadedFileName();
}

void CEngineClient::SetMostRecentSaveGame( const char *lpszFilename )
{
	saverestore->SetMostRecentSaveGame( lpszFilename );
}

//-----------------------------------------------------------------------------
// Called by gameui to hint the engine that an exiting process has started.
// The Engine needs to stabilize to a safe quiet state. More frames are going
// to and have to run, but the true exit will occur.
//-----------------------------------------------------------------------------
void CEngineClient::StartXboxExitingProcess()
{
	if ( IsPC() )
	{
		// not for PC
		return;
	}

	g_pInputSystem->StopRumble();

	// save out the achievements
	g_pAchievementMgr->SaveGlobalStateIfDirty( false );

	S_StopAllSounds( true );

	// Shutdown QMS, need to go back to single threaded
	Host_AllowQueuedMaterialSystem( false );
}

bool CEngineClient::IsSaveInProgress()
{
	return saverestore->IsSaveInProgress();
}

extern IXboxSystem *g_pXboxSystem;

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
uint CEngineClient::OnStorageDeviceAttached( void )
{
	return g_pXboxSystem->OpenContainers();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEngineClient::OnStorageDeviceDetached( void )
{
	XBX_SetStorageDeviceId( XBX_INVALID_STORAGE_ID );
	g_pXboxSystem->CloseContainers();
}

void CEngineClient::ResetDemoInterpolation( void )
{
	if( demorecorder->IsRecording() )
		demorecorder->ResetDemoInterpolation();
	if (demoplayer->IsPlayingBack() )
		demoplayer->ResetDemoInterpolation();
}


extern CGamestatsData *g_pGamestatsData;
void CEngineClient::SetGamestatsData( CGamestatsData *pGamestatsData )
{
	g_pGamestatsData = pGamestatsData;
}

CGamestatsData *CEngineClient::GetGamestatsData()
{
	return g_pGamestatsData;
}

//-----------------------------------------------------------------------------

#if defined( USE_SDL )

void CEngineClient::GetMouseDelta( int &x, int &y, bool bIgnoreNextMouseDelta )
{
	g_pLauncherMgr->GetMouseDelta( x, y, bIgnoreNextMouseDelta );
}

#endif


void CEngineClient::ServerCmdKeyValues( KeyValues *pKeyValues )
{
	cl.SendServerCmdKeyValues( pKeyValues );
}

bool CEngineClient::IsSkippingPlayback( void )
{
	return demoplayer->IsSkipping();
}

bool CEngineClient::IsLoadingDemo( void )
{
	return demoplayer->IsLoading();
}

bool CEngineClient::IsPlayingDemoALocallyRecordedDemo()
{
	return IsPlayingDemo() &&
		   demoplayer &&
		   demoplayer->GetDemoFile() &&
		   !V_strnicmp( demoplayer->GetDemoFile()->m_DemoHeader.servername, "localhost", 9 );
}

uint CEngineClient::GetProtocolVersion()
{
	if ( demoplayer && demoplayer->IsPlayingBack() )
	{
		return demoplayer->GetProtocolVersion();
	}
	return PROTOCOL_VERSION;
}

bool CEngineClient::IsWindowedMode()
{
	return videomode->IsWindowedMode();
}

int	CEngineClient::GetClientVersion() const
{
	return GetSteamInfIDVersionInfo().ClientVersion;
}

bool CEngineClient::IsActiveApp( void )
{
	return game->IsActiveApp();
}

//-----------------------------------------------------------------------------
// Previously ConCommand disconnect
//-----------------------------------------------------------------------------
void CEngineClient::DisconnectInternal( void )
{
	Disconnect();
}

//-----------------------------------------------------------------------------
// The client DLL serves out this interface
//-----------------------------------------------------------------------------
IBaseClientDLL *g_ClientDLL = NULL;
IClientVirtualReality *g_pClientVR = NULL;
IPrediction	*g_pClientSidePrediction = NULL;
IClientRenderTargets *g_pClientRenderTargets = NULL;
IClientEntityList *entitylist = NULL;
ICenterPrint *centerprint = NULL;
IClientLeafSystemEngine *clientleafsystem = NULL;
bool g_bClientLeafSystemV1;
ClientClass *g_pClientClassHead = NULL;
IClientReplay *g_pClientReplay = NULL;

ClientClass *ClientDLL_GetAllClasses( void )
{
	if ( g_ClientDLL )
		return g_ClientDLL->GetAllClasses();
	else
		return g_pClientClassHead;
}

static void ClientDLL_InitRecvTableMgr()
{
	// Register all the receive tables.
	RecvTable *pRecvTables[MAX_DATATABLES];
	int nRecvTables = 0;
	for ( ClientClass *pCur = ClientDLL_GetAllClasses(); pCur; pCur=pCur->m_pNext )
	{
		ErrorIfNot( 
			nRecvTables < ARRAYSIZE( pRecvTables ), 
			("ClientDLL_InitRecvTableMgr: overflowed MAX_DATATABLES")
			);
		
		pRecvTables[nRecvTables] = pCur->m_pRecvTable;
		++nRecvTables;
	}

	RecvTable_Init( pRecvTables, nRecvTables );
}


static void ClientDLL_ShutdownRecvTableMgr()
{
	RecvTable_Term();
}

CreateInterfaceFn ClientDLL_GetFactory( void )
{
	return g_ClientFactory;
}

//-----------------------------------------------------------------------------
// Purpose: Loads the client DLL
// Input  :  - 
//-----------------------------------------------------------------------------
bool ClientDLL_Load()
{
	Assert ( !g_ClientDLLModule );

	// Check the signature on the client dll.  If this fails we load it anyway but put this client
	// into insecure mode so it won't connect to secure servers and get VAC banned
	if ( !Host_AllowLoadModule( "client.dll", "GAMEBIN", true ) )
	{
		// not supposed to load this but we will anyway
		Host_DisallowSecureServers();
		Host_AllowLoadModule( "client.dll","GAMEBIN", true );
	}

	g_ClientDLLModule = g_pFileSystem->LoadModule( "client", "GAMEBIN", false );
	if ( g_ClientDLLModule )
	{
		g_ClientFactory = Sys_GetFactory( g_ClientDLLModule );
		if ( g_ClientFactory )
		{
			g_ClientDLL = (IBaseClientDLL *)g_ClientFactory( CLIENT_DLL_INTERFACE_VERSION, NULL );
			// this is to ensure the old format of the string table is used for clients version 13 and older.
			// when the client version gets revved, there will need to be an else that sets this bool to true
			g_bClientGameDLLGreaterThanV13 = false;
			if ( !g_ClientDLL )
			{
				Sys_Error( "Could not get client.dll interface from library client" );
			}

			if( g_pSourceVR )
			{
				g_pClientVR = (IClientVirtualReality *)g_ClientFactory( CLIENTVIRTUALREALITY_INTERFACE_VERSION, NULL );
				if( !g_pClientVR )
				{
					Msg( "client.dll is not VR-compatible.\n" );
				}
			}
		}
		else
		{
			Sys_Error( "Could not find factory interface in library client" );
		}
	}
	else
	{	
		// tell Steam to do a quick verify of the install. Sys_Error doesn't return, so do this first.
		if( Steam3Client().SteamApps() )
			Steam3Client().SteamApps()->MarkContentCorrupt( true );

		// library failed to load
		Sys_Error( "Could not load library client. Try restarting. If that doesn't work, verify the cache." );
	}

	// Load the client render targets interface from the client .dll
	// NOTE: Its OK if this returns NULL, as some mods won't provide the interface and will just use the default behavior of the engine
	g_pClientRenderTargets = (IClientRenderTargets *)g_ClientFactory( CLIENTRENDERTARGETS_INTERFACE_VERSION, NULL );

	return true;
}

void InitExtraClientCmdCanExecuteVars()
{	
	// This can go away when we ship a client DLL with the FCVAR_CLIENTCMD_CAN_EXECUTE flag set on these cvars/concommands.
	Cmd_AddClientCmdCanExecuteVar( "cancelselect" );
	Cmd_AddClientCmdCanExecuteVar( "menuselect" );
	Cmd_AddClientCmdCanExecuteVar( "playgamesound" );
	Cmd_AddClientCmdCanExecuteVar( "_cl_classmenuopen" );
	Cmd_AddClientCmdCanExecuteVar( "cl_buy_favorite" );
	Cmd_AddClientCmdCanExecuteVar( "voice_modenable" );
	Cmd_AddClientCmdCanExecuteVar( "togglescores" );

	Cmd_AddClientCmdCanExecuteVar( "spec_next" );
	Cmd_AddClientCmdCanExecuteVar( "spec_prev" );
	Cmd_AddClientCmdCanExecuteVar( "spec_mode" );
	Cmd_AddClientCmdCanExecuteVar( "spec_menu" );
	Cmd_AddClientCmdCanExecuteVar( "spec_autodirector" );
	Cmd_AddClientCmdCanExecuteVar( "overview_zoom" );
	Cmd_AddClientCmdCanExecuteVar( "overview_mode" );
	Cmd_AddClientCmdCanExecuteVar( "overview_health" );
	Cmd_AddClientCmdCanExecuteVar( "overview_names" );
	Cmd_AddClientCmdCanExecuteVar( "overview_tracks" );
	Cmd_AddClientCmdCanExecuteVar( "overview_locked" );
	Cmd_AddClientCmdCanExecuteVar( "overview_alpha" );

	Cmd_AddClientCmdCanExecuteVar( "playgamesound" );
}

//-----------------------------------------------------------------------------
// Purpose: Inits the client .dll
//-----------------------------------------------------------------------------
void ClientDLL_Init( void )
{
	extern void CL_SetSteamCrashComment();

	// Assert ClientDLL_Load successfully created these interfaces, as we need them to init properly
	Assert ( g_ClientDLL );
	Assert ( g_ClientFactory );

	// this will get updated after we load a map, but this gets video info if we sys_error() prior to loading a map
	CL_SetSteamCrashComment();

	if ( g_ClientDLL )
	{
		COM_TimestampedLog( "g_ClientDLL->Init" );

		if ( !g_ClientDLL->Init(g_AppSystemFactory, g_AppSystemFactory, &g_ClientGlobalVariables ) )
		{
			Sys_Error("Client.dll Init() in library client failed.");
		}

		if ( g_ClientFactory )
		{
			COM_TimestampedLog( "g_pClientSidePrediction->Init" );

			// Load the prediction interface from the client .dll
			g_pClientSidePrediction = (IPrediction *)g_ClientFactory( VCLIENT_PREDICTION_INTERFACE_VERSION, NULL );
			if ( !g_pClientSidePrediction )
			{
				Sys_Error( "Could not get IPrediction interface from library client" );
			}
			g_pClientSidePrediction->Init();

			entitylist = ( IClientEntityList  *)g_ClientFactory( VCLIENTENTITYLIST_INTERFACE_VERSION, NULL );
			if ( !entitylist )
			{
				Sys_Error( "Could not get client entity list interface from library client" );
			}

			centerprint = ( ICenterPrint * )g_ClientFactory( VCENTERPRINT_INTERFACE_VERSION, NULL );
			if ( !centerprint )
			{
				Sys_Error( "Could not get centerprint interface from library client" );
			}

			clientleafsystem = ( IClientLeafSystemEngine *)g_ClientFactory( CLIENTLEAFSYSTEM_INTERFACE_VERSION, NULL );
			if ( clientleafsystem )
			{
				g_bClientLeafSystemV1 = false;
			}
			else if ( !clientleafsystem )
			{
				clientleafsystem = ( IClientLeafSystemEngine *)g_ClientFactory( CLIENTLEAFSYSTEM_INTERFACE_VERSION_1, NULL );
				if ( !clientleafsystem )
				{
					Sys_Error( "Could not get client leaf system interface from library client" );
				}
				else
				{
					g_bClientLeafSystemV1 = true;
				}
			}

#if defined( REPLAY_ENABLED )
			if ( Replay_IsSupportedModAndPlatform() )
			{
				// Replay dll should be loaded by this point
				if ( !g_pReplay )
				{
					Sys_Error( "Replay.dll was not loaded" );
				}

				// Get pointer to client-side replay interface implementation
				g_pClientReplay = (IClientReplay *)g_ClientFactory( CLIENT_REPLAY_INTERFACE_VERSION, NULL );
				if ( !g_pClientReplay )
				{
					Sys_Error( "Could not get the replay interface from library client" );
				}

				// Allow the client dll to setup pointers to replay interfaces
				extern CreateInterfaceFn g_fnReplayFactory;
				if ( !g_ClientDLL->ReplayInit( g_fnReplayFactory ) )
				{
					Sys_Error( "Client ReplayInit() failed!" );
				}

				// Allow the replay dll to setup pointers to client interfaces
				if ( !g_pReplay->CL_Init( g_ClientFactory ) )
				{
					Sys_Error( "Replay system ClientInit() failed" );
				}

				if ( !g_ClientDLL->ReplayPostInit() )
				{
					Sys_Error( "Client ReplayPostInit() failed!" );
				}

#if !defined( DEDICATED )
				extern CGameServer sv;
				if ( !sv.IsDedicated() )
				{
					g_pClientReplayContext = g_pReplay->CL_GetContext();
					g_pReplayManager = g_pClientReplayContext->GetReplayManager();
					g_pReplayMovieManager = g_pClientReplayContext->GetMovieManager();
					g_pReplayMovieRenderer = g_pClientReplayContext->GetMovieRenderer();
					g_pReplayPerformanceManager = g_pClientReplayContext->GetPerformanceManager();
					g_pReplayPerformanceController = g_pClientReplayContext->GetPerformanceController();

					ReplayLib_Init( com_gamedir, g_pClientReplayContext );
				}
#endif
			}
#endif

			toolframework->ClientInit( g_ClientFactory );
		}

		// Don't want TF2 running less than DX 8
		if ( g_pMaterialSystemHardwareConfig && g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 )
		{
			if ( ( Q_stricmp( COM_GetModDirectory(), "tf" ) == 0 ) || ( Q_stricmp( COM_GetModDirectory(), "ep2" ) == 0 ) || ( Q_stricmp( COM_GetModDirectory(), "portal" ) == 0 ) || ( Q_stricmp( COM_GetModDirectory(), "tf_beta" ) == 0 ) )
			{
				Sys_Error( "Your graphics hardware must support at least pixel shader version 1.1 to run this game!" );
			}
		}
	}

	COM_TimestampedLog( "ClientDLL_InitRecvTableMgr" );

	ClientDLL_InitRecvTableMgr();
	
	InitExtraClientCmdCanExecuteVars();
}

//-----------------------------------------------------------------------------
// Purpose: Shuts down the client .dll
//-----------------------------------------------------------------------------
void ClientDLL_Shutdown( void )
{
#if defined( REPLAY_ENABLED )
	if ( g_pReplay )
	{
		g_pReplay->CL_Shutdown();
	}
#endif

	toolframework->ClientShutdown();

	ClientDLL_ShutdownRecvTableMgr();

	vgui::ivgui()->RunFrame();
	
	materials->UncacheAllMaterials();

	vgui::ivgui()->RunFrame();

	g_pClientSidePrediction->Shutdown();

	entitylist = NULL;
	g_pClientSidePrediction = NULL;
	g_ClientFactory = NULL;
	centerprint = NULL;

	g_ClientDLL->Shutdown();
}

//-----------------------------------------------------------------------------
// Purpose: Unloads the client .dll
// Input  :  - 
//-----------------------------------------------------------------------------
void ClientDLL_Unload()
{
	FileSystem_UnloadModule( g_ClientDLLModule );

	g_ClientDLL = NULL;
	g_ClientDLLModule = NULL;
	g_pClientRenderTargets = NULL;

}

//-----------------------------------------------------------------------------
// Purpose: Called when the game initializes and whenever the vid_mode is changed
//   so the HUD can reinitialize itself.
//-----------------------------------------------------------------------------
void ClientDLL_HudVidInit( void )
{
	g_ClientDLL->HudVidInit();
}

//-----------------------------------------------------------------------------
// Purpose: Allow client .dll to modify input data
//-----------------------------------------------------------------------------

void ClientDLL_ProcessInput( void )
{
	if ( !g_ClientDLL )
		return;

	VPROF("ClientDLL_ProcessInput");
	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
	g_ClientDLL->HudProcessInput( cl.IsConnected() );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ClientDLL_FrameStageNotify( ClientFrameStage_t frameStage )
{
	if ( !g_ClientDLL )
		return;

	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );

	g_ClientDLL->FrameStageNotify( frameStage );
}


//-----------------------------------------------------------------------------
// Purpose: Allow client .dll to think
//-----------------------------------------------------------------------------
void ClientDLL_Update( void )
{
	if ( sv.IsDedicated() )
		return;

	if ( !g_ClientDLL )
		return;

	g_ClientDLL->HudUpdate( true );
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ClientDLL_VoiceStatus( int entindex, bool bTalking )
{
	if( g_ClientDLL )
		g_ClientDLL->VoiceStatus( entindex, bTalking );
}


#ifdef IS_WINDOWS_PC
#include "winlite.h"
#endif

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEngineClient::FlashWindow()
{
#ifndef USE_SDL
	FLASHWINFO flashwinfo;
	flashwinfo.cbSize = sizeof( flashwinfo );
	flashwinfo.hwnd = (HWND)game->GetMainWindow();
	flashwinfo.dwFlags = FLASHW_ALL;
	flashwinfo.uCount = 5;
	flashwinfo.dwTimeout = 0;
	FlashWindowEx( &flashwinfo );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CEngineClient::GetInstancesRunningCount( )
{
	return CheckOtherInstancesRunning( );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CEngineClient::GetPausedExpireTime( void )
{
	return cl.GetPausedExpireTime();
}


int CEngineClient::GetServerTick( void )
{
	return cl.GetServerTickCount();
}

//-----------------------------------------------------------------------------
// Adds a handle to the list of entities to update when a partition query occurs
//-----------------------------------------------------------------------------
void CEngineClient::SolidMoved( IClientEntity *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks )
{
	CL_SolidMoved( pSolidEnt, pSolidCollide, pPrevAbsOrigin, accurateBboxTriggerChecks );
}

void CEngineClient::TriggerMoved( IClientEntity *pTriggerEnt, bool accurateBboxTriggerChecks )
{
	CL_TriggerMoved( pTriggerEnt, accurateBboxTriggerChecks );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CEngineClient::StartDemoRecording( const char *pszFilename, const char *pszFolder /* = NULL */ )
{
	bool bUseFolder = ( pszFolder && pszFolder[0] );

	if ( demorecorder->IsRecording() )
	{
		ConMsg( "Already recording.\n" );
		return false;
	}

	if ( demoplayer->IsPlayingBack() )
	{
		ConMsg( "Can't record during demo playback.\n" );
		return false;
	}

	// check filename
	if ( !COM_IsValidPath( pszFilename ) )
	{
		ConMsg( "record %s: invalid filename.\n", pszFilename );
		return false;
	}

	if ( bUseFolder )
	{
		// check folder
		if ( !COM_IsValidPath( pszFolder ) )
		{
			ConMsg( "record %s: invalid folder.\n", pszFolder );
			return false;
		}
	}

	char szFinal[MAX_OSPATH];
	char szTemp[MAX_OSPATH];

	if ( !g_ClientDLL->CanRecordDemo( szTemp, sizeof( szTemp ) ) )
	{
		ConMsg( "%s\n", szTemp );	// re-use szTemp as the error string if the client prevents us from starting a demo
		return false;
	}

	if ( bUseFolder )
	{
		V_sprintf_safe( szTemp, "%s%c%s", pszFolder, CORRECT_PATH_SEPARATOR, pszFilename );
	}
	else
	{
		V_sprintf_safe( szTemp, "%s", pszFilename );
	}

	// remove .dem extension if it exists
	Q_StripExtension( szTemp, szFinal, sizeof( szFinal ) );

	// record it
	demorecorder->StartRecording( szFinal, false );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEngineClient::StopDemoRecording( void )
{
	if ( !demorecorder->IsRecording() )
	{
		ConDMsg( "Not recording a demo.\n" );
		return;
	}

	demorecorder->StopRecording();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CEngineClient::TakeScreenshot( const char *pszFilename, const char *pszFolder /* = NULL */ )
{
	bool bUseFolder = ( pszFolder && pszFolder[0] );

	// check filename
	if ( !COM_IsValidPath( pszFilename ) )
	{
		ConMsg( "TakeScreenshot invalid filename: %s\n", pszFilename );
		return;
	}

	if ( bUseFolder )
	{
		// check folder
		if ( !COM_IsValidPath( pszFolder ) )
		{
			ConMsg( "TakeScreenshot invalid folder: %s\n", pszFolder );
			return;
		}
	}

	bool bReadPixelsFromFrontBuffer = g_pMaterialSystemHardwareConfig->ReadPixelsFromFrontBuffer();
	if ( bReadPixelsFromFrontBuffer )
	{
		Shader_SwapBuffers();
	}

	// Disable threading for the duration of the screenshots, because we need to get pointers to the (complete) 
	// back buffer right now.
	bool bEnabled = materials->AllowThreading( false, g_nMaterialSystemThread );

	char szFinal[MAX_OSPATH] = {0};

	if ( bUseFolder )
	{
		V_sprintf_safe( szFinal, "%s%c%s", pszFolder, CORRECT_PATH_SEPARATOR, pszFilename );
	}
	else
	{
		V_sprintf_safe( szFinal, "%s", pszFilename );
	}

	V_SetExtension( szFinal, ".tga", sizeof( szFinal ) ); 

	videomode->TakeSnapshotTGA( szFinal );

	// Restore threading if it was previously enabled (if it wasn't this will do nothing).
	materials->AllowThreading( bEnabled, g_nMaterialSystemThread );

	if ( !bReadPixelsFromFrontBuffer )
	{
		Shader_SwapBuffers();
	}
}
