Interactive Ingame VGUI Panels: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
m (clean up, added deadend tag)
 
(3 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{Dead end|date=January 2024}}
== Introduction ==
== Introduction ==


Line 7: Line 9:
This first problem is that <code>CInput::ExtraMouseSample</code> calls <code>g_pClientMode->CreateMove()</code> with a faked <code>CUserCmd</code> which does not have the correct button flags.  When <code>DetermineVguiInputMode</code> is called from within <code>CreateMove</code> with this bad input problems occur.  This can be fixed if a boolean is added that will prevent the function running during the extra mouse samples.
This first problem is that <code>CInput::ExtraMouseSample</code> calls <code>g_pClientMode->CreateMove()</code> with a faked <code>CUserCmd</code> which does not have the correct button flags.  When <code>DetermineVguiInputMode</code> is called from within <code>CreateMove</code> with this bad input problems occur.  This can be fixed if a boolean is added that will prevent the function running during the extra mouse samples.


First in <b>client/in_main.cpp</b> replace:
First in '''client/in_main.cpp''' replace:


// Let the move manager override anything it wants to.
<source lang=cpp>
if ( g_pClientMode->CreateMove( frametime, cmd ) )
// Let the move manager override anything it wants to.
{
if ( g_pClientMode->CreateMove( frametime, cmd ) )
{
// Get current view angles after the client mode tweaks with it
// Get current view angles after the client mode tweaks with it
engine->SetViewAngles( cmd->viewangles );
engine->SetViewAngles( cmd->viewangles );
prediction->SetLocalViewAngles( cmd->viewangles );
prediction->SetLocalViewAngles( cmd->viewangles );
}
}
</source>


With
With


// Let the move manager override anything it wants to.
<source lang=cpp>
if ( g_pClientMode->CreateMove( frametime, cmd, true ) )
// Let the move manager override anything it wants to.
{
if ( g_pClientMode->CreateMove( frametime, cmd, true ) )
// Get current view angles after the client mode tweaks with it
{
engine->SetViewAngles( cmd->viewangles );
// Get current view angles after the client mode tweaks with it
prediction->SetLocalViewAngles( cmd->viewangles );
engine->SetViewAngles( cmd->viewangles );
}
prediction->SetLocalViewAngles( cmd->viewangles );
}
</source>


So now when <code>CreateMove</code> is called it passed a Boolean which will be used to show this is a valid call to the VGUI screen as well as <code>frametime</code> and <code>cmd</code>.  In order for this to work the <code>CreateMove</code> function must be modified to accommodate this extra Boolean.  To do this, add:
So now when <code>CreateMove</code> is called it passed a Boolean which will be used to show this is a valid call to the VGUI screen as well as <code>frametime</code> and <code>cmd</code>.  In order for this to work the <code>CreateMove</code> function must be modified to accommodate this extra Boolean.  To do this, add:


virtual bool CreateMove( float flInputSampleTime, CUserCmd *cmd, bool bVguiUpdate ) = 0;
<source lang=cpp>
virtual bool CreateMove( float flInputSampleTime, CUserCmd *cmd, bool bVguiUpdate ) = 0;
</source>
 
above
above


virtual bool CreateMove( float flInputSampleTime, CUserCmd *cmd ) = 0;
<source lang=cpp>
virtual bool CreateMove( float flInputSampleTime, CUserCmd *cmd ) = 0;
</source>


in <b>client/iclientmode.h</b>, <b>client/hl2/c_basehlplayer.h</b> and <b>client/clientmode_shared.h</b>
in '''client/iclientmode.h''', '''client/hl2/c_basehlplayer.h''' and '''client/clientmode_shared.h'''


Next functionality has to be provided for these headers, so in <b>client/clientmode_shared.cpp</b> change the section
Next functionality has to be provided for these headers, so in '''client/clientmode_shared.cpp''' change the section


bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
<source lang=cpp>
{
bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
// Let the player override the view.
{
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
// Let the player override the view.
if(!pPlayer)
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
return true;
if(!pPlayer)
return true;
// Let the player at it
 
return pPlayer->CreateMove( flInputSampleTime, cmd );
// Let the player at it
}
return pPlayer->CreateMove( flInputSampleTime, cmd );
}
</source>


To
To


bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd, bool bVguiUpdate )
<source lang=cpp>
{
bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd, bool bVguiUpdate )
// Let the player override the view.
{
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
// Let the player override the view.
if(!pPlayer)
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
return true;
if(!pPlayer)
return true;
// Let the player at it
return pPlayer->CreateMove( flInputSampleTime, cmd, bVguiUpdate );
}
bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
{
Return CreateMove( flInputSampleTime, cmd, false );
}


And in <b>client/hl2/c_basehlplayer.cpp</b> replace
// Let the player at it
return pPlayer->CreateMove( flInputSampleTime, cmd, bVguiUpdate );
}
bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
{
return CreateMove( flInputSampleTime, cmd, false );
}
</source>


bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
And in '''client/hl2/c_basehlplayer.cpp''' replace
{
 
bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd );
<source lang=cpp>
bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
{
bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd );
</source>


with
with


bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd , bool bVguiUpdate )
<source lang=cpp>
{
bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd , bool bVguiUpdate )
bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd, bVguiUpdate );
{
bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd, bVguiUpdate );
</source>


Finally in <b>client/c_baseplayer.cpp</b> find:
Finally in '''client/c_baseplayer.cpp''' find:


bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
<source lang=cpp>
bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
</source>


and replace it with:
and replace it with:


bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd, bool bVguiUpdate )
<source lang=cpp>
bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd, bool bVguiUpdate )
</source>


Then inside the <code>CreateMove</code> function find:
Then inside the <code>CreateMove</code> function find:


DetermineVguiInputMode( pCmd );
<source lang=cpp>
DetermineVguiInputMode( pCmd );
</source>


And replace it with:
And replace it with:


if (bVguiUpdate)
<source lang=cpp>
{
if (bVguiUpdate)
bool tempvguimode = IsInVGuiInputMode();
{
// Check to see if we're in vgui input mode...
bool tempvguimode = IsInVGuiInputMode();
DetermineVguiInputMode( pCmd );
// Check to see if we're in vgui input mode...
DetermineVguiInputMode( pCmd );
if (tempvguimode == !IsInVGuiInputMode())
 
{
if (tempvguimode == !IsInVGuiInputMode())
if (IsInVGuiInputMode())
{
{
if (IsInVGuiInputMode())
engine->ClientCmd( "vguimode_true" );
{
}
engine->ClientCmd( "vguimode_true" );
else
}
{
else
engine->ClientCmd( "vguimode_false" );
{
}
engine->ClientCmd( "vguimode_false" );
}
}
}
}
}
</source>


This function witll now only call the DetermineVguiInputMode function when a valid call is being made to the CreateMove function.  Furthermore, when the VguiMode changes as a player interacts with a screen it will send messages from the client to the server.
This function witll now only call the DetermineVguiInputMode function when a valid call is being made to the CreateMove function.  Furthermore, when the VguiMode changes as a player interacts with a screen it will send messages from the client to the server.
Line 117: Line 143:


