4 Reference Commands
bryanthaboi edited this page 2026-10-20 17:01:40 -04:00

Script command reference

The verbs available in script rows - map scripts, quest scripts, cutscenes. A script is an array of rows; each row is { "verb", arg1, arg2, ... }. Implementation and per-verb porting notes: src/script/Commands.lua. All of these are records in the commands registry, registered by the engine under the owner engine, so a mod adds a verb with mod.commands:register(verb, fn) and replaces one with mod.content.commands:override(verb, fn).

Generations. This page is the Gen 1 (Red, Blue, Yellow) verb set. Gold, Silver and Crystal run cart script bytecode in their own VM, and the Gen 3 games run theirs, so the engine verbs below are not rows there. What does carry across: a verb a mod registers resolves on all three generations (Registering your own verb), and Portable API 3 mods get a common verb set that the engine adapts per game (Portable script verbs).

Find the command family

You want a script to Start here
Show dialogue or ask a question Text and choices
Branch, wait, or start another script Flow
Change flags, items, or money Flags, items, money
Start a battle or give a Pokémon Battles and mons
Move people, objects, or the camera Movement and staging
Play sound, music, or open a screen Sound and screens

Where rows go: a map_scripts entry or another API that accepts script rows. Status: the verb tables are exact contracts; the traced example below is a shape you adapt with text and flag ids that exist in your mod.

In an Args cell, commas separate values in the row after the verb. Quoted text such as "wild" is literal text, and A | B means either listed form is accepted. A textId may be a generated text label, a map pointer, or literal text where that command says literal text is allowed.

A handler is fn(ctx, ...); returning a string jumps to that label ("end" ends the script). ctx carries ctx.save, ctx.overworld, ctx.game, ctx.runner, and the branch state below.

Branch state

Check-style verbs set ctx.lastCheck; jump_if_true / jump_if_false branch on it. choice also sets ctx.lastChoice = { index, label }. Use label rows as jump targets - never absolute row numbers, which break when a row is inserted.

Trace one small branch

This script gives a Potion the first time it runs and shows different text on later runs. It is shape only: replace the flag and text with ids or literal text appropriate to your script, then register the rows through map_scripts.

"mod:" makes the branch state private to the mod that owns these script rows. This works only for a mod-owned script contribution; engine-owned rows must use the shared story-flag namespace. It is the script-row form of mod.save.

local rows = {
  { "check_flag", "mod:got_potion" },
  { "jump_if_true", "already_got_it" },
  { "show_text", "You found a POTION!" },
  { "give_item", "POTION", 1 },
  { "set_field", "mod:got_potion", true },
  { "jump", "end" },
  { "label", "already_got_it" },
  { "show_text", "The spot is empty." },
  { "label", "end" },
}
Row What the runner remembers What happens next
check_flag lastCheck is true when the flag is set continue to the conditional jump
jump_if_true reads lastCheck jumps to already_got_it only on later visits
give_item and set_field the private value changes only after the item is given the next visit takes the other branch; a full bag stops before that row
label nothing by itself provides a stable jump destination

Text and choices

