G U I Menus
| DISCLAIMER |
|---|
| The following is a feature only available in Mythic Dungeons 1.3.0+! |
In order to make dungeon elements easy to customize and edit in-game, Mythic Dungeons uses both a chest-based GUI for selecting elements, and a hotbar-based menu for modifying options on those elements.
Browser Menu Buttons
In order for your custom elements to even be accessible by server owners, you must first create a menu button for it. This is done through the provided buildMenuButton() method.
@Override
public MenuButton buildMenuButton() {
// Create a new menu button that uses the paper item as its base.
MenuButton functionButton = new MenuButton(Material.PAPER);
// Set the display name of the item.
functionButton.setDisplayName("&aMessage Sender");
// Add some lore to describe the function.
functionButton.addLore("&eSends a chat or action bar");
functionButton.addLore("&emessage to the target player(s).");
// Finally, return the button you've created so the menu can use it.
return functionButton;
}
This code produces this menu button.

MenuButton reference
MenuButton wraps a single ItemStack. The same class is used for browser buttons and for hotbar buttons.
| Member | What it does |
|---|---|
new MenuButton(Material mat) |
Build from a material. |
new MenuButton(ItemStack item) |
Build from an existing item, so you can pre-set NBT, custom model data, and so on. |
void setDisplayName(String name) |
Set the name. Colour codes (&a, hex) are parsed for you. |
void addLore(String line) |
Append one lore line, colourised. |
void setLoreLine(int index, String line) |
Replace an existing lore line. |
void removeLore(int index) |
Delete one lore line. |
void clearLore() |
Delete all lore. |
void setAmount(int amount) |
Set the stack size. The item is clamped to 1-99. |
void setEnchanted(boolean enchanted) |
Apply or remove the enchant glow. Very handy for showing a toggle's state. |
ItemStack getItem() |
The finished item. |
String getDisplayName() / int getAmount() / boolean isEnchanted() |
Read the current state. getDisplayName() gives the already-colourised name, and getAmount() gives the raw value you passed, not the clamped one. |
Caution:
setLoreLineis meant to log and swallow an out-of-range index, but its guard catches the wrong exception type and never fires. Calling it with an index past the end of the lore throws out ofbuildButton(), which aborts the whole menu build and leaves the player's hotbar half-drawn. CalladdLoreuntil the line exists, or rebuild the lore withclearLore()andaddLore(...).
The Hotbar Menu
Hotbar menus take up a lot of space in the class, however they are relatively straightforward! Simply populate the buildHotbarMenu() method with options and Mythic Dungeons will do the rest. Let's break it down...
For every option you want to have in the hotbar menu, you need to call menu.addMenuItem, and then provide details about how your button will behave. There are a couple pre-made menu item types to help simplify the process, or you can make your own.
Do not assign
menuyourself. The base-class javadoc onbuildHotbarMenu()tells you to "Assignmenuwith a new HotbarMenu object". That instruction is out of date. By the time yourbuildHotbarMenu()is called,menuhas already been created and the element's base buttons have already been added to it. Just callmenu.addMenuItem(...). Creating a newHotbarMenuhere throws away the Back button and everything else the base class put there.
Nine slots, and no more
A hotbar has nine slots, and each menu item takes one. Items are placed in the order you add them, starting at slot 0, and anything past slot 8 is silently dropped: the button never appears, and its option becomes unreachable in-game.
Your element's base class has already used some of those slots before buildHotbarMenu() runs:
| Element | Base buttons | Slots left for you |
|---|---|---|
| Function | Back, Trigger, Target Type | 6 |
Function with setAllowChangingTargetType(false) |
Back, Trigger | 7 |
| Trigger | Back, Trigger Conditions | 7 |
| Condition | Back, invert toggle | 7 |
Calling addRoomLimitToggleButton() in a trigger's buildHotbarMenu() costs one more slot.
If you need more options than that, build a second HotbarMenu with HotbarMenu.create() and add a menu item that switches the player onto it with MythicDungeons.inst().getMythicPlayer(player).setHotbar(subMenu). Switching menus automatically pushes the menu the player was on onto their menu stack, so a Back button (aPlayer.previousHotbar()) returns to it. The optional second argument, setHotbar(subMenu, true), additionally moves the player's selected slot back to the first button. That is how the rewards functions handle large option sets.
Always create menus with the static
HotbarMenu.create(), nevernew HotbarMenu().HotbarMenuis abstract, andcreate()picks the Paper-specific subclass when the server supports it.
MenuItem
The default menu entry. All it requires is that you provide a button and some code to run when the button is selected!
menu.addMenuItem(new MenuItem() {
// Build the hotbar menu button. Works similarly to the function's menu button.
// You can also customize the quantity of the item, and whether it's enchanted!
@Override
public void buildButton() {
button = new MenuButton(Material.KNOWLEDGE_BOOK);
button.setDisplayName("&d&lExample Option");
}
// What to do when the button is selected with right-click.
@Override
public void onSelect(PlayerEvent event) {
Player player = event.getPlayer();
player.sendMessage("Player has selected this button!");
}
});
The hooks you can override
buildButton() and onSelect(PlayerEvent) are abstract, so you must implement both. The rest are optional and empty by default.
| Hook | Fires when |
|---|---|
void buildButton() |
The menu is drawn or redrawn. Assign the button field here. Called on every redraw, so this is where a button's lore or glow reflects the option's current value. |
void onSelect(PlayerEvent event) |
The player right-clicks while holding this slot. |
void onClick(PlayerEvent event) |
The player left-clicks while holding this slot. Empty by default, so a button without an override simply does nothing on left-click. Useful for a second action on the same button, for example right-click to cycle forwards and left-click to cycle backwards. |
void onChat(AsyncPlayerChatEvent event) |
The player sends a chat message while this slot is selected and chat listening is on. ChatMenuItem implements this for you. |
void onHover(PlayerItemHeldEvent event) |
The player scrolls onto this slot. |
void onUnhover(PlayerItemHeldEvent event) |
The player scrolls off this slot. |
The menu is rebuilt after every select, click and chat input, so a button's appearance updates itself without any extra work from you.
Caution: there is a matching set of
addSelectAction/addClickAction/addChatAction/addHoverActionmethods for attaching extra callbacks without subclassing. AvoidaddHoverAction: the unhover dispatch runs the hover action list, so an action added this way also fires when the player scrolls away. The overridableonUnhover(...)hook above is unaffected and works correctly.
ChatMenuItem
Chat menu items add an extra option for accepting chat input. Otherwise, they work the same as a normal menu button.
menu.addMenuItem(new ChatMenuItem() {
@Override
public void buildButton() {
button = new MenuButton(Material.MAP);
button.setDisplayName("&d&lEdit Message");
}
@Override
public void onSelect(Player player) {
player.sendMessage(Util.colorize(debugPrefix + "&eWhat should the message say?"));
player.sendMessage(Util.colorize(debugPrefix + "&eCurrent message: &6" + message));
}
// What to do when the player sends a chat message after selecting this button.
@Override
public void onInput(Player player, String message) {
FunctionMessage.this.message = message;
player.sendMessage(Util.colorize(debugPrefix + "&aSet message to '&6" + message + "&a'"));
}
});
Note the two different onSelect signatures. ChatMenuItem implements onSelect(PlayerEvent) itself (that is where it turns chat listening on) and gives you a simpler onSelect(Player) to override instead. Use onSelect(Player) to prompt the player for what you want, and onInput(Player, String) to consume their answer.
onInput runs back on the main thread, and the player's menu is redrawn straight afterwards, so you can set fields directly without scheduling anything yourself. The message you receive has already had its colour codes parsed.
If a select should not start listening for chat (say the button is currently disabled), call setCancelled(true) from inside onSelect(Player). The flag is reset at the start of every select.
ToggleMenuItem
For an on/off option, ToggleMenuItem saves you the redraw. It gives you the same simplified onSelect(Player), and refreshes the player's hotbar for you once your code returns, so the button immediately shows its new state.
menu.addMenuItem(new ToggleMenuItem() {
@Override
public void buildButton() {
button = new MenuButton(Material.REDSTONE_TORCH);
button.setDisplayName("&d&lCase Sensitive");
button.setEnchanted(caseSensitive); // the glow shows the current state
}
@Override
public void onSelect(Player player) {
caseSensitive = !caseSensitive;
}
});
Pairing setEnchanted(...) in buildButton() with the flip in onSelect(...) is the standard pattern, and is what every built-in toggle does.
```java
@Override
public void buildHotbarMenu() {
// First, we add our example menu item.
menu.addMenuItem(new MenuItem() {
@Override
public void buildButton() {
button = new MenuButton(Material.KNOWLEDGE_BOOK);
button.setDisplayName("&d&lExample Option");
}
@Override
public void onSelect(PlayerEvent event) {
Player player = event.getPlayer();
player.sendMessage("Player has selected this button!");
}
});
// Then, we add our chat menu item. These will appear in the hotbar in the order we add them.
menu.addMenuItem(new ChatMenuItem() {
@Override
public void buildButton() {
button = new MenuButton(Material.MAP);
button.setDisplayName("&d&lEdit Message");
}
@Override
public void onSelect(Player player) {
player.sendMessage(Util.colorize(debugPrefix + "&eWhat should the message say?"));
player.sendMessage(Util.colorize(debugPrefix + "&eCurrent message: &6" + message));
}
@Override
public void onInput(Player player, String message) {
FunctionMessage.this.message = message;
player.sendMessage(Util.colorize(debugPrefix + "&aSet message to '&6" + message + "&a'"));
}
});
}
```
Hiding a menu item
A menu item can remove itself from the bar. Leave button as null in buildButton() and the slot is drawn empty instead:
menu.addMenuItem(new ToggleMenuItem() {
@Override
public void buildButton() {
// Only offer this option in a procedural dungeon.
if (instance.as(InstanceEditableProcedural.class) == null) {
button = null;
return;
}
button = new MenuButton(Material.STRUCTURE_BLOCK);
button.setDisplayName("&d&lLimit To Room");
}
@Override
public void onSelect(Player player) {
// ...
}
});
Because buildButton() runs on every redraw, an item can appear and disappear as the element's state changes. The slot is still reserved either way, so a hidden item still counts against the nine.
HotbarMenu reference
You will mostly use addMenuItem, but the rest of the surface is small:
| Member | What it does |
|---|---|
static HotbarMenu create() |
Build a menu. Always use this rather than the constructor. |
void addMenuItem(MenuItem item) |
Append to the next free slot. |
void addMenuItem(int slot, MenuItem item) |
Place at an explicit slot (0-8). |
void removeMenuItem(int slot) |
Drop the item at that slot. |
HashMap<Integer, MenuItem> getMenuItems() |
The items, keyed by slot. |
void buildMenu() |
Redraw every button. Called for you after a select, click or chat input. |
void updateMenu(MythicPlayer aPlayer) |
Redraw and push the result to that player's hotbar. |
int getSelected() |
The slot the player last acted on. |
While it may look complicated, this system allows for an enormous amount of flexibility and power when creating menus and customizable options. If you're really clever, you can even create nested hotbar menus and hide-able menu items, such as what's seen in the rewards functions! Play around and get creative!