Ingame menu for server plugins (CS:S only)
This page from sourceWiki
The ingame menu is a usermessage so we will need to include bitbuf.cpp into the project. This can be found at src/tier1/bitbuf.cpp This needs to be added to the project and have
#include "bitbuf.h"
in the includes at the top of serverplugin_empty.cpp
We will also need a Recipient Filter, I have used one from mosca.br at HL2Coding
MRecipientFilter.cpp
#include "MRecipientFilter.h" #include "interface.h" #include "filesystem.h" #include "engine/iserverplugin.h" #include "dlls/iplayerinfo.h" #include "eiface.h" #include "igameevents.h" #include "convar.h" #include "Color.h" #include "shake.h" #include "IEffects.h" #include "engine/IEngineSound.h" extern IVEngineServer *engine; extern IPlayerInfoManager *playerinfomanager; extern IServerPluginHelpers *helpers; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" MRecipientFilter::MRecipientFilter(void) { } MRecipientFilter::~MRecipientFilter(void) { } int MRecipientFilter::GetRecipientCount() const { return m_Recipients.Size(); } int MRecipientFilter::GetRecipientIndex(int slot) const { if ( slot < 0 || slot >= GetRecipientCount() ) return -1; return m_Recipients[ slot ]; } bool MRecipientFilter::IsInitMessage() const { return false; } bool MRecipientFilter::IsReliable() const { return false; } void MRecipientFilter::AddAllPlayers(int maxClients) { m_Recipients.RemoveAll(); int i; for ( i = 1; i <= maxClients; i++ ) { edict_t *pPlayer = engine->PEntityOfEntIndex(i); if ( !pPlayer || pPlayer->IsFree()) { continue; } //AddRecipient( pPlayer ); m_Recipients.AddToTail(i); } } void MRecipientFilter::AddRecipient( int iPlayer ) { // Already in list if ( m_Recipients.Find( iPlayer ) != m_Recipients.InvalidIndex() ) return; m_Recipients.AddToTail( iPlayer ); }
and
MRecipientFilter.h
#ifndef _MRECIPIENT_FILTER_H #define _MRECIPIENT_FILTER_H #include "irecipientfilter.h" #include "bitvec.h" #include "tier1/utlvector.h" class MRecipientFilter : public IRecipientFilter { public: MRecipientFilter(void); ~MRecipientFilter(void); virtual bool IsReliable( void ) const; virtual bool IsInitMessage( void ) const; virtual int GetRecipientCount( void ) const; virtual int GetRecipientIndex( int slot ) const; void AddAllPlayers( int maxClients ); void AddRecipient (int iPlayer ); private: bool m_bReliable; bool m_bInitMessage; CUtlVector< int > m_Recipients; }; #endif
These both need adding the project and have the line in the includes at the top or serverplugin_empty.cpp
#include "MRecipientFilter.h"
We also need a function to get the clients index from their userid.
int getIndexFromUserID(int userid) { edict_t *player; IPlayerInfo *info; for(int i = 1; i <= maxplayers; i++) //int maxplayers; has to be added after the includes and clientMax=maxplayers; in the ServerActivate function { player = engine->PEntityOfEntIndex(i); if(!player || player->IsFree() ) continue; info = playerinfomanager->GetPlayerInfo(player); if(info->GetUserID() == userid) return i; } return -1; }
I have used made the menu as a client command activated on the command a_menu.
if ( FStrEq( pcmd, "a_menu" ) ) { IPlayerInfo *playerInfo = playerinfomanager->GetPlayerInfo(pEntity); MRecipientFilter filter; filter.AddRecipient(getIndexFromUserID(playerInfo->GetUserID())); bf_write *pBuffer = engine->UserMessageBegin( &filter, 10 ); // coriged by Jeanlepail (changed 9 to 10) pBuffer->WriteShort( 7 ); //Sets how many options the menu has pBuffer->WriteChar( -1 ); //Sets how long the menu stays open -1 for stay until option selected pBuffer->WriteByte( false ); pBuffer->WriteString( "1.Assault Rifle\n2.AWP\n3.Exit" ); //The text shown on the menu engine->MessageEnd(); return PLUGIN_STOP; }
This menu will be an alternative buy menu that will buy ammo, armor and grenades, as well as the primary weapon.
The writeshort number is 7 dispite having 3 options because it goes up in powers of 2
1 option: 2^0 = 1
2 options: 2^0+2^1 = 3
3 options: 2^0+2^1+2^2 = 7
If you have n options (n = 1, n = 2 and n = 3 above), you can calculate the writeshort number as 2^n - 1.
If you have done this correcly so far then you should have the menu show up in game when the client types a_menu but this menu doesnt do anything.
The menu run the command menuselect with the parameter as the option selected, so for this menu the commands would be menuselect 1 menuselect 2 and menuselect 3
else if ( FStrEq( pcmd, "menuselect" ) ) { const char *parameter = engine->Cmd_Argv(1); if ( FStrEq( parameter, "1" ) ) { engine->ClientCommand (pEntity, "buy m4a1;buy ak47;buy primammo;buy vesthelm;buy deagle;buy secammo;buy flashbang;buy hegrenade;buy flashbang"); } else if ( FStrEq( parameter, "2" ) ) { engine->ClientCommand (pEntity, "buy awp;buy primammo;buy vesthelm;buy deagle;buy secammo;buy flashbang;buy hegrenade;buy flashbang"); } else if ( FStrEq( parameter, "3" ) ) { engine->ClientPrintf(pEntity, "Menu Exited") } return PLUGIN_STOP; }
This will make the client run the commands to buy weapons ammo armor and grenades. The menu runs the commands.
Summary
All being right this should work, the whole code should be
int getIndexFromUserID(int userid) { edict_t *player; IPlayerInfo *info; for(int i = 1; i <= maxplayers; i++) { player = engine->PEntityOfEntIndex(i); if(!player || player->IsFree() ) continue; info = playerinfomanager->GetPlayerInfo(player); if(info->GetUserID() == userid) return i; } return -1; } //--------------------------------------------------------------------------------- // Purpose: called when a client types in a command (only a subset of commands however, not CON_COMMAND's) //--------------------------------------------------------------------------------- PLUGIN_RESULT CEmptyServerPlugin::ClientCommand( edict_t *pEntity ) { const char *pcmd = engine->Cmd_Argv(0); if ( !pEntity || pEntity->IsFree() ) { return PLUGIN_CONTINUE; } if ( FStrEq( pcmd, "a_menu" ) ) { IPlayerInfo *playerInfo = playerinfomanager->GetPlayerInfo(pEntity); MRecipientFilter filter; filter.AddRecipient(getIndexFromUserID(playerInfo->GetUserID())); bf_write *pBuffer = engine->UserMessageBegin( &filter, 10 ); // edited by Jeanlepail (changed 9 to 10) pBuffer->WriteShort( 7 ); pBuffer->WriteChar( -1 ); pBuffer->WriteByte( false ); pBuffer->WriteString( "1.Assault Rifle\n2.AWP\n3.Exit" ); engine->MessageEnd(); return PLUGIN_STOP; } else if ( FStrEq( pcmd, "menuselect" ) ) { const char *parameter = engine->Cmd_Argv(1); if ( FStrEq( parameter, "1" ) ) { engine->ClientCommand (pEntity, "buy m4a1;buy ak47;buy primammo;buy vesthelm;buy deagle;buy secammo;buy flashbang;buy hegrenade;buy flashbang"); } else if ( FStrEq( parameter, "2" ) ) { engine->ClientCommand (pEntity, "buy awp;buy primammo;buy vesthelm;buy deagle;buy secammo;buy flashbang;buy hegrenade;buy flashbang"); } else if ( FStrEq( parameter, "3" ) ) { engine->ClientPrintf(pEntity, "Menu Exited"); } return PLUGIN_STOP; } return PLUGIN_CONTINUE; }