|
|
|
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