Skip to content
Alexander Gottwald edited this page Dec 21, 2015 · 1 revision

Actions

Action handling in Wurm Unlimited basicly consists of two parts:

  • determining which action is available in a certain situation
  • performing the selected action over time

Available actions

When the player right clicks anything in Wurm Unlimited the server detemines which actions are available. It checks which target the user clicked (eg. creature, item, tile) and if there was an active item.

Depending on the target the corresponding Behaviour classes are selected and a getBehaviourFor() method is called which returns a list of ActionEntries. There are different getBehaviourMethods for the targets. The methods always come in pairs. One takes the active item as second parameter the other is missing that parameter but all other parameters are the same.

The returned list is presented to the player as available options.

Performing actions

Once an action was selected it is scheduled to be performed. There are a few checks before the action starts like checks for movement, blocking structures and some more.

To perform an action the action() methods of the Behaviour class is called multiple times. There are again pairs of methods to be called with or without an active item. Each action() method also has the parameter counter which gives the current second of the performed action.

The action is finished if the the action() method returns true. It will continue if the method returns false. The method will then be called again unless any other condition like movement ended the action.

Modding

For adding new actions the ModActions helper was extended.

Creating an ActionEntry

To add a new action an ActionEntry must be created. The ActionEntry distinguishes all actions. It has a name, an action number and an array of action types.

The name can be freely choosen. There is a second parameter which gives the verb for the action.

The action number must increment and the next available action number can be requested with ModActions.getNextActionId();

The available action types can be looked up from the interface com.wurmonline.server.behaviours.ActionTypes. The interface is not public so any names in there can only be used in the behaviours package. Other classes must use the numbers instead of the symbolic names.

When the ActionEntry was created it must be added to an list of ActionEntries. ModActions.registerAction() is a helper that deals with the required reflection code to add the entry.

actionId = (short) ModActions.getNextActionId();
actionEntry = ActionEntry.createEntry(actionId, "Fish", "fishing", new int[] { 6 /* ACTION_TYPE_NOMOVE */, 48 /* ACTION_TYPE_ENEMY_ALWAYS */, 36 /* ACTION_TYPE_ALWAYS_USE_ACTIVE_ITEM */});
ModActions.registerAction(actionEntry);

ModAction

providing available actions and performing the action are bound into the ModAction interface. It has the two methods getBehaviourProvider() and getActionPerformer().

An object implementing ModAction can be registered with ModActions.registerAction()

	@Override
	public void onServerStarted() {
		ModActions.registerAction(new DemoAction());
	}

getBehaviourProvider() and getActionPerformer() return null by default which make registerAction() ignore the specific parts. The object is not queried for available actions and no special action handling will happen for that object.

A ModAction can implement one or both of the interfaces. Providing only getBehaviourProvider() can add an existing action under special conditions. Providing only getActionPerformer() would replace the handling of an existing action.

The most common case however should be providing both methods.

To avoid working with nested classes it's recommended to have the implementation class implement ModAction, BehaviourProvider and ActionPerformer together and return "this" in the methods.

public class TestAction implements ModAction, BehaviourProvider, ActionPerformer {

	@Override
	public BehaviourProvider getBehaviourProvider() {
		return this;
	}

	@Override
	public ActionPerformer getActionPerformer() {
		return this;
	}

	...
}

BehaviourProvider

The getBehavioursFor() methods from Behaviour were extracted into the interface BehaviourProvider. All registered BehaviourProviders are checked for actions when a player requests the action list.

The action from the providers and from the original Behaviour class are merged and sent to the client. The default implementations return nothing.

A mod would usually implement one or more of the getBehavioursFor() methods and return a list with the created ActionEntry.

ActionPerformer

The ActionPerformer is registered for the action number returned by getActionId(). This should usually be the action number used for the ActionEntry. If the player starts the action with this number the action() methods of the ActionPerformer are called instead of the methods on the Behaviour classes. The default implementations do nothing and finish the action immediatly.

By implementing one or more of the action() methods specific behaviour for the action can be added.

The action ususally starts with a block for the condition "counter == 1.0f" where preconditions are checked and the action duration is determined. The duration should be set on the action and the client should be informed of the duration.

if (counter == 1.0f) {
    performer.getCommunicator().sendNormalServerMessage("You start the action.");
    final int time = 50; // 10th of seconds. 50 means 5 seconds
    performer.getCurrentAction().setTimeLeft(time);
    performer.sendActionControl("Performing action", true, time);
    return false;
}

There should also be a condition to check the end of the action

int time = performer.getCurrentAction().getTimeLeft();
if (counter * 10.0f > time) {
    performer.getCommunicator().sendNormalServerMessage("You finish the action.");
    return true;
}

Example

For a code example check the ActionDemo mod and specificly the classes TestAction and DemoAction.

TestAction provides a "Test entry" action on creatures which counts to 5.

DemoAction allows the player to use a branch on a tile to create a christmas tree.