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:
this.instance.asPlayInstance()with anullcheck. The same function class is also constructed in edit sessions, where there is no run to act on.Util.parseVars(instance, message)runs the dungeon's variable placeholders through the message.- 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.