Custom Conditions
| DISCLAIMER |
|---|
| The API update will only be available in Mythic Dungeons 1.3.0+! |
A condition is a gate on a trigger. A trigger can carry any number of conditions, and it only fires when every one of them passes. Conditions are where "but only if the player is holding a key" and "but only 25% of the time" live.
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 conditions.
The skeleton
Extend TriggerCondition, annotate the class with @DeclaredCondition, and implement three abstract methods.
@DeclaredCondition
public class ConditionChance extends TriggerCondition {
@SavedField private double chance = 1.0;
public ConditionChance() {
super("Chance");
}
public ConditionChance(Map<String, Object> config) {
super("Chance", config);
}
@Override
public boolean check(TriggerFireEvent event) { return true; }
@Override
public MenuButton buildMenuButton() { return null; }
@Override
public void buildHotbarMenu() { }
}
| Method | Purpose |
|---|---|
boolean check(TriggerFireEvent event) |
Your test. Return true to let the trigger through. |
MenuButton buildMenuButton() |
The item shown in the condition browser. Must be declared on this class, and must not return null. |
void buildHotbarMenu() |
The in-game options for this condition. Add items to the existing menu field; do not create a new one. |
Conditions have no category, so the constructor is usually just the super(...) call. The String is the condition's namespace, and it is what appears in the misconfiguration warnings described below.
Writing check
check is called by the trigger, once per firing attempt, for each of its conditions in order. The first false stops the trigger.
Two rules:
- Do not implement inversion yourself. Builders can flip any condition in-game with the base invert toggle. Inversion is applied to your return value for you, after
checkruns. Write the positive test and nothing else. - Do not assume there is a player.
TriggerFireEventcan be raised without one, for instance by a trigger withhasTargetset tofalse.event.getDPlayer()is thennull, andevent.getPlayer()(the Bukkit player, already null-safe) returnsnulltoo. Check before you dereference either.
The condition's own instance, trigger and location fields are populated before the first check, so you can read the running instance from check without passing anything in. location is the location of the trigger the condition is attached to.
Half-configured conditions
A condition that the builder placed but never filled in is a real situation, and returning false from check in that case would silently block the trigger forever with nothing in the console to explain it.
Override isConfigured() instead. Returning false makes the trigger skip the condition entirely (treating it as absent, not as failed) and log a warning once per dungeon run, naming the dungeon, the trigger and the block coordinates. Override misconfiguredReason() to make that warning specific:
@Override
public boolean isConfigured() {
return itemToMatch != null;
}
@Override
protected String misconfiguredReason() {
return "no item has been set on it";
}
The default isConfigured() returns true, so a condition with no required fields needs neither override.
Optional overrides
| Method | When it runs |
|---|---|
void onEnable() |
The condition is bound to its trigger and instance. |
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. |
boolean isConfigured() / String misconfiguredReason() |
As above. |
void initLegacyFields() / void initLegacyFields(Map<String, Object> config) / void initAdditionalFields() |
Field loading. Conditions may override all three, unlike functions and triggers. See Getting Started with Elements. |
init(), enable(...), disable(), initMenu() and warnMisconfigured() are final.
Conditions do not receive events. Unlike functions and triggers, condition registration does no
@EventHandlerwiring, so an event handler declared on a condition is never called. If your condition needs to observe something over time, cache it from a task started inonEnable(), and cancel that task inonDisable().
The hotbar menu
The base class has already placed two buttons before your buildHotbarMenu() runs: Back, and the invert toggle. That leaves seven slots. See GUI Menus for what to do when you need more.
A worked example
The built-in Chance condition, 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.)
@DeclaredCondition
public class ConditionChance extends TriggerCondition {
@SavedField private double chance = 1.0;
public ConditionChance(Map<String, Object> config) {
super("Chance", config);
}
public ConditionChance() {
super("Chance");
}
@Override
public boolean check(TriggerFireEvent event) {
return MathUtils.getRandomBoolean(chance);
}
@Override
public MenuButton buildMenuButton() {
MenuButton conditionButton = new MenuButton(Material.ENDER_EYE);
conditionButton.setDisplayName("&aChance");
conditionButton.addLore("&ePasses at random, with the");
conditionButton.addLore("&econfigured probability.");
return conditionButton;
}
@Override
public void buildHotbarMenu() {
menu.addMenuItem(new ChatMenuItem() {
@Override
public void buildButton() {
button = new MenuButton(Material.ENDER_EYE);
button.setDisplayName("&d&lSet Chance");
button.addLore("&eCurrent chance: &6" + chance);
}
@Override
public void onSelect(Player player) {
MessageUtils.sendMessage(player, Util.fullColorModern("&eWhat is the percent chance of this trigger running? (0.0-1.0)"));
MessageUtils.sendMessage(player, Util.fullColorModern("&eCurrent chance is: &6" + chance));
}
@Override
public void onInput(Player player, String message) {
Optional<Double> value = StringUtils.readDoubleInput(player, message);
chance = value.orElse(chance);
if (value.isPresent()) MessageUtils.sendMessage(player, Util.fullColorModern("&aSet success chance to '&6" + chance + "&a'"));
}
});
}
}
Two details worth copying:
checkis one line and has no side effects. A condition should answer a question, not change the dungeon.- The chance is read with
StringUtils.readDoubleInput, which messages the player itself on bad input and returns an emptyOptional, so the old value is kept. Do not let a typo write a broken value into a saved field.
Registering it
From your own plugin's onEnable():
MythicDungeons.inst().registerConditions("com.example.myplugin.conditions");
or, one class at a time:
MythicDungeons.inst().registerCondition(ConditionChance.class);
Add MythicDungeons to depend in your plugin.yml so it loads first. The condition browser is built once during Mythic Dungeons' startup, so register on enable: a condition registered later still works, but may not appear in the browser until the next restart. See Getting Started with Elements for the full registration rules.