Ingame menu for server plugins (CS:S only): Difference between revisions
| Line 142: | Line 142: | ||
| 	if ( FStrEq( pcmd, "a_menu" ) ) | 	if ( FStrEq( pcmd, "a_menu" ) ) | ||
| 	{ | 	{ | ||
| 		IPlayerInfo *playerInfo = playerinfomanager->GetPlayerInfo(pEntity);   | 		/*IPlayerInfo *playerInfo = playerinfomanager->GetPlayerInfo(pEntity);   | ||
| 		MRecipientFilter filter; | 		MRecipientFilter filter; | ||
| 		filter.AddRecipient(getIndexFromUserID(playerInfo->GetUserID())); | 		filter.AddRecipient(getIndexFromUserID(playerInfo->GetUserID())); | ||
|                 No need what so ever of The Cusom function Valve have there own | |||
|                 engine->IndexOfEdict(pEntity); | |||
|                 */ | |||
| 		MRecipientFilter filter; | |||
| 		filter.AddRecipient(engine->IndexOfEdict(pEntity)); | |||
| 		bf_write *pBuffer = engine->UserMessageBegin( &filter, 10 ); | 		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 | ||
Revision as of 15:37, 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()));
                No need what so ever of The Cusom function Valve have there own
                engine->IndexOfEdict(pEntity);
                */
		MRecipientFilter filter;
		filter.AddRecipient(engine->IndexOfEdict(pEntity));
		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.