Override GameUI
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.
Requirements
- A mod running on Source SDK 2013
- Knowledge of C++
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.
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();
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