Ingame menu for server plugins (CS:S only): Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (typos & tidy)
Line 5: Line 5:
== Creating an in-game menu for server plugins ==
== Creating an in-game menu for server plugins ==


The ingame menu is a usermessage so we will need to include <code>src/tier1/bitbuf.cpp</code> into the project. This needs to be added to the project and then bitbuf.h needs to be included in <code>serverplugin_empty.cpp</code>
The in-game menu is a usermessage so we will need to include <code>src/tier1/bitbuf.cpp</code> into the project. This needs to be added to the project and then bitbuf.h needs to be included in <code>serverplugin_empty.cpp</code>


We will also need a Recipient Filter, you can use the one from mosca.br at [http://www.hl2coding.com/forums/viewtopic.php?t=31 HL2Coding]
We will also need a Recipient Filter, you can use the one from mosca.br at [http://www.hl2coding.com/forums/viewtopic.php?t=31 HL2Coding]


===MRecipientFilter.cpp===
=== MRecipientFilter.cpp ===
<pre>
<pre>
#include "MRecipientFilter.h"
#include "MRecipientFilter.h"
Line 84: Line 84:
}
}
</pre>
</pre>
===MRecipientFilter.h===
 
=== MRecipientFilter.h ===
<pre>
<pre>
#ifndef _MRECIPIENT_FILTER_H
#ifndef _MRECIPIENT_FILTER_H
Line 114: Line 115:
#endif
#endif
</pre>
</pre>
===Usage===
 
=== Usage ===
These both need adding the project and have the line in the includes at the top or <code>serverplugin_empty.cpp</code>
These both need adding the project and have the line in the includes at the top or <code>serverplugin_empty.cpp</code>
  #include "MRecipientFilter.h"
  #include "MRecipientFilter.h"
We also need a function to get the clients index from their userid.
We also need a function to get the clients index from their userid.


'''Edit: '''This is not actualy needed, valve have alredy got a function: engine->IndexOfEdict(edict_t *)
'''Edit: '''This is not actually needed, valve have already got a function: engine->IndexOfEdict(edict_t *)
<pre>int getIndexFromUserID(int userid)
<pre>int getIndexFromUserID(int userid)
{
{
Line 147: Line 149:
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
                 No need what so ever of The Custom function Valve have there own
                 engine->IndexOfEdict(pEntity);
                 engine->IndexOfEdict(pEntity);
                 */
                 */
Line 202: Line 204:
This will make the client run the commands to buy weapons ammo armor and grenades. The menu runs the commands.
This will make the client run the commands to buy weapons ammo armor and grenades. The menu runs the commands.


[[category:Tutorials]][[category:Programming]]
[[Category:Tutorials]]
[[Category:Programming]]

Revision as of 06:37, 9 February 2008

Broom icon.png
This article or section should be converted to third person to conform to wiki standards.

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.


Creating an in-game menu for server plugins

The in-game 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.

Edit: This is not actually needed, valve have already got a function: engine->IndexOfEdict(edict_t *)

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

Making the menu

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

Making commands for the 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.