Skip to content

Actions

Actions are what happens when an enchantment gets activated.

Creating an action

To start, create a class that implements the interface RegistrableAction.

class MyAction : RegistrableAction {
  override val aliases = listOf<String>()

  override fun execute(event: Event, trigger: RegistrableTrigger, arguments: List<String>, target: TargetType): EventModifications? {
    return null
  }
}

Fill the aliases list with however you want to identify your action.

override val aliases = listOf("myaction", "coolaction")

The execute method is the main part of the action where the logic happens. You will notice that you are given:

  • event - an instance of the event that has happened
  • trigger - the trigger which has activated the enchantment
  • arguments - the arguments supplied for the action
  • target - the target supplied for the action

Obtaining the target

Often the target will simply be the player that has activated the enchantment, but this is not always the case, which is why you are given a target.

If you have read the triggers preamble for users, you will know that triggers provide data retrieval methods. DataRetrievalType is an enum separate from TargetType, and there's no straight way to go from one to another as of 2.2. Internally, UnderscoreEnchants uses this extension function:

fun TargetType.mapToDrt(): DataRetrievalType = when (this) {
  TargetType.FIRST_PLAYER -> DataRetrievalType.FIRST_PLAYER
  TargetType.SECOND_PLAYER -> DataRetrievalType.SECOND_PLAYER
  TargetType.THIRD_PLAYER -> DataRetrievalType.THIRD_PLAYER
  TargetType.ENTITY -> DataRetrievalType.ENTITY
  TargetType.BLOCK -> DataRetrievalType.BLOCK
  TargetType.FIRST_ITEM -> DataRetrievalType.FIRST_ITEM
  TargetType.SECOND_ITEM -> DataRetrievalType.SECOND_ITEM
}

You can copy and use it as well until a straightforward conversion is added to the API.

To obtain the target, you need to reference the trigger data holder, which is where the data retrieval methods are stored as a map of DataRetrievalType to Method. After you obtain it, invoke the method on the event and cast it to whatever type you need.

Obtaining a player, or failing if the specified target is not a player
val method = trigger.getTriggerDataHolder().dataRetrievalMethods[target.mapToDrt()]
val target = method?.invoke(event) as? Player ?: return null

Using the arguments

The arguments in actions are positional and not named. As such, you can simply index the list of arguments to obtain what you need.

if (arguments.size < 2) return null

val argument1 = arguments[0].toDoubleOrNull() ?: return null
val argument2 = arguments[1]

Returning

The execute method returns a nullable instance of EventModifications. What is that? It is a list of methods to call or fields to modify on the event. Most actions will not benefit from this and just return null. However, some actions, like the built-in cancel event, will make use of it as follows:

return EventModifications.Builder()
  // Adding a method and supplying arguments
  .addMethodToCall(event.javaClass.getDeclaredMethod("setCancelled", Boolean::class.java), listOf(true))
  .build()

Example action

Built-in ApplyVelocityAction
class ApplyVelocityAction : RegistrableAction {
  override val aliases = listOf("velocity", "apply-velocity")

  override fun execute(event: Event, trigger: RegistrableTrigger, arguments: List<String>, target: TargetType): EventModifications? {
    if (arguments.size < 3) return null

    val x = arguments[0].toDoubleOrNull() ?: return null
    val y = arguments[1].toDoubleOrNull() ?: return null
    val z = arguments[2].toDoubleOrNull() ?: return null

    val method = trigger.getTriggerDataHolder().dataRetrievalMethods[target.mapToDrt()] ?: return null
    val entity = method.invoke(event) as? Entity ?: return null

    entity.velocity = Vector(x, y, z)

    return null
  }
}