By performing the previous fix, vguiscreens will be initialised with proper data, however due to a flaw in <code>DetermineVguiInputMode</code> the vgui screens are not detected and so the player never enters vguiinput mode.  If the vgui screens are simply for display purposes this is fine, to get the screens interactive this must change however.  The problem lies with the <code>FindNearbyVguiScreen</code> function in '''client/c_vguiscreen.cpp'''.  In this function a sphere enumerater is used to detect the screens, but this fails to give a result.  To change this a vector can be added to contain all vguiscreens present within the level.  This is done by adding:
By performing the previous fix, vguiscreens will be initialised with proper data, however due to a flaw in <code>DetermineVguiInputMode</code> the vgui screens are not detected and so the player never enters vguiinput mode.  If the vgui screens are simply for display purposes this is fine, to get the screens interactive this must change however.  The problem lies with the <code>FindNearbyVguiScreen</code> function in '''client/c_vguiscreen.cpp'''.  In this function a sphere enumerater is used to detect the screens, but this fails to give a result.  To change this a vector can be added to contain all vguiscreens present within the level.  This is done by adding:
  CUtlVector<C_VGuiScreen*> g_pVGUIScreens;
  CUtlVector<C_VGuiScreen*> g_pVGUIScreens;
Above  
 
Above
 
  CUtlDict<KeyValues*, int> g_KeyValuesCache;
  CUtlDict<KeyValues*, int> g_KeyValuesCache;
In '''client/c_vguiscreen.cpp'''.
In '''client/c_vguiscreen.cpp'''.


In order to fill this vector the function  
In order to fill this vector the function
 
  g_pVGUIScreens.AddToTail( this );
  g_pVGUIScreens.AddToTail( this );
is added to the constructor of C_VGuiScreen and
is added to the constructor of C_VGuiScreen and
  g_pVGUIScreens.FindAndRemove( this );
  g_pVGUIScreens.FindAndRemove( this );
is added to the destructor.
is added to the destructor.
Next find the <code>FindNearbyVguiScreen</code> and replace it with:
Next find the <code>FindNearbyVguiScreen</code> and replace it with:


