AMX Mod X has two menu systems. The old one (show_menu with a keys bitmask and register_menucmd) still compiles, but it is fiddly and caps at ten items with manual paging. The new menu API — menu_create, menu_additem, menu_display — handles paging, back/next/exit, and access control for you. It is what every modern plugin uses. This guide builds a working menu end to end and covers the one thing people forget: destroying it.
1. Create the menu
menu_create(title, handler) returns a menu id and registers the callback that receives selections. Build the menu when you are about to show it, not once at plugin load, because the item list is usually dynamic:
#include <amxmodx>
public plugin_init()
{
register_plugin("Menu Demo", "1.0", "CSB")
register_clcmd("say /tools", "cmd_tools")
}
public cmd_tools(id)
{
show_tools_menu(id)
return PLUGIN_HANDLED
}
2. Add items and display
menu_additem(menu, text, info) adds a selectable line. The info string is an opaque payload handed back to your handler when that item is picked — use it to encode what the item means:
show_tools_menu(id)
{
new menu = menu_create("\yCSB Tools\w", "tools_handler")
menu_additem(menu, "Heal to 100", "heal")
menu_additem(menu, "Give armor", "armor")
menu_additem(menu, "Teleport to spawn", "tp")
menu_display(id, menu, 0) // 0 = first page
}
Colour codes work in item text: \y yellow, \w white,
red, \d dim (used automatically for disabled items). menu_display's third argument is the page number.
3. Handle the selection
The handler receives the player, the menu, and the chosen item index. Two special values matter: MENU_EXIT when the player closes the menu, and the item index otherwise. Read the item's info string with menu_item_getinfo, act on it, then destroy the menu:
public tools_handler(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
return PLUGIN_HANDLED
}
new info[16], name[64], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info),
name, charsmax(name), callback)
if (equal(info, "heal"))
set_user_health(id, 100)
else if (equal(info, "armor"))
set_user_armor(id, 100)
else if (equal(info, "tp"))
// teleport logic here
client_print(id, print_chat, "[CSB] Teleported.")
menu_destroy(menu)
return PLUGIN_HANDLED
}
Returning PLUGIN_HANDLED tells AMXX you consumed the selection. If you want the menu to stay open after a pick, re-display it instead of destroying, but for most action menus one selection is the end.
4. Paging is automatic
Add more than seven items and the new API inserts Next and Back entries and pages them for you — no bitmask arithmetic. Slot 8 becomes Next, slot 9 Back, slot 0 Exit by default. You can change the reserved slots and labels with menu_setprop:
menu_setprop(menu, MPROP_EXITNAME, "Close") menu_setprop(menu, MPROP_BACKNAME, "Previous") menu_setprop(menu, MPROP_NEXTNAME, "More")
5. Access control and disabled items
The fourth argument to menu_additem is an access flag — the item is greyed out and unselectable for players without it. Combine with get_user_flags to build an admin menu whose entries adapt to the viewer:
menu_additem(menu, "Ban player", "ban", ADMIN_BAN) menu_additem(menu, "Slay player", "slay", ADMIN_SLAY)
A player lacking ADMIN_BAN sees "Ban player" dimmed and cannot pick it, without any check in your handler.
Common errors
- Menus stop appearing after a while — you never called
menu_destroy. Everymenu_createallocates; leaking them exhausts the menu pool and new menus silently fail. Destroy in the handler on bothMENU_EXITand every real selection. - Wrong action runs — you compared
item(a page-relative index) instead of the info string. With paging, item 0 on page 2 is not your first item. Always branch on the info string frommenu_item_getinfo. - Handler never called — the callback name passed to
menu_createdoes not match apublicfunction, or you displayed the menu to a disconnected/invalid id. - Colour codes printed literally — you escaped the backslash wrong. In Pawn a single
\yis the code; a doubled\yprints the letters. - Menu closes instantly — you returned
PLUGIN_CONTINUEfrom aregister_clcmdthat also matches another handler. ReturnPLUGIN_HANDLED.
Verification
Bind the open command, type it in chat, and confirm the menu shows with correct colours and your items. Pick each item and confirm the right action runs. Add ten items and confirm Next/Back appear and page correctly. Finally, open and close the menu a few dozen times and confirm it still opens — that is the leak test for missing menu_destroy calls. To learn the language fundamentals behind this, see Pawn basics for AMXX; for a real admin menu built on this API, look at CSB Admin Menu.









