Paper Mario uses a custom bytecode scripting system called EVT (Event) to drive nearly all in-game logic: map initialization, NPC behavior, cutscenes, item pickups, battle actions, and more. Rather than hardcoding these sequences in C, the game executes compact arrays ofDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/pmret/papermario/llms.txt
Use this file to discover all available pages before exploring further.
Bytecode values interpreted by a lightweight virtual machine. This separation lets designers compose complex interactions from reusable building blocks without recompiling the engine.
How the VM works
The EVT interpreter manages up to 128 concurrent script threads (seeMAX_SCRIPTS in include/macros.h). On every game frame, the engine calls update_scripts, which walks all active threads and executes instructions until each thread either blocks or completes.
Each thread is represented by an Evt struct. The interpreter’s main loop calls evt_execute_next_command, which reads one instruction at a time from the thread’s program counter and dispatches to a handler function. The handler returns one of three internal values:
| Return value | Meaning |
|---|---|
EVT_CONTINUE (0) | Move to the next instruction immediately |
EVT_ABORT (1) | Stop executing this thread for the current frame |
EVT_FINISH (255) | The thread has ended |
ApiStatus values returned by callable API functions (described below).
Script data layout
A script is aconst Bytecode[] array — typedef’d as EvtScript — stored in ROM. Each instruction is laid out as:
EVT_CMD macro in include/script_api/macros.h constructs these entries and computes argc automatically at compile time, so you never write the argument count by hand. All high-level macros such as Call, Set, and IfEq expand to EVT_CMD calls.
ApiStatus: the callable return protocol
When a script executes aCall instruction, the VM calls a C function matching the API_CALLABLE signature:
ApiStatus values defined in include/evt.h:
| Value | Constant | Description |
|---|---|---|
| 0 | ApiStatus_BLOCK | Suspend the thread; call this function again next frame |
| 1 | ApiStatus_DONE1 | Complete unconditionally |
| 2 | ApiStatus_DONE2 | Complete; respects Evt->disableScripts |
| 3 | ApiStatus_REPEAT | Call again immediately (same frame) |
| 255 | ApiStatus_FINISH | Terminate the thread, equivalent to Return |
isInitialCall parameter is true on the first call and false on subsequent calls when ApiStatus_BLOCK was returned. Use it to perform one-time setup (allocating state, starting an animation) before entering a per-frame polling loop.
The API_CALLABLE macro
API_CALLABLE (defined in include/macros.h) expands to the full function signature:
script->ptrReadPos and returns an appropriate status:
script->functionTemp[]:
Script execution
You launch a script from another script using one of the execution opcodes:| Macro | Opcode | Behavior |
|---|---|---|
Exec(script) | EVT_OP_EXEC | Spawns a new thread; parent continues immediately |
ExecGetTID(script, outVar) | EVT_OP_EXEC_GET_TID | Spawns a new thread; stores its thread ID |
ExecWait(script) | EVT_OP_EXEC_WAIT | Spawns a child thread; parent blocks until it finishes |
LVar0–LVarF) and local flags (LFlag0–LFlagF) are copied into the child thread when it spawns. ExecWait also copies them back when the child returns.
Script groups and priorities
Every thread belongs to a group (EventGroupFlags) and has a priority (EventPriority). Groups control which threads are suspended automatically during battles, menu transitions, or map exits. Priority determines execution order within a frame — higher-priority threads run first.
Opcode reference
Full listing of all EVT_OP_* bytecode instructions with argument descriptions.
Script API reference
Catalogue of API_CALLABLE functions available from scripts.
Writing EVT scripts
Practical guide to authoring scripts in C using the EVT macro system.