C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &viewAngle, int nTeam )
<source lang=cpp>
{
C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &viewAngle, int nTeam )
if ( IsX360() )
{
{
if ( IsX360() )
// X360TBD: Turn this on if feature actually used
{
return NULL;
// X360TBD: Turn this on if feature actually used
}
return NULL;
}
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
 
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
Assert( pLocalPlayer );
 
Assert( pLocalPlayer );
if ( !pLocalPlayer )
 
return NULL;
if ( !pLocalPlayer )
return NULL;
// Get the view direction...
 
Vector lookDir;
// Get the view direction...
AngleVectors( viewAngle, &lookDir );
Vector lookDir;
AngleVectors( viewAngle, &lookDir );
// Create a ray used for raytracing  
 
Vector lookEnd;
// Create a ray used for raytracing  
VectorMA( viewPosition, 2.0f * VGUI_SCREEN_MODE_RADIUS, lookDir, lookEnd );
Vector lookEnd;
VectorMA( viewPosition, 2.0f * VGUI_SCREEN_MODE_RADIUS, lookDir, lookEnd );
Ray_t lookRay;
 
lookRay.Init( viewPosition, lookEnd );
Ray_t lookRay;
lookRay.Init( viewPosition, lookEnd );
 
Vector vecOut, vecViewDelta;
Vector vecOut, vecViewDelta;
 
float flBestDist = 2.0f;
float flBestDist = 2.0f;
C_VGuiScreen *pBestScreen = NULL;
C_VGuiScreen *pBestScreen = NULL;
for (int i = 0; i < g_pVGUIScreens.Count(); i++)
for (int i = 0; i < g_pVGUIScreens.Count(); i++)
{
{
if (g_pVGUIScreens.IsValidIndex(i))
if (g_pVGUIScreens.IsValidIndex(i))
{
{
C_VGuiScreen *pScreen = g_pVGUIScreens[i];
C_VGuiScreen *pScreen = g_pVGUIScreens[i];
if ( pScreen->IsAttachedToViewModel() )
if ( pScreen->IsAttachedToViewModel() )
continue;
continue;
 
// Don't bother with screens I'm behind...
// Don't bother with screens I'm behind...
// Hax - don't cancel backfacing with viewmodel attached screens.
// Hax - don't cancel backfacing with viewmodel attached screens.
// we can get prediction bugs that make us backfacing for one frame and
// we can get prediction bugs that make us backfacing for one frame and
// it resets the mouse position if we lose focus.
// it resets the mouse position if we lose focus.
if ( pScreen->IsBackfacing(viewPosition) )
if ( pScreen->IsBackfacing(viewPosition) )
continue;
continue;
 
// Don't bother with screens that are turned off
// Don't bother with screens that are turned off
if (!pScreen->IsActive())
if (!pScreen->IsActive())
continue;
continue;
 
// FIXME: Should this maybe go into a derived class of some sort?
// FIXME: Should this maybe go into a derived class of some sort?
// Don't bother with screens on the wrong team
// Don't bother with screens on the wrong team
if (!pScreen->IsVisibleToTeam(nTeam))
if (!pScreen->IsVisibleToTeam(nTeam))
continue;
continue;
 
if ( !pScreen->AcceptsInput() )
if ( !pScreen->AcceptsInput() )
continue;
continue;
 
if ( pScreen->IsInputOnlyToOwner() && pScreen->GetPlayerOwner() != pLocalPlayer )
if ( pScreen->IsInputOnlyToOwner() && pScreen->GetPlayerOwner() != pLocalPlayer )
continue;
continue;
 
// Test perpendicular distance from the screen...
// Test perpendicular distance from the screen...
pScreen->GetVectors( NULL, NULL, &vecOut );
pScreen->GetVectors( NULL, NULL, &vecOut );
VectorSubtract( viewPosition, pScreen->GetAbsOrigin(), vecViewDelta );
VectorSubtract( viewPosition, pScreen->GetAbsOrigin(), vecViewDelta );
float flPerpDist = DotProduct(vecViewDelta, vecOut);
float flPerpDist = DotProduct(vecViewDelta, vecOut);
if ( (flPerpDist < 0) || (flPerpDist > VGUI_SCREEN_MODE_RADIUS) )
if ( (flPerpDist < 0) || (flPerpDist > VGUI_SCREEN_MODE_RADIUS) )
continue;
continue;
 
// Perform a raycast to see where in barycentric coordinates the ray hits
// Perform a raycast to see where in barycentric coordinates the ray hits
// the viewscreen; if it doesn't hit it, you're not in the mode
// the viewscreen; if it doesn't hit it, you're not in the mode
float u, v, t;
float u, v, t;
if (!pScreen->IntersectWithRay( lookRay, &u, &v, &t ))
if (!pScreen->IntersectWithRay( lookRay, &u, &v, &t ))
continue;
continue;
 
// Barycentric test
// Barycentric test
if ((u < 0) || (v < 0) || (u > 1) || (v > 1))
if ((u < 0) || (v < 0) || (u > 1) || (v > 1))
continue;
continue;
 
if ( t < flBestDist )
if ( t < flBestDist )
{
{
flBestDist = t;
flBestDist = t;
pBestScreen = pScreen;
pBestScreen = pScreen;
}
}
}
}
}
}
return pBestScreen;
return pBestScreen;
}
}
</source>


== Preventing Accidental Deaths ==
== Preventing Accidental Deaths ==
Line 227: Line 263:


Start by looking at the <code>CBasePlayer</code> function in '''server/player.h''' and adding the following lines:
Start by looking at the <code>CBasePlayer</code> function in '''server/player.h''' and adding the following lines:
public:
 
bool m_pVGUImode;
<source lang=cpp> public:
bool GetVGUIMode(void) {return m_pVGUImode;}
bool m_pVGUImode;
void SetVGUImode(bool newmode) { m_pVGUImode = newmode;}
bool GetVGUIMode(void) {return m_pVGUImode;}
void SetVGUImode(bool newmode) { m_pVGUImode = newmode;}
</source>
 
