Ingame menu for server plugins (CS:S only): Difference between revisions
| Line 145: | Line 145: | ||
MRecipientFilter filter; | MRecipientFilter filter; | ||
filter.AddRecipient(getIndexFromUserID(playerInfo->GetUserID())); | filter.AddRecipient(getIndexFromUserID(playerInfo->GetUserID())); | ||
bf_write *pBuffer = engine->UserMessageBegin( &filter, | bf_write *pBuffer = engine->UserMessageBegin( &filter, 10 ); | ||
pBuffer->WriteShort( (1<<0) | (1<<1) | (1<<2) ); //Sets how many options the menu has | pBuffer->WriteShort( (1<<0) | (1<<1) | (1<<2) ); //Sets how many options the menu has | ||
pBuffer->WriteChar( -1 ); //Sets how long the menu stays open -1 for stay until option selected | pBuffer->WriteChar( -1 ); //Sets how long the menu stays open -1 for stay until option selected | ||
Revision as of 15:30, 15 April 2006
It should be noted, before you start, that this method is mod dependent and that this menu-system is not enabled in the standard SDK. This menu only seems to function in Counter-Strike: Source. Use of the simpler IServerPluginHelpers interface is preferred.
The ingame menu is a usermessage so we will need to include src/tier1/bitbuf.cpp into the project. This needs to be added to the project and then bitbuf.h needs to be included in serverplugin_empty.cpp
We will also need a Recipient Filter, you can use the 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();
for ( int 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 );
}
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
Usage
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 maxplayers=clientMax; 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 );
pBuffer->WriteShort( (1<<0) | (1<<1) | (1<<2) ); //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 contains a number of bits which tell the game which options are enabled for the menu.
- (1<<0) enables 1
- (1<<1) enables 2
- etc..
To enable 4 and 7, one would use:
(1<<3) | (1<<6)
If you have done this correcly so far then you should have the menu show up in game when the client types a_menu.
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);
switch(atoi(parameter))
{
case 1:
engine->ClientCommand (pEntity, "buy m4a1;buy ak47;buy primammo;buy vesthelm;buy deagle;buy secammo;buy flashbang;buy hegrenade;buy flashbang\n");
break;
case 2:
engine->ClientCommand (pEntity, "buy awp;buy primammo;buy vesthelm;buy deagle;buy secammo;buy flashbang;buy hegrenade;buy flashbang\n");
break;
case 3:
engine->ClientPrintf(pEntity, "Menu Exited");
break;
}
return PLUGIN_STOP;
}
This will make the client run the commands to buy weapons ammo armor and grenades. The menu runs the commands.