Verb Args Does
show_text textId, subs show a text box; textId is a generated-text label ("_PalletTownGirlText"), a map TEXT_* pointer, or a literal. subs fills {RAM:...} tokens
ask textId, subs text, then YES/NO; result in lastCheck
choice labels, opts N-way menu; lastChoice = { index, label }, lastCheck = (index == 1); opts.default preselects, opts.cancel is the B index
text_opts opts TextBox options for the next show_text only (auto = true is the no-button-wait box)
text_sound soundId arm the NEXT show_text with a trailing jingle (the ROM's sound_get_item_1 / sound_get_key_item text-command idiom): it fires once the last page has typed and the box waits on it before the button wait

Flow

Verb Args Does
label name jump target, pre-scanned by the runner
jump target unconditional jump to a label
jump_if_true / jump_if_false target branch on lastCheck
wait frames block for n frames
wait_sound block until the last play_sound effect has finished (capped by a ceiling)
wait_flag flagName, timeoutFrames yield until the flag sets; lastCheck false on timeout. The sync primitive for parallel scripts
run_parallel rowsOrRef, opts start a background script (bounded slots) and continue; rowsOrRef is a row array or "MAP_ID/name" naming a map_scripts scripts entry

Flags, items, money

Verb Args Does
set_flag / clear_flag name write the shared story-flag namespace
check_flag name lastCheck = flag state
check_item itemId lastCheck = bag has one
give_item itemId, count, gotText add to bag with the received jingle; gotText = false when the script shows its own text
take_item itemId, count remove from bag
give_money amount add money (clamped at 0)
check_money amount lastCheck = the player has at least amount
take_money amount remove money (floors at 0)
take_coins amount remove Game Corner coins (floors at 0)
set_field key, value scratch state outside the flag namespace; "mod:key" routes to the owning mod's save.modData
set_option key, value write save.options[key] (the Champion's Room idiom)

Battles and mons

Verb Args Does
start_battle "wild", species, level or "trainer", OPP_CLASS, partyIndex run a battle
static_battle species, level, beatFlag a static encounter (Snorlax/birds idiom); beatFlag set on any non-blackout end
rival_battle oppClass, baseParty, offsets the starter-counterpick party math
check_battle_result "win"|"lose"|"run"|"caught", ... lastCheck = result is any of the given
give_pokemon species, level, skipNickname gift mon; emits the mutable pokemon.before_give first; lastCheck false when party and boxes are full. skipNickname = true suppresses the nickname prompt for callers that name the gift themselves
save_end_battle_text textId stash the after-battle line the next scripted battle shows on defeat, with {PLAYER}/{RIVAL} expanded
set_catch_rate slot, value set the catchRate of the party mon in a one-based slot
heal_party full heal
trade tradeIndex, doneFlag an in-game NPC trade
old_man_demo the Viridian catch tutorial
record_hall_of_fame Hall of Fame sequence
mark_seen species set the Pokedex SEEN flag
check_dex_owned n lastCheck = at least n species owned (default 1)
dex_rating Oak's seen/owned tally and rating line; blocks until the box closes

Movement and staging

Verb Args Does
move_player dir, tiles walk the player
move_npc objIndex, dir, tiles walk an NPC
place_npc objIndex, x, y, facing teleport an NPC to a cell, no walk
move_npc_to objIndex, tx, ty pathfind an NPC to a cell
walk_npc objIndex | "player", dirs, opts chained walk along a direction list; opts.wait = false returns immediately
march_in_place objIndex, on toggle walk-in-place; non-blocking
face dir face the scripted NPC
face_player scripted NPC faces the player
no_npc_face_player on stop (or restore) NPCs turning to face the player while a script runs
face_object objIndex, dir face an arbitrary object
face_npc player faces the scripted NPC
face_player_dir dir force the player's facing
emote target, bubble, frames emote bubble ("shock", "question", "happy")
warp mapId, x, y, facing scripted warp
remember_outdoor mapId, x, y set save.lastOutdoor, the Dig / Escape Rope / blackout return point
show_object / hide_object mapId, objName persistent object toggles
replace_block bx, by, blockId the Cut-tree / card-key-door idiom
ss_anne_departs the S.S. Anne leaves the Vermilion dock; foreground, blocks until it finishes
set_tile_anim anim | false override the tileset animation until the next map change
pan_camera dx, dy, frames or "reset" offset the camera by cells (blocking)
fade dir, frames screen fade

Sound and screens

Verb Args Does
play_sound soundId one-shot SFX
play_cry species a cry; a following show_text waits for it
engage_music songId arm the trainer-engage song for the next scripted trainer battle
play_music songId, opts switch music now; opts.keep survives the next warp
play_once songId one-shot jingle (Music_PkmnHealed idiom); blocks until it finishes, then the map theme resumes
play_default_music resume the current map's own theme after a cutscene override, keeping the bike/surf substitution rules
fade_music frames fade the music out (default 10)
stop_music stop music
open_mart textConst open a mart by TEXT constant
push_screen screenId, args instantiate through the screens registry and block until the screen pops

Yellow support

Verbs ported for Pokemon Yellow's scripts; on Red and Blue they are safe no-ops or fall back sensibly.

Verb Args Does
load_player_starter_name put the chosen starter's species name in the engine string buffer for the next text substitution
spawn_pikachu_follower spawn the Pikachu follower on the current map
pikachu_make_way Oak's Lab Pikachu step-aside movement; a no-op without a Yellow follower

Portable script verbs

mod.world:queueScript(rows, { onDone = fn }) is the one script entry point that works on every generation for a "api": 3 mod (Portable API 3). Rows are { verb, arg... }. The whole list is validated before the first row runs: a bad row, a missing native adapter or an unknown ID refuses the list with nil, reason and nothing executes. At most 1024 rows. onDone(ok, reason) fires once; queueing a new list replaces the running one.

Aliases: text is show_text, setflag is set_flag, clearflag is clear_flag.

Common verbs (Gen 1, Gen 2, Gen 3), with the Gen 1 argument shapes above:

Group Verbs
Flow label, jump, jump_if_true, jump_if_false, end, wait, wait_flag
Checks check_flag, check_item, check_money, check_dex_owned, check_battle_result
State set_flag, clear_flag, set_field, set_option, give_money, take_money, take_coins, give_item, take_item, heal_party, mark_seen, give_pokemon
Dialogue show_text, ask, choice
World warp, move_player, move_npc, move_npc_to, face, face_object, face_npc, face_player, face_player_dir, place_npc, show_object, hide_object, replace_block
Battle and shops start_battle, static_battle, open_mart
Audio play_sound, play_music, stop_music, play_cry, play_once, text_sound, wait_sound, engage_music, fade_music, play_default_music, remember_outdoor

Adapted verbs exist on Gen 2 and Gen 3 through an engine adapter:

Verb Gen 1 Gen 2 Gen 3
emote, fade, walk_npc, text_opts, push_screen, trade the Gen 1 command above adapted adapted
pan_camera, march_in_place the Gen 1 command above adapted only where the native game has it, else refused adapted

On Gen 1 any verb the commands registry resolves also runs, including verbs a mod registered. On Gen 2 and Gen 3 a mod-registered verb runs the same way; an engine Gen 1 verb outside the two tables above (rival_battle, dex_rating, run_parallel, ss_anne_departs, set_catch_rate and the rest) is refused with native content adapter unavailable: <verb>, and an unknown verb with unsupported script command: <verb>.

Notes that differ from Gen 1:

  • text_opts on Gen 2 and Gen 3 accepts only auto; a show_text options argument is refused there.
  • push_screen needs a screen registered in content.screens; trade needs a native trade entry.
  • give_item, give_pokemon and static_battle resolve species and item IDs through the selected game's data; an unknown ID refuses the list.
  • warp, remember_outdoor, directions, flag names and screen IDs are validated up front.
  • Native script bytecode (Gen 2 VM, Gen 3 script engine) stays version-specific and is not exposed as verbs.

For one action instead of a list use mod.world:warpTo or mod.world:startWildBattle.

Registering your own verb

mod.commands:register("shake_screen", function(ctx, frames)
  -- runs like any engine verb; return a label string to jump
end)

A registered verb is also dispatched by the Gen 2 VM (a row such as { "mymod:shake", 4, 2 }, or { verb = ..., args = ... }) and by the Gen 3 script engine (a row whose op is the verb name). The handler receives a context and the row's arguments; a returned label string jumps within the script, and "end" ends it. An error in a registered verb is logged and the row is skipped on Gen 2 and Gen 3 rather than taking the map script down. Use a modid:verb name so the failure can be attributed.

A registered record may also be { fn = fn, foreground = true, blocking = true } - foreground verbs are illegal in parallel scripts, and a parallel script moving an NPC takes a per-NPC lock a foreground script preempts.