Documentation 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.
Every EVT script is a flat Bytecode array interpreted by the EVT virtual machine. Each instruction begins with an opcode constant from the EVT_OP_* enum (defined in include/evt.h), followed by an argument count and then the argument values. You never write these low-level arrays by hand — the high-level macros in include/script_api/macros.h generate them — but understanding the opcode set helps you read decompiled scripts and diagnose unexpected behavior.
EVT_OP_INTERNAL_FETCH (value 0) is used internally by the VM to prefetch the next instruction. You will never write it in a script, and the assembler macros never emit it directly.
Control flow
These opcodes govern branching, looping, and early exit from a script.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_END | End | — | Signals the end of script data. Every script must terminate with this; a missing End will likely crash on load. |
EVT_OP_RETURN | Return | — | Kills the current thread. A script missing Return will remain alive but do nothing until something else (e.g. leaving the map) kills it. |
EVT_OP_LABEL | Label(id) | id | Marks a jump target. Valid label IDs are 0–22. |
EVT_OP_GOTO | Goto(id) | id | Moves execution to the label with the given ID. |
EVT_OP_LOOP | Loop(times) | times | Begins a loop. The counter is decremented after each iteration. Loop(0) loops infinitely. Up to 8 loops may be nested. |
EVT_OP_END_LOOP | EndLoop | — | Marks the end of a loop body. |
EVT_OP_BREAK_LOOP | BreakLoop | — | Exits the innermost loop immediately. |
EVT_OP_WAIT_FRAMES | Wait(n) | n | Blocks the thread for n frames. This is one of the four blocking instructions. |
EVT_OP_WAIT_SECS | WaitSecs(n) | n | Blocks the thread for n seconds. |
EVT_OP_IF_EQ | IfEq(a, b) | a, b | Begins an if block that executes only when a == b. |
EVT_OP_IF_NE | IfNe(a, b) | a, b | Executes when a != b. |
EVT_OP_IF_LT | IfLt(a, b) | a, b | Executes when a < b. |
EVT_OP_IF_GT | IfGt(a, b) | a, b | Executes when a > b. |
EVT_OP_IF_LE | IfLe(a, b) | a, b | Executes when a <= b. |
EVT_OP_IF_GE | IfGe(a, b) | a, b | Executes when a >= b. |
EVT_OP_IF_FLAG | IfFlag(a, b) | a, b | Executes when (a & b) != 0. |
EVT_OP_IF_NOT_FLAG | IfNotFlag(a, b) | a, b | Executes when (a & b) == 0. |
EVT_OP_ELSE | Else | — | Begins the else branch of an if block. |
EVT_OP_END_IF | EndIf | — | Closes an if or else block. |
EVT_OP_JUMP | Jump(script) | EvtScript* | Jumps unconditionally to another script pointer, resetting the timescale. Labels and other state are loaded from the target source. |
Switch statements
EVT switch statements do not have implicit fallthrough; use CaseOrEq when you need it.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_SWITCH | Switch(expr) | expr | Begins a switch over an expression resolved through evt_get_variable. Up to 8 switches may be nested. |
EVT_OP_SWITCH_CONST | SwitchConst(val) | val | Begins a switch where the value is used as-is, without variable lookup. |
EVT_OP_CASE_EQ | CaseEq(val) | val | Matches when the switch expression equals val. |
EVT_OP_CASE_NE | CaseNe(val) | val | Matches when not equal. |
EVT_OP_CASE_LT | CaseLt(val) | val | Matches when less than. |
EVT_OP_CASE_GT | CaseGt(val) | val | Matches when greater than. |
EVT_OP_CASE_LE | CaseLe(val) | val | Matches when less than or equal. |
EVT_OP_CASE_GE | CaseGe(val) | val | Matches when greater than or equal. |
EVT_OP_CASE_DEFAULT | CaseDefault | — | Matches unconditionally; used as the default case. |
EVT_OP_CASE_OR_EQ | CaseOrEq(val) | val | Like CaseEq, but falls through to the next case until EndCaseGroup. |
EVT_OP_CASE_AND_EQ | CaseAndEq(val) | val | Falls through only if expr == val; skips the group body otherwise. |
EVT_OP_CASE_FLAG | CaseFlag(val) | val | Matches when the flag bit val is set on the switch expression. |
EVT_OP_END_CASE_GROUP | EndCaseGroup | — | Closes a CaseOrEq / CaseAndEq fallthrough group. |
EVT_OP_CASE_RANGE | CaseRange(min, max) | min, max | Matches when min <= expr <= max (inclusive). |
EVT_OP_BREAK_SWITCH | BreakSwitch | — | Exits the current switch block. |
EVT_OP_END_SWITCH | EndSwitch | — | Closes the switch statement. |
Variables and assignment
EVT encodes variable references as large negative integers so they are distinguishable from literal values. The evt_get_variable and evt_set_variable functions decode the encoding at runtime.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_SET | Set(var, val) | var, val | Sets var to the integer result of evaluating val through evt_get_variable. |
EVT_OP_SET_CONST | SetConst(var, const) | var, const | Sets var to const as-is, skipping variable lookup. |
EVT_OP_SETF | SetF(var, val) | var, val | Sets var to a fixed-point float value via evt_get_float_variable. |
Integer arithmetic
All arithmetic opcodes read from and write back to a variable container.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_ADD | Add(var, val) | var, val | var += val |
EVT_OP_SUB | Sub(var, val) | var, val | var -= val |
EVT_OP_MUL | Mul(var, val) | var, val | var *= val |
EVT_OP_DIV | Div(var, val) | var, val | Integer division: var /= val |
EVT_OP_MOD | Mod(var, val) | var, val | var %= val |
Floating-point arithmetic
These operate on fixed-point floats using the same encoding as Float() and SetF.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_ADDF | AddF(var, val) | var, val | var += val (float) |
EVT_OP_SUBF | SubF(var, val) | var, val | var -= val (float) |
EVT_OP_MULF | MulF(var, val) | var, val | var *= val (float) |
EVT_OP_DIVF | DivF(var, val) | var, val | var /= val (float) |
Bitwise operations
| Opcode | Macro | Args | Description |
|---|
EVT_OP_BITWISE_AND | BitwiseAnd(var, val) | var, val | var &= val — val is resolved through evt_get_variable. |
EVT_OP_BITWISE_AND_CONST | BitwiseAndConst(var, const) | var, const | var &= const — const is used as-is. |
EVT_OP_BITWISE_OR | BitwiseOr(var, val) | var, val | var |= val |
EVT_OP_BITWISE_OR_CONST | BitwiseOrConst(var, const) | var, const | var |= const — const is used as-is. |
Buffers and arrays
Scripts can read sequential data from a C pointer via buffer opcodes, or access named elements via array opcodes.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_USE_BUF | UseBuf(ptr) | s32* | Sets the integer buffer pointer for subsequent BufRead calls. |
EVT_OP_BUF_READ1 | BufRead1(v) | container | Reads one s32 from the buffer and advances the read position. |
EVT_OP_BUF_READ2 | BufRead2(v1, v2) | container, container | Reads two s32 values. |
EVT_OP_BUF_READ3 | BufRead3(v1, v2, v3) | 3 containers | Reads three s32 values. |
EVT_OP_BUF_READ4 | BufRead4(v1, v2, v3, v4) | 4 containers | Reads four s32 values. |
EVT_OP_BUF_PEEK | BufPeek(idx, v) | index, container | Reads the s32 at idx without advancing the position. |
EVT_OP_USE_FBUF | UseFBuf(ptr) | f32* | Identical to UseBuf for float buffers (same underlying pointer). |
EVT_OP_FBUF_READ1 | FBufRead1(v) | container | Reads one f32. |
EVT_OP_FBUF_READ2 | FBufRead2(v1, v2) | 2 containers | Reads two f32 values. |
EVT_OP_FBUF_READ3 | FBufRead3(v1, v2, v3) | 3 containers | Reads three f32 values. |
EVT_OP_FBUF_READ4 | FBufRead4(v1, v2, v3, v4) | 4 containers | Reads four f32 values. |
EVT_OP_FBUF_PEEK | FBufPeek(idx, v) | index, container | Reads the f32 at idx without advancing. |
EVT_OP_USE_ARRAY | UseArray(ptr) | s32* | Loads an s32 array for use with ArrayVar(index). |
EVT_OP_USE_FLAGS | UseFlagArray(ptr) | s32* | Loads a packed flag array for use with ArrayFlag(index). |
EVT_OP_MALLOC_ARRAY | MallocArray(size, outVar) | length, s32* | Allocates a new array of size elements. Scripts do not free this memory. |
Script execution
| Opcode | Macro | Args | Description |
|---|
EVT_OP_EXEC | Exec(script) | EvtScript* | Spawns a new independent thread. The parent continues immediately. |
EVT_OP_EXEC_GET_TID | ExecGetTID(script, outVar) | EvtScript*, container | Spawns a thread and stores its thread ID for later use with KillThread, SuspendThread, etc. |
EVT_OP_EXEC_WAIT | ExecWait(script) | EvtScript* | Spawns a child thread and blocks until it returns. Local variables and flags are copied back on completion. This is a blocking instruction. |
EVT_OP_CALL | Call(func, ...) | *function, ... | Calls a C API_CALLABLE function with optional arguments. Blocking if the function returns ApiStatus_BLOCK. |
Thread management
| Opcode | Macro | Args | Description |
|---|
EVT_OP_BIND_TRIGGER | BindTrigger(script, trigger, colliderID, unkA3, outVar) | 5 args | Registers a script to run when a trigger fires (e.g. TRIGGER_FLOOR_TOUCH). Only one thread runs per trigger at a time. |
EVT_OP_BIND_PADLOCK | BindPadlock(script, trigger, colliderID, itemList, unkA3, outVar) | 6 args | Like BindTrigger, but also specifies an item list for padlock interactions. |
EVT_OP_UNBIND | Unbind | — | Removes the current thread’s trigger binding. |
EVT_OP_KILL_THREAD | KillThread(tid) | ScriptID | Terminates the thread with the given ID. |
EVT_OP_SUSPEND_GROUP | SuspendGroup(group) | group | Pauses all threads belonging to group. |
EVT_OP_RESUME_GROUP | ResumeGroup(group) | group | Resumes all threads in group. |
EVT_OP_SUSPEND_OTHERS | SuspendOthers(group) | group | Pauses all threads in group except the current one. |
EVT_OP_RESUME_OTHERS | ResumeOthers(group) | group | Resumes all threads in group except the current one. |
EVT_OP_SUSPEND_THREAD | SuspendThread(tid) | ScriptID | Pauses a specific thread by ID. |
EVT_OP_RESUME_THREAD | ResumeThread(tid) | ScriptID | Resumes a specific thread by ID. |
EVT_OP_IS_THREAD_RUNNING | IsThreadRunning(tid, outVar) | ScriptID, container | Sets outVar to true if a thread with the given ID is still alive. |
EVT_OP_SET_PRIORITY | SetPriority(priority) | priority | Sets the current thread’s execution priority. Higher values run first. |
EVT_OP_SET_TIMESCALE | SetTimescale(scale) | timescale | Sets a multiplier applied to Wait and WaitSecs durations. |
EVT_OP_SET_GROUP | SetGroup(group) | group | Assigns the current thread to a group. See EventGroupFlags in include/evt.h. |
Inline threads
These opcodes create threads inline within a script body rather than from an external EvtScript array.
| Opcode | Macro | Description |
|---|
EVT_OP_THREAD | Thread | Begins an inline thread block. The enclosed commands run as a new, independent thread. |
EVT_OP_END_THREAD | EndThread | Closes an inline thread block. |
EVT_OP_CHILD_THREAD | ChildThread | Begins an inline child thread block. The child is killed automatically when the parent dies. |
EVT_OP_END_CHILD_THREAD | EndChildThread | Closes an inline child thread block. |
Debug
These opcodes are stripped or become no-ops in the release build.
| Opcode | Macro | Args | Description |
|---|
EVT_OP_DEBUG_LOG | EVT_DEBUG_LOG(str) | string | Logs a string. Does nothing in the release build. |
EVT_OP_DEBUG_PRINT_VAR | DebugPrintVar(var) | expression | Prints the name and value of a variable. |
Reserved opcodes
Opcodes EVT_OP_92, EVT_OP_93, and EVT_OP_94 appear in the enum but are not yet documented in the decompilation.