Then in '''server/player.cpp''' at the end of the <code>Spawn</code> function add
Then in '''server/player.cpp''' at the end of the <code>Spawn</code> function add
SetVGUImode(false);
SetVGUImode(false);
And at the end of the <code>ClientCommand</code> function, after the existing else ifs add
And at the end of the <code>ClientCommand</code> function, after the existing else ifs add
else if ( stricmp( cmd, "vguimode_true" ) == 0 )
 
{
<source lang=cpp>
SetVGUImode( true );
else if ( stricmp( cmd, "vguimode_true" ) == 0 )
return true;
{
}
SetVGUImode( true );
else if ( stricmp( cmd, "vguimode_false" ) == 0 )
return true;
{
}
SetVGUImode( false );
else if ( stricmp( cmd, "vguimode_false" ) == 0 )
return true;
{
}
SetVGUImode( false );
return true;
}
</source>
 
These functions will capture the commands we sent earlier when the player enters VGUI mode on the client and enable the server to know when the weapons should be lowered.  Finally, in '''server/hl2/hl2_player.cpp''' beneath
These functions will capture the commands we sent earlier when the player enters VGUI mode on the client and enable the server to know when the weapons should be lowered.  Finally, in '''server/hl2/hl2_player.cpp''' beneath
  UpdateWeaponPosture();
  UpdateWeaponPosture();
Add
Add
// If we're in VGUI mode we should avoid shooting
 
if ( GetVGUIMode() )
<source lang=cpp>
{
// If we're in VGUI mode we should avoid shooting
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
if ( GetVGUIMode() )
}
{
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
}
</source>
 
And inside the <code>UpdateWeaponPosture</code> function add
And inside the <code>UpdateWeaponPosture</code> function add


if ( GetVGUIMode() )
<source lang=cpp> if ( GetVGUIMode() )
{
{
//We're over a friendly, drop our weapon
//We're over a friendly, drop our weapon
if ( Weapon_Lower() == false )
if ( Weapon_Lower() == false )
{
{
//FIXME: We couldn't lower our weapon!
//FIXME: We couldn't lower our weapon!
}
}
 
return;
return;
}
}
</source>
Beneath
Beneath
  CBaseEntity *aimTarget = tr.m_pEnt;
  CBaseEntity *aimTarget = tr.m_pEnt;
These functions will stop the player firing, and lower the weapon respectively when the player is looking at a VGUI screen.
These functions will stop the player firing, and lower the weapon respectively when the player is looking at a VGUI screen.


[[Category:Free source code]]
[[Category:Free source code]]

Latest revision as of 10:05, 21 January 2024

Dead End - Icon.png
This article has no Wikipedia icon links to other VDC articles. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024

Introduction

This tutorial will detail how to add the necessary functionality to create a fully working ingame VGUI panel. There are a few small flaws with the workings of the original VGUI panel presented in the source code that must be rectified before these panels will operate.

Fixing the VGUI Screen errors

This first problem is that CInput::ExtraMouseSample calls g_pClientMode->CreateMove() with a faked CUserCmd which does not have the correct button flags. When DetermineVguiInputMode is called from within CreateMove with this bad input problems occur. This can be fixed if a boolean is added that will prevent the function running during the extra mouse samples.

First in client/in_main.cpp replace:

// Let the move manager override anything it wants to.
if ( g_pClientMode->CreateMove( frametime, cmd ) )
{
	// Get current view angles after the client mode tweaks with it
	engine->SetViewAngles( cmd->viewangles );
	prediction->SetLocalViewAngles( cmd->viewangles );
}

With

// Let the move manager override anything it wants to.
if ( g_pClientMode->CreateMove( frametime, cmd, true ) )
{
	// Get current view angles after the client mode tweaks with it
	engine->SetViewAngles( cmd->viewangles );
	prediction->SetLocalViewAngles( cmd->viewangles );
}

So now when CreateMove is called it passed a Boolean which will be used to show this is a valid call to the VGUI screen as well as frametime and cmd. In order for this to work the CreateMove function must be modified to accommodate this extra Boolean. To do this, add:

