Skip to main content

Level recipes

A recipe is a text file of edits applied to a level's scripts by twinsdump edit. They live in mod/levels/*.ops (built into the ISO), mod/levels-wip/*.ops (written but not yet proven) and tools/rig/testops/*.ops (test-only scaffolding, never shipped).

A recipe starts with a file line and then lists operations:

# Iceberg Lab exterior (L04A); skip runs COM_ICELAB_CUTSCENE_SKIP
file Levels\Ice\Hub\labext.rm2
copybody 5495 1 9 0 4 572 1 0 # state 1 plays the scene: add the developers' Triangle rule -> state 4
clearbodies 5495 9 # drop the unreachable original in state 9

One recipe may cover several levels: each file line starts a new section.

Reading a level first​

twinsdump level.rm2 scripts # every script, state by state
twinsdump level.rm2 scripts NAME # filtered by name
twinsdump level.rm2 objects [regex] # object types and their script slots
twinsdump level.rm2 instances [-v] # placements, with positions
twinsdump level.rm2 triggers # trigger boxes and what they point at
twinsdump level.rm2 refs 1234 # what refers to a script
twinsdump level.rm2 msg 244 # who sends and who handles a user message

The operations​

OpWhat it does
addbody SCRIPT STATE COND PARAM TARGET [INTERVAL [THRESHOLD]]Add a rule with no commands
copybody SCRIPT TOSTATE FROMSTATE BODYIDX TARGET COND PARAM INTERVAL [THRESHOLD]Copy a rule's commands into another state, with a new condition and target
clearbodies SCRIPT STATERemove every rule from a state
appendcmds SCRIPT STATE BODYIDX FROMSCRIPT FROMSTATE FROMBODYIDXAppend a copy of another rule's commands
delcmd SCRIPT STATE BODYIDX CMDIDXRemove one command
movebody SCRIPT FROMSTATE BODYIDX TOSTATEMove a rule between states
settarget SCRIPT STATE BODYIDX TARGETRe-point a rule (size-neutral)
setcond SCRIPT STATE BODYIDX COND PARAM [INTERVAL [THRESHOLD]]Rewrite a rule's condition, keeping its commands and target (size-neutral)
setarg SCRIPT STATE BODYIDX CMDIDX ARGIDX VALUEChange one command argument
skipprompt auto or skipprompt SCRIPT STATE[,STATE] [TEXT]Add the "hold △ to skip" hint to states that can be skipped

addbody and copybody set the state's poll flag, because a rule that is only checked once is no use for "is the player holding a button". That is also why they must not be used on an instant state - see Scripts.

Adding a command out of thin air​

There is no "add command" op, because every command has its own argument layout. Instead, copy a rule that already has the command you want and trim it:

appendcmds 4015 8 0 1511 7 0 # COM_EARTH_WORM_START state 7 body 0 is a lone SetAgent(64)

If the source rule has extra commands, delcmd them afterwards and setarg the survivor into shape. Finding a clean source is usually a grep away - SetAgent(64) on its own appears in hundreds of places.

Size changes and the state library​

Most ops change the size of the level file. That invalidates the rig's save states, because a state contains the archive's file table - so after a recipe change, rebuild them:

python tools/rig/rebuild.py # ~18 minutes: builds test.iso and all 34 states

settarget, setcond and setarg are size-neutral, which makes them the tools of choice for experiments: the existing save states stay valid, so a test is two minutes instead of twenty. setcond in particular can force a script down a path the rig cannot otherwise reach - swap a "wait for message 269" for a "wait three seconds" and the rest of the machine runs unchanged.

What is left to restore​

python tools/skip_survey.py answers that, and it reads the built mod rather than the original disc, so a skip a recipe has already restored comes back LIVE and only the genuinely unreachable ones are listed. It refuses to report at all unless a known-restored skip reads LIVE - pointed at the original disc by mistake, every restored skip looks orphaned again and the output reads like a pile of new work.

It also reports skips that are reachable but have no HOLD △ TO SKIP prompt, because restoring a skip and prompting for it are two separate recipes and mod/skip_prompt.ops names its levels by hand. That is not theoretical: covering roofcor2 in rooftop.ops gave that scene a working skip with no prompt on screen, and the player would never have found out.

Two cautions are built into how it reports. A condition-572 rule in an ordinary behaviour script is an alternative transition, not a cutscene, so only states that actually play one are counted - the same rule skipprompt auto uses. And results are grouped by script, not by level: a script sitting in dozens of level files is global boilerplate linked everywhere rather than dozens of missing prompts. COM_CORTEX_DOCAMOK_EARTH_PHASE2 is in 38 files and its scene only plays in the Doc Amok levels, which are prompted already - they are precisely the three that do not appear in its list. Presence in a level file says nothing about whether the thing runs there.

Does a recipe reach every copy of what it edits?​

Script ids are game-wide and the same script sits in as many .rm2 files as need it, but a recipe only edits the files its file lines name. Miss one and the fix silently does not apply there - which is exactly how the Rooftop skip came to be missing from roofcor2, the Nina route through that level.

python tools/recipe_reach.py checks every recipe against every level that holds the scripts it edits. It also asserts that at least one edited script really does live in more than one level, because otherwise "no gaps" would only mean the lookup found nothing. Current answer: 17 recipes, 42 scripts, no gaps - the Rooftop one was the only case and it is fixed.

Test-only recipes​

Some things cannot be reached in the rig without help. tools/rig/testops/tiki_defeat.ops sends the Totem Hokum boss straight to his defeat cutscene when the fight starts, so the beaten-boss state can be tested without fighting him:

file Levels\Earth\Hub\hubd.rm2
settarget 4015 13 0 5

Those are built only with --include tools/rig/testops, and never ship.