Creating a Team-Menu: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
m (clean up, replaced: <b> → ''', </b> → ''', added orphan, underlinked tags)
 
(20 intermediate revisions by 10 users not shown)
Line 1: Line 1:
'''Warning: there no vgui_TeamFortressViewport.pre file in latest sdk anymore. All its functions now in Base_viewport.cpp'''
{{Multiple issues|
----
{{Underlinked|date=January 2024}}
[originally from sourcewiki.org]
{{Orphan|date=January 2024}}
----
}}
The Source SDK already has most of the code you will need for a team menu, but not everything. Valve must want us to learn by filling in the gaps. ;)


My goal in this first step is to enable a team-selection menu, using the available code as much as possible. I do not implement actual teamplay at this time; it's just the VGUI menu without underlying game logic.
{{warning|There is no vgui_TeamFortressViewport.pre file in latest sdk anymore, all of its functions now in Baseviewport.cpp.}}


==Creating the Viewport==
The [[Source SDK]] already has most of the code you will need to create a team menu, but not everything. Valve must want us to learn by filling in the gaps.


First you should take a look at the [http://www.valve-erc.com/srcsdk/VGUI2/vgui2.html VGUI2 Overview]. You will learn that the base for all VGUI panels (not class-based, but where VGUI-objects are attached) is a viewport.
The goal in this first step is to enable a team-selection menu, using the available code as much as possible.
This viewport is created in '''cl_dll/sdk/clientmode_sdk.pre''' in the method '''ClientModeSDKNormal::InitViewport''':
 
== Creating the viewport ==
First you should take a look at the [http://www.valve-erc.com/srcsdk/VGUI2/vgui2.html VGUI2 Overview](broken link). You will learn that the base for all VGUI panels (not class-based, but where VGUI-objects are attached) is a viewport.
 
This viewport is created in <code>cl_dll/clientmode_shared.cpp</code> in the method <code>ClientModeSharedl::InitViewport</code>:
<pre>
<pre>
void ClientModeSDKNormal::InitViewport()
void ClientModeShared::InitViewport()
{
{
   m_pViewport = new SDKViewport();
   m_pViewport = new CBaseViewport();
   m_pViewport->Start( gameuifuncs, gameeventmanager );
   m_pViewport->Start( gameuifuncs, gameeventmanager );
}
}
</pre>
</pre>
I wrote this just for information, so you know, where the voodoo begins - you don't need to change anything here :)
 
==Creating the panel==
== Creating the panel ==
Let's take a look at the CBaseViewport, which handles all the different Panels used by the mod. It is defined in '''cl_dll/game_controls/vgui_TeamFortressViewport.pre'''.
Let's take a look at the <code>CBaseViewport</code>, which handles all the different Panels used by the mod. It is defined in <code>cl_dll/game_controls/baseviewport.cpp</code>.


This function creates all panels known to this viewport:
This function creates all panels known to this viewport:
Line 35: Line 38:
}
}
</pre>
</pre>
Since you want to get the team menu, remove the comment before PANEL_TEAM.


The "real" creation is done in '''CBaseViewport::CreatePanelByName''', a function which takes the panelname as string and creates a corresponding class. The panel names are referenced in '''game_shared/viewport_panel_names.h''', e.g.
Since you want to get the team menu, remove the comment before <code>PANEL_TEAM</code>.
 
The "real" creation is done in <code>CBaseViewport::CreatePanelByName</code>, a function which takes the panel name as string and creates a corresponding class. The panel names are referenced in <code>game_shared/viewport_panel_names.h</code>, e.g.
<pre>
<pre>
#define PANEL_TEAM "team"
#define PANEL_TEAM "team"
</pre>
</pre>
PANEL_TEAM creates the class CTeamMenu, which contains all the functionality of that panel and is implemented in '''game_controls/teammenu.pre'''. So uncomment that, too.
<code>PANEL_TEAM</code> creates the class <code>CTeamMenu</code>, which contains all the functionality of that panel and is implemented in <code>game_controls/teammenu.cpp</code>. So uncomment that, too.
<pre>
<pre>
   else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 )
   else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 )
Line 48: Line 52:
   }
   }
</pre>
</pre>
==Implementing the Menu==
 
The class CTeamMenu is nearly functional; the only thing missing is the function which handles the messages created by the panel. Open '''game_controls/teammenu.h''' to add the declaration of the new function '''OnCommand''' to the class. Find  
== Implementing the menu ==
The class <code>CTeamMenu</code> is nearly functional; the only thing missing is the function which handles the messages created by the panel. Open <code>game_controls/teammenu.h</code> to add the declaration of the new function <code>OnCommand</code> to the class. Find  
<pre>
<pre>
// command callbacks
// command callbacks
Line 57: Line 62:
  void OnCommand( const char *command );
  void OnCommand( const char *command );
</pre>
</pre>
Then add the implementation in '''game_controls/teammenu.pre''':
Then add the implementation in <code>game_controls/teammenu.cpp</code>:
<pre>
<pre>
void CTeamMenu::OnCommand( const char *command )
void CTeamMenu::OnCommand( const char *command )
Line 70: Line 75:
}
}
</pre>
</pre>
You don't see anything done here with the commands. They are passed to the baseclass where hopefully they will be employed in a useful manner, but that is beyond the scope of this tutorial.


Another thing you need to modify: Remove (comment) any lines containing "m_pMapInfo", since you don't need nor create it for that panel. Also remove the line "LoadMapPage( m_szMapName );", because there's no need to get the mapinfo, if it is not displayed.
You don't see anything done here with the commands. They are passed to the base class where hopefully they will be employed in a useful manner, but that is beyond the scope of this tutorial.


==Defining the layout==
Another thing you need to modify: Remove (comment) any lines containing "<code>m_pMapInfo</code>", since you don't need nor create it for that panel. Also remove the line "<code>LoadMapPage( m_szMapName );</code>", because there's no need to get the <code>mapinfo</code>, if it is not displayed.
 
== Defining the layout ==
{| cellpadding=6
{| cellpadding=6
|- valign="top"
|- valign="top"
Line 80: Line 86:
The layout and contents of your panel can be defined solely by a resource file. If you don't need to do customized things (e.g. displaying gamemode-dependent buttons or map information), you won't require any more to complete your menu.
The layout and contents of your panel can be defined solely by a resource file. If you don't need to do customized things (e.g. displaying gamemode-dependent buttons or map information), you won't require any more to complete your menu.


Most resources are missing in the Source SDK; the team menu resource is no exception. You need to create one yourself (or copy from any cache-file), the standard path would be '''<modname>/multimod/resource/ui/Teammenu.res'''.
Most resources are missing in the Source SDK; the team menu resource is no exception. You need to create one yourself (or copy from any cache-file), the standard path would be <code><modname>/multimod/resource/ui/Teammenu.res</code>.
I think the format is self-explanatory, and you find an example on the right.
 
It defines one panel (team), one label (joinTeam) and five buttons (jointeam1, jointeam2, jointeam3, autojoin, CancelButton)
The interesting part in the button-definition is "Command", because this defines, which command the client executes, if he presses that button.


You don't need to edit the file by hand. Valve included a nice tool with which you may change it ingame, as mentioned in [http://www.valve-erc.com/srcsdk/VGUI2/vgui2.html VGUI2 Overview] (again). Just press '''SHIFT+CTRL+ALT+B''' to open the VGUI Build-Mode editor:
It defines one panel (<code>team</code>), one label (<code>joinTeam</code>) and five buttons (<code>jointeam1, jointeam2, jointeam3, autojoin, CancelButton</code>)
The interesting part in the button-definition is "Command", because this defines which command the client executes if he presses that button.


You don't need to edit the file by hand. Valve included a nice tool with which you may change it ingame, as mentioned in [http://www.valve-erc.com/srcsdk/VGUI2/vgui2.html VGUI2 Overview] (again). Just press <code>Shift+Ctrl+ALT+B</code> to open the VGUI Build-Mode editor:


http://www.valve-erc.com/srcsdk/VGUI2/vgui2_4.jpg
|
  "Resource/UI/TeamMenu.res"
  "Resource/UI/TeamMenu.res"
  {
  {
Line 237: Line 239:
|}
|}


==Displaying the menu==
== Displaying the menu ==
The only thing missing to show the menu is a command, which invokes it. '''cl_dll/game_controls/vgui_TeamFortressViewport.pre''' already implements a function to show any panel (e.g. "showpanel team" in the console)
The only thing missing to show the menu is a command, which invokes it. <code>cl_dll/baseviewport.cpp</code> already implements a function to show any panel (e.g. "showpanel team" in the console)
  CON_COMMAND( showpanel, "Shows a viewport panel <name>" )
  CON_COMMAND( showpanel, "Shows a viewport panel <name>" )
but a teamselection-command is no big deal:
but a teamselection-command is no big deal:
Line 249: Line 251:
If you use use the command "chooseteam" in the console, this code will be executed and if the viewport was created successfully, it will show the menu.
If you use use the command "chooseteam" in the console, this code will be executed and if the viewport was created successfully, it will show the menu.


==Localization==
But maybe you will need to show this menu after player connected to the server (like in CSS).
So open hl2mp_client.cpp and find<br>
<pre>pPlayer->ShowViewPortPanel( PANEL_INFO, true, data );</pre> and add before or after it
<pre>pPlayer->ShowViewPortPanel( PANEL_TEAM, true, data );</pre><br>Now open hl2mp_player.cpp, find  <pre>void CHL2MP_Player::PickDefaultSpawnTeam( void )</pre> and replace it with this:
<pre>
void CHL2MP_Player::PickDefaultSpawnTeam( void )
{
    if ( GetTeamNumber() == 0 )
    {
        if ( HL2MPRules()->IsTeamplay() == false )
        {
            if ( GetModelPtr() == NULL )
            {
                ChangeTeam( TEAM_UNASSIGNED );
            }
        }
        else
        {
            ChangeTeam( TEAM_SPECTATOR );
        }
    }
}
</pre>
 
== Localization ==
Unlike written in the [http://www.valve-erc.com/srcsdk/VGUI2/vgui2.html VGUI2 Overview], the translation file is not loaded automatically. Here's a statement about that from the coding-list:
Unlike written in the [http://www.valve-erc.com/srcsdk/VGUI2/vgui2.html VGUI2 Overview], the translation file is not loaded automatically. Here's a statement about that from the coding-list:
<pre>
<pre>
Line 265: Line 291:
-game moddir
-game moddir


That is - run it without any qoutes, and use the relative folder name.. the
That is - run it without any quotes, and use the relative folder name.. the
same way as you would have loaded a HL1 mod.  
same way as you would have loaded a HL1 mod.  
</pre>
</pre>
For now you need to load the file yourself, for example in the viewport-constructor.
For now you need to load the file yourself, for example in the viewport-constructor.
Look into '''cl_dll\game_controls\vgui_TeamFortressViewport.pre''' and find "CBaseViewport::CBaseViewport". Then add the AddFile-function after "SetProportional":
Look into <code>cl_dll\game_controls\vgui_TeamFortressViewport.pre</code> and find "<code>CBaseViewport::CBaseViewport</code>". Then add the <code>AddFile</code> function after "<code>SetProportional</code>":


  CBaseViewport::CBaseViewport() : vgui::EditablePanel( NULL, "CBaseViewport")
  CBaseViewport::CBaseViewport() : vgui::EditablePanel( NULL, "CBaseViewport")
Line 278: Line 305:
   SetProportional( true );
   SetProportional( true );
   
   
   <b>vgui::localize()->AddFile( vgui::filesystem(), "resource/<modname>_%language%.txt" );</b>
   '''vgui::localize()->AddFile( vgui::filesystem(), "resource/<modname>_%language%.txt" );'''
   ...
   ...
  }
  }


Finally the translation file itself, '''resource/<modname>_%language%.txt'''. Be sure you save it as unicode.
Finally the translation file itself, <code>resource/<modname>_%language%.txt</code>. Be sure you save it as Unicode.


  "lang"
  "lang"
Line 298: Line 325:
  }
  }


If you take a look back at '''TeamMenu.res''', you'll find labeltexts beginning with a '#'. Such labels are replaced by their counterpart in the translation-file, only without the '#'. For example #TM_Join_Team will be replaced with "Choose a team", if your steamlanguage is set to english. For other languages you need to create additional files, like <modname>_german, <modname>_french.txt, etc.
If you take a look back at <code>TeamMenu.res</code>, you'll find labeltexts beginning with a "<code>#</code>". Such labels are replaced by their counterpart in the translation file, only without the "<code>#</code>". For example <code>#TM_Join_Team</code> will be replaced with "Choose a team", if your <code>steamlanguage</code> is set to <code>english</code>. For other languages you need to create additional files, like <code><modname>_german</code>, <code><modname>_french.txt</code>, etc.


[[Category:Tutorials]]
[[Category:Outdated programming tutorials]]
[[Category:Programming]]
[[Category:VGUI]]

Latest revision as of 01:01, 6 January 2024

Wikipedia - Letter.png
This article has multiple issues. Please help improve it or discuss these issues on the talk page. (Learn how and when to remove these template messages)
Underlinked - Logo.png
This article needs more Wikipedia icon links to other articles to help Wikipedia icon integrate it into the encyclopedia. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024
Warning.pngWarning:There is no vgui_TeamFortressViewport.pre file in latest sdk anymore, all of its functions now in Baseviewport.cpp.

The Source SDK already has most of the code you will need to create a team menu, but not everything. Valve must want us to learn by filling in the gaps.

The goal in this first step is to enable a team-selection menu, using the available code as much as possible.

Creating the viewport

First you should take a look at the VGUI2 Overview(broken link). You will learn that the base for all VGUI panels (not class-based, but where VGUI-objects are attached) is a viewport.

This viewport is created in cl_dll/clientmode_shared.cpp in the method ClientModeSharedl::InitViewport:

void ClientModeShared::InitViewport()
{
  m_pViewport = new CBaseViewport();
  m_pViewport->Start( gameuifuncs, gameeventmanager );
}

Creating the panel

Let's take a look at the CBaseViewport, which handles all the different Panels used by the mod. It is defined in cl_dll/game_controls/baseviewport.cpp.

This function creates all panels known to this viewport:

void CBaseViewport::CreateDefaultPanels( void )
{
  AddNewPanel( CreatePanelByName( PANEL_SCOREBOARD ) );
  AddNewPanel( CreatePanelByName( PANEL_INFO ) );
  AddNewPanel( CreatePanelByName( PANEL_SPECGUI ) );
  AddNewPanel( CreatePanelByName( PANEL_SPECMENU ) );
  // AddNewPanel( CreatePanelByName( PANEL_TEAM ) );
  // AddNewPanel( CreatePanelByName( PANEL_CLASS ) );
  // AddNewPanel( CreatePanelByName( PANEL_BUY ) );
}

Since you want to get the team menu, remove the comment before PANEL_TEAM.

The "real" creation is done in CBaseViewport::CreatePanelByName, a function which takes the panel name as string and creates a corresponding class. The panel names are referenced in game_shared/viewport_panel_names.h, e.g.

#define PANEL_TEAM "team"

PANEL_TEAM creates the class CTeamMenu, which contains all the functionality of that panel and is implemented in game_controls/teammenu.cpp. So uncomment that, too.

  else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 )
  {
    newpanel = new CTeamMenu( this );
  }

Implementing the menu

The class CTeamMenu is nearly functional; the only thing missing is the function which handles the messages created by the panel. Open game_controls/teammenu.h to add the declaration of the new function OnCommand to the class. Find

// command callbacks

and add

 void OnCommand( const char *command );

Then add the implementation in game_controls/teammenu.cpp:

void CTeamMenu::OnCommand( const char *command )
{
  if ( Q_stricmp( command, "vguicancel" ) )
  {
    engine->ClientCmd( const_cast<char *>( command ) );
  }
  Close();
  gViewPortInterface->ShowBackGround( false );
  BaseClass::OnCommand(command);
}

You don't see anything done here with the commands. They are passed to the base class where hopefully they will be employed in a useful manner, but that is beyond the scope of this tutorial.

Another thing you need to modify: Remove (comment) any lines containing "m_pMapInfo", since you don't need nor create it for that panel. Also remove the line "LoadMapPage( m_szMapName );", because there's no need to get the mapinfo, if it is not displayed.

Defining the layout

The layout and contents of your panel can be defined solely by a resource file. If you don't need to do customized things (e.g. displaying gamemode-dependent buttons or map information), you won't require any more to complete your menu.

Most resources are missing in the Source SDK; the team menu resource is no exception. You need to create one yourself (or copy from any cache-file), the standard path would be <modname>/multimod/resource/ui/Teammenu.res.

It defines one panel (team), one label (joinTeam) and five buttons (jointeam1, jointeam2, jointeam3, autojoin, CancelButton) The interesting part in the button-definition is "Command", because this defines which command the client executes if he presses that button.

You don't need to edit the file by hand. Valve included a nice tool with which you may change it ingame, as mentioned in VGUI2 Overview (again). Just press Shift+Ctrl+ALT+B to open the VGUI Build-Mode editor:

"Resource/UI/TeamMenu.res"
{
 "team"
{
 "ControlName"  "CTeamMenu"
 "fieldName"  "team"
 "xpos"  "100"
 "ypos"  "60"
 "wide"  "240"
 "tall"  "290"
 "autoResize"  "0"
 "pinCorner"  "0"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "0"
 "settitlebarvisible"  "0"
 "title"  "#Frame_Untitled"
}
"joinTeam"
{
 "ControlName"  "Label"
 "fieldName"  "joinTeam"
 "xpos"  "20"
 "ypos"  "20"
 "wide"  "200"
 "tall"  "30"
 "autoResize"  "0"
 "pinCorner"  "0"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "0"
 "labelText"  "#TM_Join_Team"
 "textAlignment"  "center"
 "dulltext"  "0"
 "brighttext"  "0"
 "font"  "MenuTitle"
 "wrap"  "0"
}
"jointeam1"
{
 "ControlName"  "Button"
 "fieldName"  "jointeam1"
 "xpos"  "20"
 "ypos"  "60"
 "wide"  "200"
 "tall"  "30"
 "autoResize"  "0"
 "pinCorner"  "2"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "3"
 "labelText"  "#TM_Join_Team_1"
 "textAlignment"  "west"
 "dulltext"  "0"
 "brighttext"  "0"
 "wrap"  "0"
 "Command"  "jointeam 1"
 "Default"  "0"
}
"jointeam2"
{
 "ControlName"  "Button"
 "fieldName"  "jointeam2"
 "xpos"  "20"
 "ypos"  "100"
 "wide"  "200"
 "tall"  "30"
 "autoResize"  "0"
 "pinCorner"  "2"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "4"
 "labelText"  "#TM_Join_Team_2"
 "textAlignment"  "west"
 "dulltext"  "0"
 "brighttext"  "0"
 "wrap"  "0"
 "Command"  "jointeam 2"
 "Default"  "0"
}
"jointeam3"
{
 "ControlName"  "Button"
 "fieldName"  "jointeam3"
 "xpos"  "20"
 "ypos"  "140"
 "wide"  "200"
 "tall"  "30"
 "autoResize"  "0"
 "pinCorner"  "2"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "5"
 "labelText"  "#TM_Join_Team_3"
 "textAlignment"  "west"
 "dulltext"  "0"
 "brighttext"  "0"
 "wrap"  "0"
 "Command"  "jointeam 3"
 "Default"  "0"
}
"autojoin"
{
 "ControlName"  "Button"
 "fieldName"  "autojoin"
 "xpos"  "20"
 "ypos"  "180"
 "wide"  "200"
 "tall"  "30"
 "autoResize"  "0"
 "pinCorner"  "2"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "1"
 "labelText"  "#TM_Auto_Join"
 "textAlignment"  "west"
 "dulltext"  "0"
 "brighttext"  "0"
 "wrap"  "0"
 "Command"  "jointeam 0"
 "Default"  "0"
}
"CancelButton"
{
 "ControlName"  "Button"
 "fieldName"  "CancelButton"
 "xpos"  "20"
 "ypos"  "240"
 "wide"  "200"
 "tall"  "30"
 "autoResize"  "0"
 "pinCorner"  "2"
 "visible"  "1"
 "enabled"  "1"
 "tabPosition"  "0"
 "labelText"  "#TM_Cancel"
 "textAlignment"  "center"
 "dulltext"  "0"
 "brighttext"  "0"
 "wrap"  "0"
 "Command"  "vguicancel"
 "Default"  "1"
}
}

Displaying the menu

The only thing missing to show the menu is a command, which invokes it. cl_dll/baseviewport.cpp already implements a function to show any panel (e.g. "showpanel team" in the console)

CON_COMMAND( showpanel, "Shows a viewport panel <name>" )

but a teamselection-command is no big deal:

CON_COMMAND( chooseteam, "Opens a menu for teamchoose" )
{
 if ( !gViewPortInterface )
  return;
 gViewPortInterface->ShowPanel( "team", true );
}

If you use use the command "chooseteam" in the console, this code will be executed and if the viewport was created successfully, it will show the menu.

But maybe you will need to show this menu after player connected to the server (like in CSS). So open hl2mp_client.cpp and find

pPlayer->ShowViewPortPanel( PANEL_INFO, true, data );

and add before or after it

pPlayer->ShowViewPortPanel( PANEL_TEAM, true, data );


Now open hl2mp_player.cpp, find

void CHL2MP_Player::PickDefaultSpawnTeam( void )

and replace it with this:

void CHL2MP_Player::PickDefaultSpawnTeam( void )
{
    if ( GetTeamNumber() == 0 )
    {
        if ( HL2MPRules()->IsTeamplay() == false )
        {
            if ( GetModelPtr() == NULL )
            {
                ChangeTeam( TEAM_UNASSIGNED );
            }
        }
        else
        {
            ChangeTeam( TEAM_SPECTATOR );
        }
    }
}

Localization

Unlike written in the VGUI2 Overview, the translation file is not loaded automatically. Here's a statement about that from the coding-list:

If you are loading the game via the parameters:

-game "c:\absolutepathto\moddir"

The localization file will not load.  This is because the '<moddir>_' prefix
is loaded from the -game parameter.  This is a bug in the engine, and I have
reported it.  I am told that this is being fixed.

If you wish to temporarilly get around this, copy your mod into
SteamApps/<username>/half-life 2/<moddir> and run HL2 with

-game moddir

That is - run it without any quotes, and use the relative folder name.. the
same way as you would have loaded a HL1 mod. 

For now you need to load the file yourself, for example in the viewport-constructor. Look into cl_dll\game_controls\vgui_TeamFortressViewport.pre and find "CBaseViewport::CBaseViewport". Then add the AddFile function after "SetProportional":

CBaseViewport::CBaseViewport() : vgui::EditablePanel( NULL, "CBaseViewport")
{
 gViewPortInterface = this;
 ...
 ...
 SetProportional( true );

 vgui::localize()->AddFile( vgui::filesystem(), "resource/<modname>_%language%.txt" );
 ...
}

Finally the translation file itself, resource/<modname>_%language%.txt. Be sure you save it as Unicode.

"lang"
{
 "Language" "English"
 "Tokens"
 {
  "TM_Join_Team"     "Choose a team"
  "TM_Join_Team_1"   "Join team 1"
  "TM_Join_Team_2"   "Join team 2"
  "TM_Join_Team_3"   "Join team 3"
  "TM_Auto_Join"     "Auto-select"
  "TM_Cancel"        "Cancel teamchoice"
 }
}

If you take a look back at TeamMenu.res, you'll find labeltexts beginning with a "#". Such labels are replaced by their counterpart in the translation file, only without the "#". For example #TM_Join_Team will be replaced with "Choose a team", if your steamlanguage is set to english. For other languages you need to create additional files, like <modname>_german, <modname>_french.txt, etc.