virtual bool	CreateMove( float flInputSampleTime, CUserCmd *cmd, bool bVguiUpdate ) = 0;

above

virtual bool	CreateMove( float flInputSampleTime, CUserCmd *cmd ) = 0;

in client/iclientmode.h, client/hl2/c_basehlplayer.h and client/clientmode_shared.h

Next functionality has to be provided for these headers, so in client/clientmode_shared.cpp change the section

bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
{
	// Let the player override the view.
	C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
	if(!pPlayer)
		return true;

	// Let the player at it
	return pPlayer->CreateMove( flInputSampleTime, cmd );
}

To

bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd, bool bVguiUpdate )
{
	// Let the player override the view.
	C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
	if(!pPlayer)
		return true;

	// Let the player at it
	return pPlayer->CreateMove( flInputSampleTime, cmd, bVguiUpdate );
}
bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
{
	return CreateMove( flInputSampleTime, cmd, false );
}

And in client/hl2/c_basehlplayer.cpp replace

bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
{
	bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd );

with

bool C_BaseHLPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd , bool bVguiUpdate )
{
	bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd, bVguiUpdate );

Finally in client/c_baseplayer.cpp find:

bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )

and replace it with:

bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd, bool bVguiUpdate )

Then inside the CreateMove function find:

DetermineVguiInputMode( pCmd );

And replace it with:

if (bVguiUpdate)
{
	bool tempvguimode = IsInVGuiInputMode();
	// Check to see if we're in vgui input mode...
	DetermineVguiInputMode( pCmd );

	if (tempvguimode == !IsInVGuiInputMode())
	{
		if (IsInVGuiInputMode())
		{
			engine->ClientCmd( "vguimode_true" );
		}
		else
		{
			engine->ClientCmd( "vguimode_false" );
		}
	}
}

This function witll now only call the DetermineVguiInputMode function when a valid call is being made to the CreateMove function. Furthermore, when the VguiMode changes as a player interacts with a screen it will send messages from the client to the server.

Fixing the Interactive Mode

By performing the previous fix, vguiscreens will be initialised with proper data, however due to a flaw in DetermineVguiInputMode the vgui screens are not detected and so the player never enters vguiinput mode. If the vgui screens are simply for display purposes this is fine, to get the screens interactive this must change however. The problem lies with the FindNearbyVguiScreen function in client/c_vguiscreen.cpp. In this function a sphere enumerater is used to detect the screens, but this fails to give a result. To change this a vector can be added to contain all vguiscreens present within the level. This is done by adding:

CUtlVector<C_VGuiScreen*> g_pVGUIScreens;

Above

CUtlDict<KeyValues*, int> g_KeyValuesCache;

In client/c_vguiscreen.cpp.

In order to fill this vector the function

g_pVGUIScreens.AddToTail( this );

is added to the constructor of C_VGuiScreen and

g_pVGUIScreens.FindAndRemove( this );

is added to the destructor.

Next find the FindNearbyVguiScreen and replace it with:

