Override GameUI

From Valve Developer Community
Jump to navigation Jump to search

The Source 2013 branch offers two new functions for overriding GameUI. This is the method used by the current main menu of Team Fortress 2, and can now be used to have more control over any mod's UI. The first function (which is covered by most of this tutorial) is SetMainMenuOverride(), which takes a VPANEL and makes it the sole visible child of the GameUI root.

The main menu of Team Fortress 2.

Requirements

Have read and/or understand:

Creating a root panel

The first and most important part of the overriding process is the panel that is used. Here, we will be using a child class that includes functions to access the GameUI interface. These files need to be saved in a folder/filter under the client project in Visual Studio.

Todo: This panel needs to handle an actual menu as a child of this at some point.

OverrideUI_RootPanel.h

#include "vgui_controls/Panel.h"
#include "GameUI/IGameUI.h"

// This class is what is actually used instead of the main menu.
class OverrideUI_RootPanel : public vgui::Panel
{
	DECLARE_CLASS_SIMPLE( OverrideUI_RootPanel, vgui::Panel );
public:
	OverrideUI_RootPanel(vgui::VPANEL parent);
	virtual ~OverrideUI_RootPanel();

	IGameUI*		GetGameUI();

protected:
	virtual void	ApplySchemeSettings(vgui::IScheme *pScheme);

private:
	bool			LoadGameUI();

	int				m_ExitingFrameCount;
	bool			m_bCopyFrameBuffer;

	IGameUI*		gameui;
};

extern OverrideUI_RootPanel *guiroot;

OverrideUI_RootPanel.cpp

Here, we're going to be doing a couple things in advance. First of all, we have an interface to the GameUI DLL, which will be ready to access whenever need be. The OverrideGameUI() function will be used to call our needed functions straight from the DLL once the panel has been created.

#include "cbase.h"
#include "overrideui_rootpanel.h"
#include "ioverrideinterface.h"

#include "vgui/ILocalize.h"
#include "vgui/IPanel.h"
#include "vgui/ISurface.h"
#include "vgui/ISystem.h"
#include "vgui/IVGui.h"
#include "ienginevgui.h"
#include <engine/IEngineSound.h>
#include "filesystem.h"

using namespace vgui;

// See interface.h/.cpp for specifics:  basically this ensures that we actually Sys_UnloadModule the dll and that we don't call Sys_LoadModule 
//  over and over again.
static CDllDemandLoader g_GameUIDLL( "GameUI" );

OverrideUI_RootPanel *guiroot = NULL;

void OverrideGameUI()
{
	if( !OverrideUI->GetPanel() )
	{
		OverrideUI->Create(NULL);
	}
	if( guiroot->GetGameUI() )
	{
		guiroot->GetGameUI()->SetMainMenuOverride( guiroot->GetVPanel() );
		return;
	}
}

OverrideUI_RootPanel::OverrideUI_RootPanel(VPANEL parent) : Panel( NULL, "OverrideUIRootPanel" )
{
	SetParent(parent);
	guiroot = this;

	m_bCopyFrameBuffer = false;
	gameui = NULL;

	LoadGameUI();

	m_ExitingFrameCount = 0;
}

IGameUI *OverrideUI_RootPanel::GetGameUI()
{
	if( !gameui )
	{
		if ( !LoadGameUI() )
			return NULL;
	}

	return gameui;
}

bool OverrideUI_RootPanel::LoadGameUI()
{
	if( !gameui )
	{
		CreateInterfaceFn gameUIFactory = g_GameUIDLL.GetFactory();
		if ( gameUIFactory )
		{
			gameui = (IGameUI *) gameUIFactory(GAMEUI_INTERFACE_VERSION, NULL);
			if( !gameui )
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}
	return true;
}

OverrideUI_RootPanel::~OverrideUI_RootPanel()
{
	gameui = NULL;
	g_GameUIDLL.Unload();
}

void OverrideUI_RootPanel::ApplySchemeSettings(IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);

	// Resize the panel to the screen size
	// Otherwise, it'll just be in a little corner
	int wide, tall;
	vgui::surface()->GetScreenSize(wide, tall);
	SetSize(wide,tall);
}

Creating an interface

The next thing to do is create an interface to create and destroy the panel. The difference between this and the panel in this tutorial is that the panel will be created without a parent and will be redirected afterwards. These files should also be put in the same folder as above for easy access.

IOverrideInterface.h

#include <vgui/VGUI.h>
 
namespace vgui
{
	class Panel;
}
 
class IOverrideInterface
{
public:
	virtual void		Create( vgui::VPANEL parent ) = 0;
	virtual vgui::VPANEL	GetPanel( void ) = 0;
	virtual void		Destroy( void ) = 0;
};
 
extern IOverrideInterface *OverrideUI;

OverrideInterface.cpp

#include "cbase.h"
#include "ioverrideinterface.h"
#include "Overrideui_RootPanel.h"

// Derived class of override interface
class COverrideInterface : public IOverrideInterface
{
private:
	OverrideUI_RootPanel *MainMenu;
 
public:
	int UI_Interface( void )
	{
		MainMenu = NULL;
	}
 
	void Create( vgui::VPANEL parent )
	{
		// Create immediately
		MainMenu = new OverrideUI_RootPanel(parent);
	}

	vgui::VPANEL GetPanel( void )
	{
		if ( !MainMenu )
			return NULL;
		return MainMenu->GetVPanel();
	}
 
	void Destroy( void )
	{
		if ( MainMenu )
		{
			MainMenu->SetParent( (vgui::Panel *)NULL );
			delete MainMenu;
		}
	}
 
};
 
static COverrideInterface g_SMenu;
IOverrideInterface *OverrideUI = ( IOverrideInterface * )&g_SMenu;

Calling the functions

Finally, we're going to be plugging these functions into the VGUI client loading function VGui_CreateGlobalPanels(). This is found about halfway through the vgui_int.cpp file. Right after the first set of VPANEL declarations, enter this in:

OverrideUI->Create( NULL );
OverrideGameUI();

Once that's done, move to the top of the file and include the IOverrideInterface.h and Overrideui_RootPanel.h files. And after the MP3 player functions are declared below that, add:

void OverrideGameUI();

Calling menu commands

The second new function that IGameUI offers is SendMainMenuCommand(), which takes dialog-opening command strings that can normally be found in the command values in GameMenu.res. When making a new menu, be sure to keep this function in mind if you don't want to make your own versions of these dialogs.

Conclusion

Feel free to play around with this setup. It took innumerable tries to get a setup to work correctly, and this is the result. Be sure to report any issues with or suggest corrections to this tutorial.

Changelog

UI_RootPanel *guiroot = NULL;

(line 21 of Override_RootPanel.cpp) changed to

OverrideUI_RootPanel *guiroot = NULL;

to solve undefined error

UI_Interface(void)

(line 12 of OverrideInterface.cpp) changed to

int UI_Interface(void)

to solve int expected error