Menus Explained |
BigGuy |
Reference |
Menus Explained
It seems there have been many, many questions about the menu system available
at Wavelength (by Tim Smith). This, I hope, will end all final questions with
menu messaging in Half-Life. I have put code in yellow.
What it does...
The basic menu is already present in Half-Life. It is setup like so:
1. The ShowMenu() is called and sends a message to the Client.dll
2. The client receives this message, displays the menu, and waits for input.
3. Upon selecting an option, the client sends 'menuselect #'. The # being
the option selected. This is how we know what the player has picked.
4. In the ClientCommand() (of teamplay_gamerules.cpp), you already have
the code to intercept the menuselect command. (You can also move this into
multiplay_gamerules.cpp, just remember to define it in gamerules.h)
Code from ClientCommand() *you do NOT need to add this to teamplay_gamerules.cpp*
(move this to deathmatch by copying this code, declaring ClientCommand()
in gamerules.h, and changing the appropriate CHalfLifeTeamplay)
BOOL CHalfLifeTeamplay::ClientCommand( CBasePlayer *pPlayer, const char *pcmd)
{
if ( FStrEq( pcmd, "menuselect" ) )
{
if ( CMD_ARGC() < 2 )
return TRUE;
int slot = atoi( CMD_ARGV(1) );
return TRUE;
}
return FALSE;
}
This is where the menuselect option is decoded. The slots begin with 1 (slot 1) and
go to 10 (slot 0).
Now for the actual function, ShowMenu(). The best place Ive found to put this is in
player.h This makes it accessible to ALL the gamerules, without multiple declarations.
Place this in the CBasePlayer class (make it public).
void ShowMenu(int bitsValidSlots, int nDisplayTime, BOOL fNeedMore, char *pszText);
(This is taken mostly from the original tutorial)
bitsValidSlots-is a bitmask that informs the menuing system which options in the menu
are valid. Bit 0 is option #1, Bit 1 is option #2, ..., Bit 9 is option #0. For more
about menubits, read this tutorial.
nDisplayTime-is the number of seconds that the menu will be displayed. The
maximum time is 127 seconds. If the value is between 0 and (-128) inclusive, then the
menu will not time out.
fNeedMore-informs the client whether more menu information is going to be sent. If FALSE,
the menu will be shown. If TRUE, the client will wait for a ShowMenu with fNeedMore as
FALSE before displaying the menu. The values of bitsValidSlots and nDisplayTime will be
ignored for all ShowMenu messages where fNeedMore is TRUE.
pszText-text to display.
Then in player.cpp:
void CBasePlayer::ShowMenu(int bitsValidSlots, int nDisplayTime, BOOL fNeedMore,
char *pszText)
{
MESSAGE_BEGIN( MSG_ONE, gmsgShowMenu, NULL, pev);
WRITE_SHORT( bitsValidSlots);
WRITE_CHAR( nDisplayTime );
WRITE_BYTE( fNeedMore );
WRITE_STRING (pszText);
MESSAGE_END();
}
After changing a header file, ALWAYS ALWAYS ALWAYS rebuild ALL the files.
To access the menu, use this:
(pPlayer is my pointer to the player, yours could be anything.)
pPlayer->ShowMenu ( 0x3FF, 0, 0, "This is a MENU!\n" );
Now to keep up with the currently open menu, the easiest way I see of doing this is to
create an enum listing of all the available menus and then a new CBasePlayer variable.
Add this in player.h OUTside the CBasePlayer class:
enum e_menu {
MENU_TEAM = 0,
MENU_CLASS,
MENU_RADIO,
};
(This is just an example, it should not be considered the only menu options)
Add this in player.h in the CBasePlayer class:
e_menu m_eMenu;
Now whenever you open a menu, you use this:
pPlayer->ShowMenu ( 0x3FF, 0, 0, "This is a MENU!\n" );
pPlayer->m_eMenu = MENU_TEAM;
Then in ClientCommand(), youll just add a little switch:
int slot = atoi( CMD_ARGV(1) );
// added:
switch (pPlayer->m_eMenu)
{
case MENU_TEAM:
default:
switch (slot)
{
case 1:
default:
// option one
break;
}
break;
case MENU_CLASS:
switch (slot)
{
case 1:
default:
// option one
break;
}
break;
case MENU_RADIO:
switch (slot)
{
case 1:
default:
// option one
break;
}
break;
}
// end addition
That checks for which menu is open, then does the appropriate action for the slot.
Titles.txt
Heres a good place for you to place commonly used menus, heres an example. Would you
rather have this everytime:
pPlayer->ShowMenu ( 0x3FF, 0, 0, "Menu\nOption 1\nOption 2\nOption 3\nOption 4\n
Option 5\nOption 6\nOption 7\nOption 8\nOption 9\nOption 10\n" );
or this:
pPlayer->ShowMenu ( 0x3FF, 0, 0, "#menuoptions" );
And then in titles.txt once:
menuoptions
{
Menu:
Option 1
Option 2
Option 3
Option 4
Option 5
Option 6
Option 7
Option 8
Option 9
Option 10
}
As you can see the second option would save you a lot more time (and room)
than the first.
Adding new commands
Say you want the player to open a menu to change team/class/etc. To do that youll
need to create a command in the ClientCommand() function.
At the end of the first if statement (after the closing '}')
else if (FStrEq(pcmd, "yourcommand"))
{
// do something
return TRUE; // you must return true to prevent an error message!
}
Common uses of the menu
Team Selection, class selection, weapon/ammo selection, radios, etc, etc, etc. There are
many, many uses of the menu system currently in Half-Life. For an advanced menu system,
read this tutorial. Most of the ones that do not understand this menu system will probably
have trouble with the advanced tutorial, dont forget to backup your files.
If Ive missed anything that you think should be included in this tutorial, dont hesitate in
emailing me about it. This tutorial is not meant to teach how to create a class system, team
selection system, or a weapon buying menu. Ive got tutorials for all of those.
Any questions or suggestions should be sent to me:
bigguy@valveworld.com
