Guide to Initiative Events

From RPTools Wiki
Jump to navigation Jump to search

Macro events are a more advanced feature, allowing Library Tokens to provide specifically-named macros that will be executed automatically when other actions are taken in MapTool. See the List of Events for all supported macro events.

Initiative Events

There are two initiative events (introduced in version 1.8) that are called when an initiative change is occurring. These events currently effect only the next/previous initiative operations (whether triggered via macro or the Panel), meaning that any mechanism for jumping directly to a specific initiative slot (the `setCurrentInitiative()` function, the `Make Current` panel action, etc.) will bypass these events. This ensures that the GM always has a way of forcing an initiative change without the need to edit any macros.

Both of these events can be present on multiple Library Tokens (unlike the otherwise similar onTokenMove). All matching handlers will be called, but there is no guarantee on their order.


The onInitiativeChangeRequest event is called first, and has the opportunity to deny the requested change by setting init.denyChange to 1. This is intended for mechanisms that need to ensure certain conditions are met before a turn ends or begins - requiring that all Action Points be expended, that the token be standing on solid ground, etc. All matching handlers will be called, and the change will be rejected if at least one denial is received. Because a change may turn out to be rejected by another handler, authors are advised to avoid any actions with side effects while handling this event.


The onInitiativeChange event is called second, and only if the change was not denied in the first step. This is intended for any turn clean-up or setup steps that should happen as a turn ends or begins - resetting a token's Action Points, updating Token States to highlight the active token, etc. When this event is called the change can no longer be rejected, so these kinds of state-changing actions are appropriate.

Event JSON details

Handlers of each of these events receive some information about the initiative change in the form of a JSON object macro parameter. This object contains details of the change - specifically the old and new initiative frame, and the direction of the change.

JSON elements
old / new round round number before / after the pending change
offset position in the initiative list (starts at 0, will be -1 if no current initiative)
initiative initiative value of the row described
holding 1 if the token is On Hold, 0 otherwise
token ID of the corresponding token, or empty string if none
direction one of NEXT, PREVIOUS, or ARBITRARY to indicate the direction of change. ARBITRARY is currently unused, as no events are triggered when jumping directly to a specific initiative slot.

Typical Example

For a typical change, the JSON might look like:

  "old":   {
    "round": 1,
    "offset": 3,
    "initiative": "14",
    "holding": 0,
    "token": "7FD82A2533C74DF3B8480F6625AB0EBC"
  "new":   {
    "round": 1,
    "offset": 4,
    "initiative": "12",
    "holding": 0,
    "token": "FF81F6365AAD4215B7E3758C36C765D1"
  "direction": "NEXT"


Are You Sure?

This is a very simple onInitiativeChangeRequest handler that makes the user confirm any attempt to move on from a PC token's turn:

[h: "<!-- onInitiativeChangeRequest -->"]
[h: jsonArgs = macro.args]
[h: oldInfo = json.get(jsonArgs, "old")]
[h: oldToken = json.get(oldInfo, "token")]
[h, if(isPC(oldToken) && json.get(jsonArgs, "direction") == "NEXT"): 
    init.denyChange = !input("junk|Are you sure?  Click OK to confirm.|ignored|LABEL|SPAN=true")]

Note: This is just an example, I will not be held responsible if you inflict this on your players.

Setting States

This example automatically updates the "CurrentTurn" state so that the token with initiative is easy to see on the map as well as the Initiative Panel. We only want this to be called if the change is definitely happening, so this is an implementation of onInitiativeChange:

[h: "<!-- onInitiativeChange -->"]
[h: jsonArgs = macro.args]
[h: oldToken= json.get(json.get(jsonArgs, "old"), "token")]
[h: newToken = json.get(json.get(jsonArgs, "new"), "token")]
[h: setState("CurrentTurn", 0, oldToken)]
[h: setState("CurrentTurn", 1, newToken)]

Action Points - A more complete example

For this example, let's assume our system uses Action Points. Each turn a token can take a combination of Actions that cost Points, and we reset those Points to their maximum at the start of their next turn. We expect players to use all their Action Points each turn, and want to ask them to confirm any attempt to end their turn before they have used all their AP.

First, the onInitiativeChangeRequest handler:

[h: "<!-- onInitiativeChangeRequest -->"]
[h: jsonArgs = macro.args]
[h: oldToken= json.get(json.get(jsonArgs, "old"), "token")]
[h: vAP = getProperty("AP", oldToken)]
[h, if(vAP > 0 && json.get(jsonArgs, "direction") == "NEXT"): 
    init.denyChange = !input("junk|You still have "+vAP+" AP remaining! Are you sure you want to end your turn?|ignored|LABEL|SPAN=true")]

And the accompanying onInitiativeChange handler to reset the AP:

[h: "<!-- onInitiativeChange -->"]
[h: newToken = json.get(json.get(jsonArgs, "new"), "token")]
[h, if(json.get(jsonArgs, "direction") == "NEXT"): resetProperty("AP", newToken)]

With both of these handlers, we take care to only prompt or update if moving to the NEXT initiative slot - we wouldn't want to reset the AP if we needed to back up to let someone use that 1 remaining point!