Basics
The client.dll that comes with Half-Life contains
basic support for a menuing system that should suffice for most
people. Menus are displayed and the results sent back to the user
in a series of basic steps.
- The server sends to the request to the
client telling him to display a menu
- The user selects an option
- Client sends a 'menuselect #' command to
the server
Displaying
Menus
All menu operations are performed using a single
client message called ShowMenu. This message has three
parameters. Instead of generating the message by hand every time,
it is best to create a routine to display the menu.
extern int gmsgShowMenu;
// the message # for ShowMenu
void ShowMenu (CBasePlayer *pPlayer, int bitsValidSlots, int
nDisplayTime, BOOL fNeedMore, char *pszText)
{
MESSAGE_BEGIN( MSG_ONE, gmsgShowMenu, NULL, pPlayer ->pev);
WRITE_SHORT( bitsValidSlots);
WRITE_CHAR( nDisplayTime );
WRITE_BYTE( fNeedMore );
WRITE_STRING (pszText);
MESSAGE_END();
}
pPlayer is the
client that will display the menu.
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.
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 that there is more menu information to be
sent. If fNeedmore is FALSE, then the
menu will be displayed. If it is TRUE, then the menu system
will wait for another ShowMenu message. The text of the
multiple ShowMenu messages will be concatenated. When the
client receives a ShowMenu message with fNeedMore set to FALSE,
then and only then will it display the menu. The values of
bitsValidSlots and
nDisplayTime will be ignored for all ShowMenu
messages where fNeedMore is TRUE.
Handling the
MENUSELECT Command From the Client
In the SDK, the CHalfLifeTeamplay subclass of the
CGameRules class contains a method called ClientCommand. This
method has a sample of handling the MENUSELECT command from the
client.
BOOL CHalfLifeTeamplay
:: ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
{
if ( FStrEq( pcmd, "menuselect" ) )
{
if ( CMD_ARGC() < 2 )
return TRUE;
int slot = atoi( CMD_ARGV(1) );
// select the item from the current menu
return TRUE;
}
return FALSE;
}
When the MENUSELECT command is received, the
code verifies that the slot number was specified. If it was, then
the slot number is decoded from the text. At this point the slot
number is a number from 1 to 10, where 10 is menu option 0.
After the slot number has been decoded, you
can handle the selected menu option.
Keeping Track
of What Menu Is Where
In most mods, multiple menus are
required. Thus, in order to handle the MENUSELECT command properly,
the server needs to know which menu is being displayed on the
client. The easiest thing to do is to add a new member variable to
CBasePlayer.
enum _Menu {
Menu_Radio,
Meun_ChangeTeam,
};
_Menu m_nMenu;
In the server, after the ShowMenu routine is
invoked, set the m_nMenu member variable of the player's class
instance to the menu that was displayed. Then when the MENUSELECT
command is received, the m_nMenu variable can be inspected to see
which menu was displayed.
Menu Macros
in TITLES.TXT
To reduce the amount of data sent from the server
to the client, the ShowMenu system enables the developer to use
macros to insert large amounts of menu text into the menu.
To send a sample 3 option menu to the user
without using macros, the ShowMenu call could look like this:
ShowMenu (pPlayer, 0x7,
0, 0, "Select an Option:\n\n1. Option A\n"
"2. Option B\n3. Option C");
Or, to send the same menu using TITLES.TXT,
the ShowMenu call would look like this:
ShowMenu (pPlayer, 0x7,
0, 0, "#Menu_Sample");
When the client DLL displays a menu, it
searches the menu text for the '#' character. If found, then it
extracts the name of the macro, searches the TITLES.TXT for a match,
and inserts the located text into the menu. Thus, to complete the
previous example, the TITLES.TXT for the game in question would
include the following lines. (DO NOT MODIFY TITLES.TXT in the VALVE
directory, only in your mod's directory)
Menu_Sample
{
Select an Option:
1. Option A
2. Option B
3. Option C
}
The macro system can also be used to handle
more dynamic menus. An example would be a radio options menu where
the client can turn features off. When the client requests the
radio menu, a menu string is generated dynamically to build the full
menu string.
char szMenu [512];
strcpy (szMenu, "#Menu_Radio_Head\n\n");
strcat (szMenu, "#Menu_Option1_Text");
strcat (szMenu, fOp1On ? "#Menu_Off" : "#Menu_On");
ShowMenu (...);
The contents of TITLES.TXT would include:
Menu_Radio_Head
{
Select an Option:
}
Menu_Radio_Option1_Text
{
1. Radio Sound
}
Menu_Off
{
Off
}
Menu_On
{
On
}
Accessing
Menu Items 6-0
Half-Life by default only defines the weapon slot
commands for 1-5. Thus menus that have menu items in slots 6-0 will
not work properly. To access these other menu items, you must bind
the other keys.
bind "6" "slot6"
bind "7" "slot7"
bind "8" "slot8"
bind "9" "slot9"
bind "0" "slot10"
-
Tim Smith |