Custom Functions

DISCLAIMER
The API update will only be available in Mythic Dungeons 1.3.0+!

A function is the "what happens" half of a dungeon element: send a message, open a door, hand out loot. It does nothing on its own. A trigger decides when it runs, and the function decides what happens and to whom.

This page assumes you have read Getting Started with Elements and GUI Menus, which cover the annotations, the two required constructors, the saved-field system and the hotbar menu. Everything below is specific to functions.

The skeleton

Extend DungeonFunction, annotate the class with @DeclaredFunction, and implement three abstract methods.

@DeclaredFunction
public class FunctionMessage extends DungeonFunction {

    @SavedField private String message = "DEFAULT";

    public FunctionMessage() {
        super("Message");
        targetType = FunctionTargetType.PARTY;
        setCategory(FunctionCategory.PLAYER);
    }

    public FunctionMessage(Map<String, Object> config) {
        super("Message", config);
        targetType = FunctionTargetType.PARTY;
        setCategory(FunctionCategory.PLAYER);
    }

    @Override
    public void runFunction(TriggerFireEvent triggerEvent, List<MythicPlayer> targets) { }

    @Override
    public MenuButton buildMenuButton() { return null; }

    @Override
    public void buildHotbarMenu() { }
}
Method Purpose
void runFunction(TriggerFireEvent triggerEvent, List<MythicPlayer> targets) Your actual behaviour. targets has already been resolved for you from the function's target type.
MenuButton buildMenuButton() The item shown in the function browser. Must be declared on this class, and must not return null.
void buildHotbarMenu() The in-game options for this function. Add items to the existing menu field; do not create a new one.

The String passed to super(...) is the function's namespace. It also seeds the element's display name, which is what appears on the function's in-world label in edit mode and in the listings of meta elements such as the multi-function. It is not the name players see in the function browser; that comes from the display name you set on your menu button.

Configuring the function

These are all set from the constructors. Set the same values in both constructors, since neither runs when the other does.

Call What it controls
setCategory(FunctionCategory) Which tab of the function browser your function appears in, and its display colour. One of DUNGEON, PLAYER, LOCATION, META, ROOM. The colour is taken from the category, so call this rather than setting the colour yourself.
setRequiresTrigger(boolean) Default true. A function that requires a trigger cannot be placed without one, and is given the Dungeon Start trigger automatically if the builder does not pick one. Set it to false for functions that are passive rules rather than events (the built-in Allow Block Place/Break and Door Control do this).
setRequiresTarget(boolean) Default false. When true, the target type can never be NONE.
setAllowChangingTargetType(boolean) Default true. When false, the Target Type button is left out of the hotbar menu entirely, freeing a slot. Use it when your function's target only ever makes sense one way.
setAllowRetriggerByDefault(boolean) Default false. Seeds the attached trigger's "allow retrigger" option, so a function that is naturally repeatable starts out repeatable.
targetType = FunctionTargetType.X The starting target type. Assign the field directly, as above.

Targets

runFunction is handed the list of players the function should act on. Which players those are depends on the function's FunctionTargetType:

  • NONE - no targets. The list is empty; act on the dungeon itself.
  • PLAYER - the single player attached to the trigger. The list is empty if the trigger fired without a player.
  • PARTY - that player's party members who are still in this instance; the single player alone if they have no party; every player in the instance when the trigger had no player.
  • ROOM - in a procedural dungeon, the players standing inside the room the function is placed in. Outside a procedural instance it resolves to nothing, and the ROOM option cannot even be selected outside a procedural edit session.

Before runFunction runs, Mythic Dungeons has already checked that the fire event belongs to this instance and that the firing trigger is the same class as this function's trigger. You do not need to re-check either.

If you delay: the target list is captured at fire time. If your function schedules work for later, a target may have left the dungeon by the time it runs. Re-filter with the protected helper activeTargets(targets), which returns only those still in this instance.

Optional overrides

Method When it runs
void onEnable() The function is bound to a running instance. Start timers here.
void onDisable() The instance is tearing down. Cancel here whatever you started in onEnable(), or you will leak the dungeon world. See Avoiding Memory Leaks.
void onExecute(TriggerFireEvent triggerEvent) Just before targets are resolved, on every execution. Useful for work that is not per-target.
void initLegacyFields(Map<String, Object> config) Field loading, for renamed saved fields. See Getting Started with Elements.

init(), enable(...), disable() and execute(...) are managed for you; execute is final.

A worked example

The built-in Message function, trimmed to its essentials. (The real one looks its display strings up from Mythic Dungeons' own language file with LangUtils; literal strings are used here since your plugin has no entries in that file.)

@DeclaredFunction
public class FunctionMessage extends DungeonFunction {

    @SavedField @Setter private String message = "DEFAULT";

    public FunctionMessage(Map<String, Object> config) {
        super("Message", config);
        targetType = FunctionTargetType.PARTY;
        setCategory(FunctionCategory.PLAYER);
    }
    public FunctionMessage() {
        super("Message");
        targetType = FunctionTargetType.PARTY;
        setCategory(FunctionCategory.PLAYER);
    }

    @Override
    public void runFunction(TriggerFireEvent triggerEvent, List<MythicPlayer> targets) {
        InstancePlayable instance = this.instance.asPlayInstance();
        if (instance == null) return;

        for (MythicPlayer dPlayer : targets) {
            Player player = dPlayer.getPlayer();
            String message = Util.parseVars(instance, this.message);
            MessageUtils.sendMessage(player, Util.fullColorModern(message));
        }
    }

    @Override
    public MenuButton buildMenuButton() {
        MenuButton functionButton = new MenuButton(Material.PAPER);
        functionButton.setDisplayName("&aMessage Sender");
        functionButton.addLore("&eSends a chat message");
        functionButton.addLore("&eto the target player(s).");

        return functionButton;
    }

    @Override
    public void buildHotbarMenu() {
        menu.addMenuItem(new ChatMenuItem() {
            @Override
            public void buildButton() {
                button = new MenuButton(Material.PAPER);
                button.setDisplayName("&d&lEdit Message");
            }

            @Override
            public void onSelect(Player player) {
                MessageUtils.sendMessage(player, Util.fullColorModern("&eWhat should the message say?"));
                MessageUtils.sendMessage(player, Util.fullColorModern("&eCurrent message: &6" + message));
            }

            @Override
            public void onInput(Player player, String message) {
                FunctionMessage.this.message = message;

                MessageUtils.sendMessage(player, Util.fullColorModern("&aSet message to '&6" + message + "&a'"));
            }
        });
    }
}

Three things in there are worth copying:

  1. this.instance.asPlayInstance() with a null check. The same function class is also constructed in edit sessions, where there is no run to act on.
  2. Util.parseVars(instance, message) runs the dungeon's variable placeholders through the message.
  3. The message is edited through a ChatMenuItem, not through some external command. Everything a builder can configure should be reachable from the hotbar menu.

Registering it

From your own plugin's onEnable():

    MythicDungeons.inst().registerFunctions("com.example.myplugin.functions");

or, one class at a time:

    MythicDungeons.inst().registerFunction(FunctionMessage.class);

Add MythicDungeons to depend in your plugin.yml so it loads first. See Getting Started with Elements for the full registration rules, including the two things that make registration fail loudly: a non-public no-argument constructor, and a buildMenuButton() that is inherited rather than declared on your class.

Updated Aug 19, 2026