Skip to content

Instantly share code, notes, and snippets.

@CherryDT
Last active October 16, 2018 23:51
Show Gist options
  • Save CherryDT/95630b80d4e0c66d76541256f8aecce2 to your computer and use it in GitHub Desktop.
Save CherryDT/95630b80d4e0c66d76541256f8aecce2 to your computer and use it in GitHub Desktop.
RM2k(3) Event Interpreter Behavior and Order of Event Execution

RM2k(3) Event Interpreter Behavior and Order of Event Execution

(I originally posted this in this comment and slightly updated it now with information regarding events acting earlier than expected.)

As I understand the engine, it is "supposed" to work like this:

Workers

The RM has workers for executing events. A worker is either a generic worker which will try to execute whatever event is waiting for being executed, or a parallel process worker which runs one specific event over and over. (Or a battle worker, but let's leave that aside for now).

Every worker can be imagined as a thread which has some program running, a stack, registers (e.g. wait counter) etc. When a worker is asked to run, it will run in a loop until 10000 iterations or execution is suspended (e.g. a command asked to wait a certain amount if frames), whatever is earlier. A generic worker will, as soon as one event is finished, try to find the next waiting event and start it (without leaving the loop). A parallel process worker will quit the loop once the end of the script is reached (this is why the "invisible Wait 0.0" seems to exist at the end of parallel process events).

Every event (map and common) has its own parallel process worker which is used in case the event is set as parallel process (note that such a worker's script may call into another event, which means that a worker associated with an event can't always be assumed to run that exact event - and that's also why turning of a switch of a parallel process event will stop execution even if execution is currently inside another event's script, because the switch suspends the worker and not its original script). Additionally, there is one global worker for "foreground" events which are autostart or activated by action button or touch.

Flags

Events have a "waiting" flag which indicates that they would be waiting to get foreground execution. This flag is implemented slightly different for map events and common events: For map events, it's an actual flag which is set and reset, while for common events, it's a property getter which returns true if the event is configured as autostart unless it's empty or the switch condition is not met.

There is also an "already acted" flag (which is used to prevent events from triggering twice on the same frame).

Execution

Every frame on the map, the engine will:

  • Reset the "already acted" flag for all events on the map
  • Ask all common events' parallel process workers to run
  • Ask all map events' parallel process workers to run
  • Ask all map events (including vehicles and party) to act (unless its "already acted" flag is set):
    • Acting is taking an action like moving (for example initiating the next step of an active move route), but it will also check if the event is autostart and the start conditions are met, in this case its "waiting" flag is set. The "already acted" flag is also set. (If an event is triggered in another way, for example by action button, it would also get its "waiting" flag set.)
    • Important: Whenever any event (including vehicles and party) checks whether another tile would be passable for it (most commonly when attempting to move onto that tile), this will check if another event is already occupying the destination tile and ask it to act immediately. Since the coordinates of an event during movement are always the coordinates of the tile it is moving onto, that means that the event currently occupying the tile has a chance to moving away, yielding the tile to the other event which wants to move onto it. This allows for rows of events (think of soldiers or larger objects made from multiple events) to move smoothly together regardless of their event ID order.
  • Ask the global autostart event worker to run. The global autostart event worker will do a loop of up to 10000 times trying the following:
    • If there is no event script loaded: See if there is any common event waiting. (Remember that this involves a call to a property getter which will be true as soon as a common event is marked as autostart, not empty, and not with unmet switch condition.) Events are checked in numerical order. Once a waiting common event is found, it's loaded.
    • If there was no waiting common event found: See if there is any map event waiting - again in numerical order. (Remember that this checks an actual flag which was previously set while the event was asked to "act".) If yes, reset the "waiting" flag for this event and load it.
    • If there was neither a waiting common nor map event found: Abort.
    • Execute a command of the currently loaded script. If the command requires waiting, quit the loop afterwards.
    • If the end of the event script is reached, unload it.

So, in short, once there is any autostart common event available for running, it will run in a tight loop until 10000 commands have been processed. If more than one is available, the one with the lowest ID will always win because once it finished running, the worker will again try to find an event and will again land at the same one unless its start conditions are now unmet (disabled switch).

With the logic I wrote above, this should also always make common events win against map events because map events are checked afterwards. (And unlike common events, more than one autostart map event may run in the same frame because for them the "waiting" flag is an actual flag which gets set only once per frame and reset upon execution, so once one is done, it won't be selected again in the next iteration until the next frame at which the "waiting" flags are enabled again.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment