<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.alliedmods.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=NgBUCKWANGS</id>
	<title>AlliedModders Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.alliedmods.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=NgBUCKWANGS"/>
	<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/Special:Contributions/NgBUCKWANGS"/>
	<updated>2026-06-06T23:35:02Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.6</generator>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menus_Step_By_Step_(SourceMod_Scripting)&amp;diff=10480</id>
		<title>Menus Step By Step (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menus_Step_By_Step_(SourceMod_Scripting)&amp;diff=10480"/>
		<updated>2017-10-23T10:26:55Z</updated>

		<summary type="html">&lt;p&gt;NgBUCKWANGS: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The SourceMod [[Menu API (SourceMod)|Menu API]] is fairly useful for displaying an information panel, a menu, or a vote to users.  This document talks specifically about menus and how to create one.&lt;br /&gt;
&lt;br /&gt;
== The working menu example ==&lt;br /&gt;
&lt;br /&gt;
This is the working code example that we'll be talking about in the upcoming sections.  If you need to, you can come back and look at it as you read the rest of this page.&lt;br /&gt;
&lt;br /&gt;
We will be using PrintToServer to print text to the server console as we run through the events.&lt;br /&gt;
&lt;br /&gt;
Usage of the [[Translations (SourceMod Scripting)|Translations]] system is highly recommended and will be used in this example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;#define CHOICE1 &amp;quot;#choice1&amp;quot;&lt;br /&gt;
#define CHOICE2 &amp;quot;#choice2&amp;quot;&lt;br /&gt;
#define CHOICE3 &amp;quot;#choice3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;menu_test.phrases&amp;quot;);&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	switch(action)&lt;br /&gt;
	{&lt;br /&gt;
		case MenuAction_Start:&lt;br /&gt;
		{&lt;br /&gt;
			PrintToServer(&amp;quot;Displaying menu&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		case MenuAction_Display:&lt;br /&gt;
		{&lt;br /&gt;
	 		char buffer[255];&lt;br /&gt;
			Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Vote Nextmap&amp;quot;, param1);&lt;br /&gt;
&lt;br /&gt;
			Panel panel = view_as&amp;lt;Panel&amp;gt;(param2);&lt;br /&gt;
			panel.SetTitle(buffer);&lt;br /&gt;
			PrintToServer(&amp;quot;Client %d was sent menu with panel %x&amp;quot;, param1, param2);&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		case MenuAction_Select:&lt;br /&gt;
		{&lt;br /&gt;
			char info[32];&lt;br /&gt;
			menu.GetItem(param2, info, sizeof(info));&lt;br /&gt;
			if (StrEqual(info, CHOICE3))&lt;br /&gt;
			{&lt;br /&gt;
				PrintToServer(&amp;quot;Client %d somehow selected %s despite it being disabled&amp;quot;, param1, info);&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				PrintToServer(&amp;quot;Client %d selected %s&amp;quot;, param1, info);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		case MenuAction_Cancel:&lt;br /&gt;
		{&lt;br /&gt;
			PrintToServer(&amp;quot;Client %d's menu was cancelled for reason %d&amp;quot;, param1, param2);&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		case MenuAction_End:&lt;br /&gt;
		{&lt;br /&gt;
			delete menu;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		case MenuAction_DrawItem:&lt;br /&gt;
		{&lt;br /&gt;
			int style;&lt;br /&gt;
			char info[32];&lt;br /&gt;
			menu.GetItem(param2, info, sizeof(info), style);&lt;br /&gt;
			&lt;br /&gt;
			if (StrEqual(info, CHOICE3))&lt;br /&gt;
			{&lt;br /&gt;
				return ITEMDRAW_DISABLED;&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				return style;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		case MenuAction_DisplayItem:&lt;br /&gt;
		{&lt;br /&gt;
			char info[32];&lt;br /&gt;
			menu.GetItem(param2, info, sizeof(info));&lt;br /&gt;
			&lt;br /&gt;
			char display[64];&lt;br /&gt;
			&lt;br /&gt;
			if (StrEqual(info, CHOICE3))&lt;br /&gt;
			{&lt;br /&gt;
				Format(display, sizeof(display), &amp;quot;%T&amp;quot;, &amp;quot;Choice 3&amp;quot;, param1);&lt;br /&gt;
				return RedrawMenuItem(display);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	Menu menu = new Menu(MenuHandler1, MENU_ACTIONS_ALL);&lt;br /&gt;
	menu.SetTitle(&amp;quot;%T&amp;quot;, &amp;quot;Menu Title&amp;quot;, LANG_SERVER);&lt;br /&gt;
	menu.AddItem(CHOICE1, &amp;quot;Choice 1&amp;quot;);&lt;br /&gt;
	menu.AddItem(CHOICE2, &amp;quot;Choice 2&amp;quot;);&lt;br /&gt;
	menu.AddItem(CHOICE3, &amp;quot;Choice 3&amp;quot;);&lt;br /&gt;
	menu.ExitButton = false;&lt;br /&gt;
	menu.Display(client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The translation file that goes with it... addons/sourcemod/translations/menu_test.phrases.txt&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;Phrases&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Choice 3&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;en&amp;quot;	&amp;quot;Choice Translated&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	&amp;quot;Menu Title&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;en&amp;quot;	&amp;quot;Menu Title Translated&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Step by Step ==&lt;br /&gt;
&lt;br /&gt;
=== OnPluginStart ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;menu_test.phrases&amp;quot;);&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This block is here to register the command /menu_test1 so that this menu can be tested.&lt;br /&gt;
&lt;br /&gt;
This block is beyond the scope of this page.  See: [[Introduction to SourceMod Plugins#Plugin Structure|Plugin Structure]] for OnPluginStart, [[Translations (SourceMod Scripting)|Translations]] for LoadTranslations, and [[Commands_(SourceMod_Scripting)|Commands]] for RegConsoleCmd.&lt;br /&gt;
&lt;br /&gt;
=== Menu_Test1 ===&lt;br /&gt;
&lt;br /&gt;
Menu_Test1 is a ConCmd, which is beyond the scope of this page.  See: [[Commands (SourceMod Scripting)|Commands]] for more details on that.  We're going to be talking about what's in it.&lt;br /&gt;
&lt;br /&gt;
==== Menu ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;	Menu menu = new Menu(MenuHandler1, MENU_ACTIONS_ALL);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Menu takes two arguments.&lt;br /&gt;
&lt;br /&gt;
The first is a function that matches the [https://sm.alliedmods.net/new-api/menus/MenuHandler MenuHandler] callback signature.&lt;br /&gt;
&lt;br /&gt;
The second is a bitmask of which MenuAction events we are going to handle in our MenuHandler.  There are 7 that apply to standard menus: &lt;br /&gt;
* MenuAction_Start - Called once when a menu is displayed&lt;br /&gt;
* MenuAction_Display - Called once for each client a menu is displayed to&lt;br /&gt;
* MenuAction_Select - Called when a client makes a selection from the menu that isn't Previous, Next, Back, or Exit.&lt;br /&gt;
* MenuAction_Cancel - Called when a client closes a menu or it is closed on them&lt;br /&gt;
* MenuAction_End - Called once when all clients have closed the menu&lt;br /&gt;
* MenuAction_DrawItem - Called once for each item for each user a menu is displayed to. Can change the menu item style.&lt;br /&gt;
* MenuAction_DisplayIitem. - Called once for each item for each user a menu is displayed to. Can change the menu item text.&lt;br /&gt;
&lt;br /&gt;
These will be discussed in more detail in the MenuHandler1 section.&lt;br /&gt;
&lt;br /&gt;
Of those 7, 3 are called whether you specify them or not: MenuAction_Select, MenuAction_Cancel, and MenuAction_End.&lt;br /&gt;
&lt;br /&gt;
MENU_ACTIONS_DEFAULT sets just the 3 required fields, while MENU_ACTIONS_ALL specifies all 10 actions (including the 3 vote actions).&lt;br /&gt;
&lt;br /&gt;
To specify just a few of them, you can do this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;	Menu menu = new Menu(MenuHandler1, MenuAction_Start|MenuAction_Select|MenuAction_Cancel|MenuAction_End);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== menu.SetTitle ====&lt;br /&gt;
&amp;lt;pawn&amp;gt;	menu.SetTitle(&amp;quot;%T&amp;quot;, &amp;quot;Menu Title&amp;quot;, LANG_SERVER);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sets the menu's title.  This example uses the translation system and the server's default language.  This isn't strictly necessary as we will set the menu title again later.&lt;br /&gt;
&lt;br /&gt;
==== menu.AddItem ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;	menu.AddItem(CHOICE1, &amp;quot;Choice 1&amp;quot;);&lt;br /&gt;
	menu.AddItem(CHOICE2, &amp;quot;Choice 2&amp;quot;);&lt;br /&gt;
	menu.AddItem(CHOICE3, &amp;quot;Choice 3&amp;quot;);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A menu isn't useful without something in it!&lt;br /&gt;
menu.AddItem has 2 required arguments and one optional argument.&lt;br /&gt;
&lt;br /&gt;
The 2 required arguments are:&lt;br /&gt;
&lt;br /&gt;
The Info string, and the Display string.&lt;br /&gt;
&lt;br /&gt;
The Info String is a string that is used to uniquely identify this menu item.  It is never displayed to the user, but is used in various callbacks.&lt;br /&gt;
&lt;br /&gt;
The Display String is the default text to be used for this item on the menu.  It can be changed via the menu's MenuAction_DisplayItem callback.&lt;br /&gt;
&lt;br /&gt;
The optional argument is the menu's item draw style.  It can be one of these values:&lt;br /&gt;
* ITEMDRAW_DEFAULT - Displays text as normal.&lt;br /&gt;
* ITEMDRAW_DISABLED - Item is displayed and has a number, but can't be selected.&lt;br /&gt;
* ITEMDRAW_RAWLINE - Item is displayed, but doesn't have a number assigned to it and thus can't be selected&lt;br /&gt;
* ITEMDRAW_NOTEXT - Draws an item with no text.  Not very useful.&lt;br /&gt;
* ITEMDRAW_SPACER - Item is blank, but has a number.  Can't be selected.&lt;br /&gt;
* ITEMDRAW_IGNORE - Item is blank with no number.  Can't be selected.&lt;br /&gt;
* ITEMDRAW_CONTROL - Don't use this one.  It's used for the items SourceMod automatically adds.&lt;br /&gt;
&lt;br /&gt;
Be aware that if your CreateMenu second argument includes MenuAction_DrawItem or MENU_ACTIONS_ALL and you don't actually implement the MenuAction_DrawItem callback (or implement it and don't return the current item's style if you don't change it), your style here will be completely ignored.&lt;br /&gt;
&lt;br /&gt;
==== menu.ExitButton ====&lt;br /&gt;
&amp;lt;pawn&amp;gt; 	menu.ExitButton = false;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Defaults to true.&lt;br /&gt;
&lt;br /&gt;
It set to false, the menu won't have an exit button.&lt;br /&gt;
&lt;br /&gt;
==== menu.ExitBackButton ====&lt;br /&gt;
&amp;lt;pawn&amp;gt; 	menu.ExitBackButton = false;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(This isn't in the code above, but it should be mentioned) &lt;br /&gt;
&lt;br /&gt;
Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Should only be used if the menu is a submenu, with the menu set up to check the cancel reason to see if the ExitBack action was used.&lt;br /&gt;
&lt;br /&gt;
If set to true, replaces the Exit item with the Back item.  The Back item still cancels the menu, but has a separate cancel reason.&lt;br /&gt;
&lt;br /&gt;
This can be used to dynamically set if a menu is being called as its own menu or as a submenu.&lt;br /&gt;
&lt;br /&gt;
==== menu.Display ====&lt;br /&gt;
&amp;lt;pawn&amp;gt; 	menu.Display(client, 20);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DisplayMenu takes 2 arguments:&lt;br /&gt;
&lt;br /&gt;
A Menu handle, a client index, and the amount of time in seconds to show the menu.&lt;br /&gt;
&lt;br /&gt;
If you want the menu to show forever, pass MENU_TIME_FOREVER as the third argument.&lt;br /&gt;
&lt;br /&gt;
=== MenuHandler1 ===&lt;br /&gt;
&amp;lt;pawn&amp;gt;public int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
MenuHandler1 is a function whose signature matches the MenuHandler callback.  It can be named something other than MenuHandler1. It must always be public and it will always have these arguments: Handle, MenuAction, cell, and cell in that order.&lt;br /&gt;
&lt;br /&gt;
The Handle argument is always the menu that called the handler.&lt;br /&gt;
&lt;br /&gt;
The MenuAction argument is always one of 7 actions for a standard menu.  They are:  MenuAction_Start, MenuAction_Display, MenuAction_Select, MenuAction_Cancel, MenuAction_End, MenuAction_DrawItem, and MenuAction_DisplayIitem.  We will discuss each of these as we reach their code.&lt;br /&gt;
&lt;br /&gt;
The two cell arguments meaning depend on the MenuAction argument. '''A common mistake is to assume param1 is the client.''' This is incorrect for MenuAction_Start, MenuAction_End, MenuAction_VoteEnd, MenuAction_VoteStart, and MenuAction_VoteCancel.&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_Start ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;		case MenuAction_Start:&lt;br /&gt;
		{&lt;br /&gt;
			PrintToServer(&amp;quot;Displaying menu&amp;quot;);&lt;br /&gt;
		}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* param1: not set&lt;br /&gt;
* param2: not set&lt;br /&gt;
* return: 0 (or don't return)&lt;br /&gt;
&lt;br /&gt;
MenuAction_Start doesn't set param1 and param2.  It is fired when the menu is displayed to one or more users using DisplayMenu, DisplayMenuAtItem, VoteMenu, or VoteMenuToAll.&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_Display ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;		case MenuAction_Display:&lt;br /&gt;
		{&lt;br /&gt;
	 		char buffer[255];&lt;br /&gt;
			Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Vote Nextmap&amp;quot;, param1);&lt;br /&gt;
&lt;br /&gt;
			Panel panel = view_as&amp;lt;Panel&amp;gt;(param2);&lt;br /&gt;
			panel.SetTitle(buffer);&lt;br /&gt;
			PrintToServer(&amp;quot;Client %d was sent menu with panel %x&amp;quot;, param1, param2);&lt;br /&gt;
		}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* param1: client index&lt;br /&gt;
* param2: MenuPanel Handle&lt;br /&gt;
* return: 0 (or don't return)&lt;br /&gt;
&lt;br /&gt;
MenuAction_Display is called once for each user a menu is displayed to.  param1 is the client, param2 is the MenuPanel handle.&lt;br /&gt;
&lt;br /&gt;
SetPanelTitle is used to change the menu's title based on the language of the user viewing it using the Translations system.  Previous versions of this guide suggested using SetMenuTitle.  This is a ''bad'' idea, as it changes the title globally.&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_Select ====&lt;br /&gt;
&amp;lt;pawn&amp;gt;		case MenuAction_Select:&lt;br /&gt;
		{&lt;br /&gt;
			char info[32];&lt;br /&gt;
			menu.GetInfo(param2, info, sizeof(info));&lt;br /&gt;
			if (StrEqual(info, CHOICE3))&lt;br /&gt;
			{&lt;br /&gt;
				PrintToServer(&amp;quot;Client %d somehow selected %s despite it being disabled&amp;quot;, param1, info);&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				PrintToServer(&amp;quot;Client %d selected %s&amp;quot;, param1, info);&lt;br /&gt;
			}&lt;br /&gt;
		}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* param1: client index&lt;br /&gt;
* param2: item number for use with GetMenuItem&lt;br /&gt;
* return: 0 (or don't return)&lt;br /&gt;
&lt;br /&gt;
MenuAction_Select is called when a user selects a non-control item on the menu (something added using AddMenuItem). param1 is the client, param2 is the menu position of the item the client selected.&lt;br /&gt;
&lt;br /&gt;
Using the item position to check which item was selected is a bad idea, as item position is brittle and will break things if AddMenuItem or InsertMenuItem is used.  It is recommended that you instead use the Menu item's info string, as done in the code above.&lt;br /&gt;
&lt;br /&gt;
GetMenuItem is used here to fetch the info string.&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_Cancel ====&lt;br /&gt;
&lt;br /&gt;
		case MenuAction_Cancel:&lt;br /&gt;
		{&lt;br /&gt;
			PrintToServer(&amp;quot;Client %d's menu was cancelled for reason %d&amp;quot;, param1, param2);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
* param1: client index&lt;br /&gt;
* param2: MenuCancel reason&lt;br /&gt;
* return: 0 (or don't return)&lt;br /&gt;
&lt;br /&gt;
MenuAction_Cancel is called whenever a user closes a menu or it is closed for them for another reason.  param1 is the client, param2 is the close reason.&lt;br /&gt;
&lt;br /&gt;
The close reasons you can receive are:&lt;br /&gt;
&lt;br /&gt;
* MenuCancel_Disconnected - The client got disconnected from the server.&lt;br /&gt;
* MenuCancel_Interrupted - Another menu opened, automatically closing our menu.&lt;br /&gt;
* MenuCancel_Exit - The client selected Exit. Not called if SetMenuExitBack was set to true. Not called if SetMenuExit was set to false.&lt;br /&gt;
* MenuCancel_NoDisplay - Our menu never displayed to the client for whatever reason.&lt;br /&gt;
* MenuCancel_Timeout - The menu timed out. Not called if the menu time was MENU_TIME_FOREVER.&lt;br /&gt;
* MenuCancel_ExitBack - The client selected Back. Only called if SetMenuExitBack has been called and set to true before the menu was sent. Not called if SetMenuExit was set to false.&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_End ====&lt;br /&gt;
&amp;lt;pawn&amp;gt;		case MenuAction_End:&lt;br /&gt;
		{&lt;br /&gt;
			delete menu;&lt;br /&gt;
		}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* param1: MenuEnd reason&lt;br /&gt;
* param2: If param1 is MenuEnd_Cancelled, the MenuCancel reason&lt;br /&gt;
* return: 0 (or don't return)&lt;br /&gt;
&lt;br /&gt;
MenuAction_End is called when ''all'' clients have closed a menu or vote.  For menus that are not going to be redisplayed, it is required that you call CloseHandle on the menu here.&lt;br /&gt;
&lt;br /&gt;
The parameters are rarely used in MenuAction_End.  param1 is the menu end reason. param2 depends on param1.&lt;br /&gt;
&lt;br /&gt;
The end reasons you can receive for normal menus are:&lt;br /&gt;
&lt;br /&gt;
* MenuEnd_Selected - The menu closed because an item was selected (MenuAction_Select was fired)&lt;br /&gt;
* MenuEnd_Cancelled - The menu was cancelled (MenuAction_Cancel was fired), cancel reason is in param2; cancel reason can be any of the ones listed in MenuAction_Cancel except MenuCancel_Exit or MenuCancel_ExitBack&lt;br /&gt;
* MenuEnd_Exit - The menu was exited via the Exit item (MenuAction_Cancel was fired with param2 set to MenuCancel_Exit)&lt;br /&gt;
* MenuEnd_ExitBack - The menu was exited via the ExitBack item (MenuAction_Cancel was fired with param 2 set to MenuCancel_ExitBack)&lt;br /&gt;
&lt;br /&gt;
Note: You do '''not''' have the client index during this callback, so it's far too late to do anything useful with this information.&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_DrawItem ====&lt;br /&gt;
&amp;lt;pawn&amp;gt;		case MenuAction_DrawItem:&lt;br /&gt;
		{&lt;br /&gt;
			int style;&lt;br /&gt;
			char info[32];&lt;br /&gt;
			menu.GetItem(param2, info, sizeof(info), style);&lt;br /&gt;
			&lt;br /&gt;
			if (StrEqual(info, CHOICE3))&lt;br /&gt;
			{&lt;br /&gt;
				return ITEMDRAW_DISABLED;&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				return style;&lt;br /&gt;
			}&lt;br /&gt;
		}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* param1: client index&lt;br /&gt;
* param2: item number for use with GetMenuItem&lt;br /&gt;
* return: new ITEMDRAW properties or style from GetMenuItem.  Since 0 is ITEMDRAW_DEFAULT, returning 0 clears all styles for this item.&lt;br /&gt;
&lt;br /&gt;
MenuAction_DrawItem is called once for each item on the menu for each user.  You can manipulate its draw style here. param1 is the client, param2 is the menu position.&lt;br /&gt;
&lt;br /&gt;
Using the item position to check which item was selected is a bad idea, as item position is brittle and will break things if AddMenuItem or InsertMenuItem is used.  It is recommended that you instead use the Menu item's info string, as done in the code above.&lt;br /&gt;
&lt;br /&gt;
GetMenuItem is used here to fetch the info string and menu style.&lt;br /&gt;
&lt;br /&gt;
You should return the style you want the menu item to have.  In our example, if client 1 is viewing the menu, we want CHOICE3 to be disabled.&lt;br /&gt;
&lt;br /&gt;
the return value is a bitfield, so to apply multiple styles, you do something like this:&lt;br /&gt;
&lt;br /&gt;
		return ITEMDRAW_NOTEXT | ITEMDRAW_SPACER;&lt;br /&gt;
&lt;br /&gt;
'''Failing to return the current item's style if you don't change the style is a programmer error.'''&lt;br /&gt;
&lt;br /&gt;
==== MenuAction_DisplayItem ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;		case MenuAction_DisplayItem:&lt;br /&gt;
		{&lt;br /&gt;
			char info[32];&lt;br /&gt;
			menu.GetItem(param2, info, sizeof(info));&lt;br /&gt;
			&lt;br /&gt;
			char display[64];&lt;br /&gt;
			&lt;br /&gt;
			if (StrEqual(info, CHOICE3))&lt;br /&gt;
			{&lt;br /&gt;
				Format(display, sizeof(display), &amp;quot;%T&amp;quot;, &amp;quot;Choice 3&amp;quot;, param1);&lt;br /&gt;
				return RedrawMenuItem(display);&lt;br /&gt;
			}&lt;br /&gt;
		}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* param1: client index&lt;br /&gt;
* param2: item number for use with GetMenuItem&lt;br /&gt;
* return: return value from RedrawMenuItem or 0 for no change&lt;br /&gt;
&lt;br /&gt;
MenuAction_DrawItem is called once for each item on the menu for each user.  You can manipulate its text here. param1 is the client, param2 is the menu position.&lt;br /&gt;
&lt;br /&gt;
This callback is intended for use with the Translation system.&lt;br /&gt;
&lt;br /&gt;
Using the item position to check which item was selected is a bad idea, as item position is brittle and will break things if AddMenuItem or InsertMenuItem is used.  It is recommended that you instead use the Menu item's info string, as done in the code above.&lt;br /&gt;
&lt;br /&gt;
GetMenuItem is used here to fetch the info string.&lt;br /&gt;
&lt;br /&gt;
Once we have the info string, we compare our item to it and apply the appropriate translation string.&lt;br /&gt;
&lt;br /&gt;
If we change an item, we have to call RedrawMenuItem and return the value it returns.  If we do not change an item, we must return 0.&lt;br /&gt;
&lt;br /&gt;
==== return ====&lt;br /&gt;
&amp;lt;pawn&amp;gt;	return 0;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you handle MenuAction_DrawItem or MenuAction_DisplayItem, you will get the following warning if you fail to return 0 after the switch block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;warning 209: function &amp;quot;MenuHandler1&amp;quot; should return a value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is because MenuAction_DrawItem and MenuAction_DisplayItem have return values, while the other actions only return 0.&lt;br /&gt;
&lt;br /&gt;
== The End ==&lt;br /&gt;
Hopefully this walk through a simple menu helped you understand why each call is being made where.  Menus have some options that we didn't explore here, such as disabling pagination (which is enabled by default).  You may want to refer to the main Menu documentation for more details.&lt;/div&gt;</summary>
		<author><name>NgBUCKWANGS</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourcePawn_Basics_-_Customization_through_ConVars&amp;diff=10213</id>
		<title>SourcePawn Basics - Customization through ConVars</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourcePawn_Basics_-_Customization_through_ConVars&amp;diff=10213"/>
		<updated>2016-11-05T12:25:53Z</updated>

		<summary type="html">&lt;p&gt;NgBUCKWANGS: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Prerequisite:  This guide assumes you have read [[Introduction to SourcePawn]] and [[Introduction to SourceMod Plugins]].&lt;br /&gt;
&lt;br /&gt;
Server owners like customization.  Lots of customization.  So, how do you add customization to your plugin?  The simplest route is by adding ConVars.&lt;br /&gt;
&lt;br /&gt;
== General Info ==&lt;br /&gt;
&lt;br /&gt;
Console Variables (ConVars or CVars for short) are server settings that can be changed by server owners.  Anything you put into your server's server.cfg file is a convar.&lt;br /&gt;
&lt;br /&gt;
Just like the server itself, plugins can create their own convars.  A convar is different from a regular variable in that it is visible outside your plugin.&lt;br /&gt;
&lt;br /&gt;
== Creating your own ConVars ==&lt;br /&gt;
&lt;br /&gt;
ConVars should always be created in OnPluginStart.&lt;br /&gt;
&lt;br /&gt;
When you create your ConVars, they should name them consistently.  A good naming practice is &amp;lt;tt&amp;gt;nameofplugin_settingname&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;nameofplugin_setting_name&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
ConVars are created via the CreateConVar command.  The order of arguments are:&lt;br /&gt;
&lt;br /&gt;
; name : The convar name, preferably using the naming scheme above&lt;br /&gt;
; defaultValue : A string with the default value.&lt;br /&gt;
; description : A description of what this ConVar does for the find or listcvars commands&lt;br /&gt;
; flags : An | separate list of convar flags&lt;br /&gt;
; hasMin : A bool stating whether the min argument exists&lt;br /&gt;
; min : A Float with the minimum allowed value&lt;br /&gt;
; hasMax : A bool stating whether the max argument exists&lt;br /&gt;
; min : A Float with the maximum allowed value&lt;br /&gt;
&lt;br /&gt;
=== Useful ConVar Flags ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_NONE&amp;lt;/tt&amp;gt; - The default, no flags set.&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_PROTECTED&amp;lt;/tt&amp;gt; - Used for passwords.  Don't send this value to clients who ask for it, only send 1 if it's set or 0 if it isn't.&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_SPONLY&amp;lt;/tt&amp;gt; - Not changeable in multiplayer&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_NOTIFY&amp;lt;/tt&amp;gt; - Players are informed when this ConVar changes.  ConVars marked with this attribute are also visible to tracking services.&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_NEVER_AS_STRING&amp;lt;/tt&amp;gt; - Don't try to print this out&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_DONTRECORD&amp;lt;/tt&amp;gt; - Not recorded in demo files.  Also, not recorded in &amp;lt;tt&amp;gt;AutoExecConfig&amp;lt;/tt&amp;gt; files.&lt;br /&gt;
* &amp;lt;tt&amp;gt;FCVAR_PLUGIN&amp;lt;/tt&amp;gt; - Defined by a 3rd Party plugin.  SourceMod probably adds this already even if you don't.&lt;br /&gt;
&lt;br /&gt;
== Standard ConVars ==&lt;br /&gt;
&lt;br /&gt;
There are a few standard ConVars that most plugins have.&lt;br /&gt;
&lt;br /&gt;
The first is a version ConVar.  Unlike most ConVars, you don't need to store the &amp;lt;tt&amp;gt;Handle&amp;lt;/tt&amp;gt; of this one as you never need to read its value.&lt;br /&gt;
&lt;br /&gt;
Normally, your version ConVar will look a little like this.&lt;br /&gt;
&lt;br /&gt;
  #define VERSION &amp;quot;1.0&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  public OnPluginStart()&lt;br /&gt;
  {&lt;br /&gt;
      CreateConVar(&amp;quot;test_version&amp;quot;, VERSION, &amp;quot;Test Plugin version&amp;quot;, FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_PLUGIN|FCVAR_SPONLY);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;|FCVAR_PLUGIN|FCVAR_SPONLY&amp;lt;/tt&amp;gt; is not strictly required here, but it is fairly common to see them set.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;VERSION&amp;lt;/tt&amp;gt; is defined here so that it can be used in the plugin's myinfo struct as well.&lt;br /&gt;
&lt;br /&gt;
A second common ConVar is the enable ConVar.&lt;br /&gt;
&lt;br /&gt;
  new Handle:g_hEnabled;&lt;br /&gt;
  &lt;br /&gt;
  public OnPluginStart()&lt;br /&gt;
  {&lt;br /&gt;
      g_hEnabled = CreateConVar(&amp;quot;test_enable&amp;quot;, &amp;quot;1&amp;quot;, &amp;quot;Test Plugin Enable&amp;quot;, FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Note that FCVAR_DONTRECORD is still present.  This is a recommendation in case your plugin has an &amp;lt;tt&amp;gt;AutoExecConfig&amp;lt;/tt&amp;gt; file so that your enable ConVar can be controlled by map configurations or server.cfg.&lt;br /&gt;
&lt;br /&gt;
FCVAR_NOTIFY is here as well so that GameTracker and the like can tell if the plugin is enabled.&lt;br /&gt;
&lt;br /&gt;
=== Loading ConVars from a file ===&lt;br /&gt;
&lt;br /&gt;
SourceMod has a command &amp;lt;tt&amp;gt;AutoExecConfig&amp;lt;/tt&amp;gt; to automatically read/create a ConVar configuration file.  To use it, put this at the end of OnPluginStart after your CreateConVar lines:&lt;br /&gt;
&lt;br /&gt;
      AutoExecConfig(true, &amp;quot;nameofplugin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
which will create a file named &amp;lt;tt&amp;gt;cfg/sourcemod/nameofplugin.cfg&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Locating ConVars from the game or other plugins ==&lt;br /&gt;
&lt;br /&gt;
Looking up plugins is an expensive operation and should be done just once if you can help it.&lt;br /&gt;
&lt;br /&gt;
The best location to do this is in the &amp;lt;tt&amp;gt;OnAllPluginsLoaded&amp;lt;/tt&amp;gt; callback.&lt;br /&gt;
&lt;br /&gt;
For example, here is how to look up the mp_autoteambalance ConVar&lt;br /&gt;
&lt;br /&gt;
  new Handle:g_hAutoTeamBalance;&lt;br /&gt;
  &lt;br /&gt;
  public OnAllPluginsLoaded()&lt;br /&gt;
  {&lt;br /&gt;
      g_hAutoTeamBalance = FindConVar(&amp;quot;mp_autoteambalance&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
== Reading and Setting ConVars ==&lt;br /&gt;
&lt;br /&gt;
Both ConVars from your plugin and ConVars from the game or other plugins can be read the same way.&lt;br /&gt;
&lt;br /&gt;
ConVars can be read as several different kinds of values: cell (int), bool, Float, or String.&lt;br /&gt;
&lt;br /&gt;
Here is how you read a bool ConVar:&lt;br /&gt;
&lt;br /&gt;
      new bool:value = GetConVarBool(g_hAutoTeamBalance);&lt;br /&gt;
&lt;br /&gt;
and to set the same ConVar:&lt;br /&gt;
&lt;br /&gt;
      SetConVarBool(g_hAutoTeamBalance, true);&lt;br /&gt;
&lt;br /&gt;
Int and Floats work roughly the same way with &amp;lt;tt&amp;gt;GetConVarInt&amp;lt;/tt&amp;gt; / &amp;lt;tt&amp;gt;GetConVarFloat&amp;lt;/tt&amp;gt; / &amp;lt;tt&amp;gt;SetConVarInt&amp;lt;/tt&amp;gt; / &amp;lt;tt&amp;gt;SetConVarFloat&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Strings work the same for setting, but reading a String ConVar works slightly differently:&lt;br /&gt;
&lt;br /&gt;
    decl String:value[64];&lt;br /&gt;
    GetConVarString(myConVar, value, sizeof(value));&lt;br /&gt;
&lt;br /&gt;
This pattern is common for functions that do String manipulations.&lt;br /&gt;
&lt;br /&gt;
== Catching that a ConVar changed ==&lt;br /&gt;
&lt;br /&gt;
In addition to just reading and changing ConVar values, you can also catch when a ConVar's value is changed.&lt;br /&gt;
&lt;br /&gt;
This is done through a ConVarChange hook.  ConVarChange hooks only fire when the value changes from one state to another.  So, if a ConVar is already set to 1 and a config changes it to 1 (the same value), the ConVarChange hook will '''not''' fire.&lt;br /&gt;
&lt;br /&gt;
You can add a ConVarChange hook like this:&lt;br /&gt;
&lt;br /&gt;
  new Handle:g_hEnabled;&lt;br /&gt;
  &lt;br /&gt;
  public OnPluginStart()&lt;br /&gt;
  {&lt;br /&gt;
      g_hEnabled = CreateConVar(&amp;quot;test_enable&amp;quot;, &amp;quot;1&amp;quot;, &amp;quot;Test Plugin Enable&amp;quot;, FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0);&lt;br /&gt;
      HookConVarChange(g_hEnabled, ConVar_EnableChange);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  public ConVar_EnableChange(Handle:convar, const String:oldValue[], const String:newValue[])&lt;br /&gt;
  {&lt;br /&gt;
      new enabled = GetConVarBool(g_hEnabled);&lt;br /&gt;
  &lt;br /&gt;
      if (enabled)&lt;br /&gt;
      {&lt;br /&gt;
          // We were just enabled&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
          // We were just disabled&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Note that it's quicker to use &amp;lt;tt&amp;gt;GetConVarBool&amp;lt;/tt&amp;gt;, Int, or Float instead of converting oldValue or newValue to the appropriate types.&lt;br /&gt;
&lt;br /&gt;
There is also an &amp;lt;tt&amp;gt;UnhookConVarChange&amp;lt;/tt&amp;gt;, but it is largely unnecessary as these hooks are automatically released when your plugin unloads.&lt;br /&gt;
&lt;br /&gt;
'''Make sure you hook the ConVar in the same function you Create or Find it.'''  It's also a good idea to check the return value of FindConVar before hooking it, as a game that doesn't support a ConVar will return &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reading ConVars from server.cfg ASAP ==&lt;br /&gt;
On server first launch, ConVars in server.cfg are loaded later than OnPluginStart (e.g, see &amp;lt;strong&amp;gt;Q: Can I get cvar values in OnPluginStart?&amp;lt;/strong&amp;gt; on this page http://www.sourcemod.net/devlog/?p=126). A working example of how to read from server.cfg ASAP but falling back to a default value in case it doesn't exist follows.&lt;br /&gt;
&lt;br /&gt;
    ConVar g_YourConVar;&lt;br /&gt;
    new g_YourGlobalVar;&lt;br /&gt;
    &lt;br /&gt;
    public OnPluginStart() {&lt;br /&gt;
        g_YourConVar = CreateConVar(&amp;quot;your_convar&amp;quot;, &amp;quot;your_default_value&amp;quot;);&lt;br /&gt;
        g_YourGlobalVar = g_YourConVar.IntValue;&lt;br /&gt;
        HookConVarChange(g_YourConVar, YourGlobalVarHook);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    public YourGlobalVarHook(Handle:convar, const String:oldValue[], const String:newValue[]) {&lt;br /&gt;
        g_YourGlobalVar = GetConVarInt(g_YourConVar);&lt;br /&gt;
        PrintToServer(&amp;quot;g_YourGlobalVar Has Changed: %d&amp;quot;, g_YourGlobalVar);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
[[Category:SourcePawn Basics]]&lt;/div&gt;</summary>
		<author><name>NgBUCKWANGS</name></author>
		
	</entry>
</feed>