C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &viewAngle, int nTeam )
{
	if ( IsX360() )
	{
		// X360TBD: Turn this on if feature actually used
		return NULL;
	}

	C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();

	Assert( pLocalPlayer );

	if ( !pLocalPlayer )
		return NULL;

	// Get the view direction...
	Vector lookDir;
	AngleVectors( viewAngle, &lookDir );

	// Create a ray used for raytracing 
	Vector lookEnd;
	VectorMA( viewPosition, 2.0f * VGUI_SCREEN_MODE_RADIUS, lookDir, lookEnd );

	Ray_t lookRay;
	lookRay.Init( viewPosition, lookEnd );

	
	Vector vecOut, vecViewDelta;

	float flBestDist = 2.0f;
	C_VGuiScreen *pBestScreen = NULL;
	
	
	for (int i = 0; i < g_pVGUIScreens.Count(); i++)
	{
		if (g_pVGUIScreens.IsValidIndex(i))
		{
			C_VGuiScreen *pScreen = g_pVGUIScreens[i];
		
			if ( pScreen->IsAttachedToViewModel() )
				continue;

			// Don't bother with screens I'm behind...
			// Hax - don't cancel backfacing with viewmodel attached screens.
			// we can get prediction bugs that make us backfacing for one frame and
			// it resets the mouse position if we lose focus.
			if ( pScreen->IsBackfacing(viewPosition) )
				continue;

			// Don't bother with screens that are turned off
			if (!pScreen->IsActive())
				continue;

			// FIXME: Should this maybe go into a derived class of some sort?
			// Don't bother with screens on the wrong team
			if (!pScreen->IsVisibleToTeam(nTeam))
				continue;

			if ( !pScreen->AcceptsInput() )
				continue;

			if ( pScreen->IsInputOnlyToOwner() && pScreen->GetPlayerOwner() != pLocalPlayer )
				continue;

			// Test perpendicular distance from the screen...
			pScreen->GetVectors( NULL, NULL, &vecOut );
			VectorSubtract( viewPosition, pScreen->GetAbsOrigin(), vecViewDelta );
			float flPerpDist = DotProduct(vecViewDelta, vecOut);
			if ( (flPerpDist < 0) || (flPerpDist > VGUI_SCREEN_MODE_RADIUS) )
				continue;

			// Perform a raycast to see where in barycentric coordinates the ray hits
			// the viewscreen; if it doesn't hit it, you're not in the mode
			float u, v, t;
			if (!pScreen->IntersectWithRay( lookRay, &u, &v, &t ))
				continue;

			// Barycentric test
			if ((u < 0) || (v < 0) || (u > 1) || (v > 1))
				continue;

			if ( t < flBestDist )
			{
				flBestDist = t;
				pBestScreen = pScreen;
			}
		}
	}
	
	return pBestScreen;
}

Preventing Accidental Deaths

At this point the VGUI screens will not only work, but will be fully interactive. Buttons can be clicked by pressing the fire button whilst pointing at them on the VGUI screen. Try this with a rocket launcher though and a problem arises. The player’s gun still fires when the buttons are pressed, which in the example case will prove fatal and annoy the player. To prevent this, the simplest way is to make the player lower their gun and remove the fire button flags. This stops the firing and gives the player a cue to known when they can interact with the screen.

Start by looking at the CBasePlayer function in server/player.h and adding the following lines:

 public:
	bool		m_pVGUImode;
	bool		GetVGUIMode(void) {return m_pVGUImode;}
	void		SetVGUImode(bool newmode) { m_pVGUImode = newmode;}

Then in server/player.cpp at the end of the Spawn function add SetVGUImode(false); And at the end of the ClientCommand function, after the existing else ifs add

else if ( stricmp( cmd, "vguimode_true" ) == 0 )
{
	SetVGUImode( true );
	return true;
}
else if ( stricmp( cmd, "vguimode_false" ) == 0 )
{
	SetVGUImode( false );
	return true;
}

These functions will capture the commands we sent earlier when the player enters VGUI mode on the client and enable the server to know when the weapons should be lowered. Finally, in server/hl2/hl2_player.cpp beneath

UpdateWeaponPosture();

Add

// If we're in VGUI mode we should avoid shooting
if ( GetVGUIMode() )
{
	m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
}

And inside the UpdateWeaponPosture function add

 if ( GetVGUIMode() )
{
	//We're over a friendly, drop our weapon
	if ( Weapon_Lower() == false )
	{
		//FIXME: We couldn't lower our weapon!
	}

	return;
}

Beneath

CBaseEntity *aimTarget = tr.m_pEnt;

These functions will stop the player firing, and lower the weapon respectively when the player is looking at a VGUI screen.