Script a mission
The Mission Graph edits mission scripts: the Lua files Sunrise runs while an activity is live. It shows a script as a node graph and as Lua. Mission Scripting explains what a script is and how it runs.
Mission scripts are plain Lua files, not package data. They are never stored as deltas.
| script | lives in | save writes |
|---|---|---|
| installed script | bin/x64/Sunrise/scripts/<stem>/<stem>.lua | that file, in place; no project needed |
| mod script | the project’s scripts/<stem>/<stem>.lua | the project file; needs its project open |
Open the Mission Graph
- Choose View > Mission Graph.
It opens in its own area of the layout, or in its own window when it is popped out. See Arrange the layout. To open a script, choose it in Installed scripts, or in Mod scripts with a project open.
The toolbar holds these controls. Icon buttons name themselves in their tooltip.
| control | does |
|---|---|
| + (New script) | starts a new script |
| Installed scripts | opens an installed script; its tooltip shows the open script’s file |
| circular arrow (Reload scripts and API schema) | reads the scripts and the SDK again |
| Mod scripts | opens a script of the open project |
| Save | saves the script; it shows Save* while there are unsaved changes |
| export (Export Lua for review) | writes a copy to a folder outside the installation |
| Read only | shows when the script’s project is not open for editing |
| warning icon | shows when there is no API schema; its tooltip says why |
| forward arrow (Finish switching scripts) | shows while a switch waits on unsaved changes; opens its dialog again |
Press reload after the game or a git pull changes the scripts or the SDK. Undo and redo are in the
Edit menu.
Create a new script
- Press + (New script), or choose File > New mission script….
- In Activity, search for and choose an activity from the installed SDK.
- In Arrival region, choose the region where the script starts.
- Check Graph key. It defaults to the script’s name.
- Under Save to, choose Installed scripts or This project (mod).
- Press Create script.
The activity sets the folder and file name, so a place holds one script per activity. Create refuses a script that already exists. Open that one instead. A new script targets an existing activity. It does not create a new activity.
The dialog names any SDK file that is missing or damaged. Start the game once to rebuild it. When
scripts/lib/flow.lua is missing, the dialog says so and Create script stays off.
The starter script waits until the player enters the arrival region, then runs its first step. It resumes saved progress on load. It does not complete the mission on its own.
Add a step
- Open the Nodes tab.
- Select the facts group or the steps group.
- Press + (Add node).
- Type a Node id.
- Choose a Template.
- Choose the Sensor, Target, Navpoint, Scene, Input, Output or Inputs the template asks for.
- Press Create.
| template | group | does |
|---|---|---|
| Observe trigger | facts | waits for a player-trigger report, then disarms the trigger |
| Scene output | facts | waits until a scene reports one of its outputs |
| Immediate step | steps | starts with an empty action |
| Arm trigger | steps | arms an SDK trigger slot |
| Show objective | steps | sets a directive on its sensor, with an optional navpoint |
| Play dialogue | steps | plays a cue on its dialogue sensor |
| Select state | steps | selects an SDK state |
| Complete mission | steps | completes the mission |
| Start scene | steps | starts a scene, then sends the inputs you tick, in the order you tick them |
| Send scene input | steps | sends one input to a scene |
In a campaign graph, the steps group offers other templates: Immediate step, Reach trigger, Complete ghost scan and Use interactable.
Scene inputs and outputs
A scene is a scripted piece of a mission, such as an explosion or a character’s performance. Each scene has its own inputs and outputs.
- An input is an event the scene waits for. A script sends it.
- An output is an event the scene reports. A script waits for it.
Pickers only offer the inputs and outputs of the scene you chose. They come from the installed
SDK’s mission.SceneInput and mission.SceneOutput tables. An SDK generated before these tables
existed lists none. Start the game once with the current Sunrise to regenerate it, then press reload
on the toolbar. See Generated SDK and
The world model.
A Scene output fact needs the script to pass scene outputs to its flow. New scripts do this.
For an older script, add on_event_scene_output to the table it returns, the same way as its other
on_event_ callbacks. Until then, Checks reports the fact.
Campaign steps
Campaign steps run in list order. Select a step and press the up or down arrow (Move before the previous node, Move after the next node) to move it. Links between campaign steps show that order and cannot be dragged.
Select a step to edit its fields:
| field | does |
|---|---|
| Objective, Navpoint, Waypoint volume | what the step shows the player |
| Start scene | starts this scene when the step starts |
| Inputs | sends the ticked inputs right after the scene starts; needs a start scene |
| Arm in leg, Trigger | ends the step on that trigger; the leg arms it |
| Ghost scan, Interaction | ends the step on that scan or interaction |
| Scene finished | ends the step when that scene finishes |
| Output scene, Scene output | ends the step when that scene reports that output after the step started |
| Encounter cleared, Region reached | ends the step on that encounter or leg |
| Barrier | a later step’s trigger cannot end this step early |
| Revisit | the step’s trigger and region count only after the step starts |
| Dialogue | cue lines, each with an optional filter volume |
A step with no end condition completes at once. The inspector then shows Completes immediately.
Choosing another start scene drops inputs that belong to the old one. Clearing it drops all inputs. Choosing another output scene clears the output until you pick a new one.
Select a leg to set its State. Edit its trigger and monitor lists in Advanced Lua.
Encounter nodes and a step’s destroyed-object list have no typed fields. Edit them in Advanced Lua.
Make a step wait for another
This works for flow steps only. Campaign steps run in list order.
- Drag from the first step’s output arrow, on its right, to the second step’s input ring, on its left.
The second step then runs after the first finishes. The Dependencies checkboxes of a selected step set the same links.
To remove a link, do one of these:
- Click it and press Disconnect.
- Right-click it and choose Disconnect.
- Drag it off its input ring onto empty space.
Dropping a link on an invalid target keeps the old link.
Edit a step’s action
Flow steps only.
- Select the step.
- Under Action, press Edit action.
- Choose the Action type and fill its fields.
- Press Apply.
Set when a step starts and finishes
Flow steps only.
- Select the step.
- In Start when, choose Immediately, Fact, All or Any.
- In Finish when, choose the same way.
- For All or Any, press Add condition for each child condition.
| condition | means |
|---|---|
| Immediately | no condition |
| Fact | a named fact in this flow is set |
| All | every child condition holds |
| Any | at least one child condition holds |
All and Any do not pick one branch: every eligible step can run. Facts stay set for the attempt unless a checkpoint resets them. Tick Checkpoint on a step to make it one.
Edit Lua directly
A Lua box opens as a preview. Press Edit to type in it, and Preview to go back.
| where | apply | drop the edit |
|---|---|---|
| a Custom Lua field, or a node written as Lua | Apply | Revert |
| the Lua section of a typed action or condition | Apply | Revert |
| Code or Advanced Lua on a script-view node | Apply | Revert |
| Inputs of a script-view node | Apply | Revert |
| Advanced Lua on a flow or campaign node | Apply | select the field again |
| the Lua tab | Apply | Revert |
Script-view inputs and the flow or campaign Advanced Lua box have no preview. In Advanced Lua, pick a field from the list or type its path, then edit the box.
Unapplied Lua marks the script as unsaved.
- A pending field edit blocks Save, Undo, Redo, Export and other edits. Apply or revert it first.
- Save, Export and graph edits apply a pending Lua tab edit first. Undo drops it, and Redo brings it back.
- While a Lua tab edit is pending, the Script group is read only and no field edit applies. Redo is refused until you revert it.
Studio reads Lua 5.4 and refuses any edit that leaves a syntax error. On the Lua tab, the error names its line and column, and the draft stays so you can fix it. A script file with a syntax error does not open; fix it in a text editor first.
Save
- Press Save, or press Ctrl+S while the Mission Graph has focus.
An installed script saves in place. A mod script saves into the project. The export button (Export Lua for review) writes a copy for review to a folder outside the installation. Node positions are kept in your local settings, per script.
When you switch scripts, projects or installations with unsaved changes, the Unsaved mission script dialog asks you to Save and continue, Discard or Cancel. After Cancel, the switch waits. Press the forward arrow (Finish switching scripts) on the toolbar to choose again. Saving also finishes the switch.
Check the script
- Open the Checks tab. Its title shows the number of problems.
It lists graph errors, such as unknown dependencies or facts, cycles, invalid conditions, duplicate ids and over-long state keys. It also lists scene errors:
- an input sent to a scene that does not have it
- an output waited for on a scene that does not report it
- a campaign step with inputs but no start scene
- a campaign step whose scene is a slot, not a
mission.Sceneentry - an input or output name that the installed SDK does not list
- a scene output fact in a script with no
on_event_scene_outputcallback
It also names a missing or damaged SDK file, and lists the problems of typed calls. See Checks on typed calls. With nothing to report, it says No problems found. With no API schema, it also says that API calls were not checked.
Graph errors block Export Lua for review, Build and install and Update running game. They do not block Save. Scene and call problems block nothing.
It is not a Lua compiler, and a passing check does not prove the mission plays in game.
Run it in the game
- An installed script runs the next time its activity starts. In game, the Script page’s Reload script loads it again. See Your first script.
- A mod script needs Build > Build and install while the game is closed.
- While the game runs, Build > Update running game reloads changed mod scripts. See Build and install a mod.
The whole-script view
The Script group shows the whole Lua file as a graph. Every top-level statement is a node, and
so is every field of the returned callback table. A link runs from the node that defines a name to
each node that uses it. A module node shows its local name, with its require line below.
| control | effect |
|---|---|
| double-click a function or callback | opens its control flow |
| folder (Open the body), forward arrow (Open the flow) | open the selected node’s body or flow |
| folder on a flow or campaign node (Open run, for example) | opens that field’s function |
| back arrow (Up to script) | returns to the whole-script view |
| corners (Frame all) | fits the group in view |
| grid (Arrange as a tree) | lays the group out again as a tree |
| up and down arrows | move the node within its scope |
| trash can (Remove node) | removes the node |
| + (Add node) | adds a function, value, callback or statement |
| + next to a body or branch name, under Add inside | adds a statement or an API call there |
| Add else if, Add else, under Branches | adds a branch to an if node |
A group opens framed, but text never shrinks too small to read. A long chain wraps to the width of the canvas. Pan or zoom out to see a graph that is still wider.
To add a function or a value, type its name in the Name box of the Add node popup, then choose Function or Value.
If, else-if, else, while, for, repeat, local scopes, calls, assignments, returns and breaks have their own nodes. Code the graph cannot show as nodes stays a Custom Lua block, and the inspector says why. Its neighbors still show as nodes. Edits replace only their own source and keep comments and line endings.
Typed API nodes
A statement that is one Sunrise API call is a typed node. Its arguments are fields you edit, not Lua text. A call inside a larger statement, such as an if condition, gets the same editor in that statement’s inspector. The statement’s own call is open. Other calls fold under their own Call headers. Calls the schema cannot describe stay Custom Lua blocks, so every script still opens and edits in full.
The types come from the generated SDK file sdk/lua/sunrise/mission_api.json. Sunrise writes it
from the same table the runtime uses to check arguments. See Generated
SDK. An installation that has not
written it yet shows every call as Lua, and the toolbar shows a warning icon. Start the game once
with the current Sunrise, then press reload on the toolbar.
How a call is typed
Studio types parameters named context, state and event, and the handles their calls return. A
Sunrise API call it cannot type stays a Custom Lua block, and the inspector says why. Any other
call is a call node with Call and Arguments inputs.
Edit a typed call
- Select the node. Under Call, each argument has a row. A
*marks a required argument. - Change a value. The edit replaces only that value in the source.
A text or number field applies when you press Enter or leave the field.
| argument kind | editor |
|---|---|
| boolean | checkbox |
| integer, number | number field, kept inside the allowed range |
| string, name | text field |
| scalar (boolean, number or string) | text field; type the Lua value, such as true, 3 or "name" |
| enum, such as a device transition | list of the collection’s entries |
| generated entry, such as a directive or a scene input | picker of the installed SDK’s entries |
| slot, squad or scene handle | picker of the entries that match the slot type |
| list | one row per entry |
| table | one row per field |
atom of run_atoms | its kind, then the fields of that kind |
| function | Lua |
A picker offers only what the schema allows. A slot picker lists slots of the slot type the call needs. A scoped picker lists the receiver’s own entries, such as the inputs of that scene.
| button | effect |
|---|---|
| Lua | edits the value as Lua; it stays lit while on, and pressing it again goes back |
| + on an absent argument | adds it with its default or a valid placeholder |
| + (Add entry) under a list | adds a list entry, up to the allowed count |
| trash can | removes an optional argument or a list entry |
In Lua mode the box is open for typing at once. Press Apply to apply it, or Revert to drop it. An absent optional argument shows its default, greyed out.
A value that is not a literal or a generated entry, such as a local, opens as Lua.
Adding keeps the table’s style: one line or one field per line, and a trailing comma only where the table has one.
A call that passes a table built in Lua, such as squad:place(args), shows no argument rows. Edit
it in Advanced Lua.
Checks on typed calls
Studio checks literal values the way the runtime does. It finds problems such as:
- the wrong call form:
:where.belongs, or the wrong number of arguments - a required argument that is missing, or a key the call does not take
- a value of the wrong type, such as text where a number belongs
- a number outside its range, or a string of the wrong length
- a list with too few or too many entries
- an entry that is not in the installed SDK, or that comes from another table
- a slot of the wrong slot type, or an entry that belongs to another slot or scene
- an atom with no kind, or a field its kind does not have
A problem shows on the node, in its inspector and on the Checks tab. It blocks nothing. Code the checks cannot read, such as a value computed at run time, is not checked.
Add a callback or a call
- On the script view, press + (Add node). Under Callbacks, choose one. It is added to the returned table with the parameters the runtime passes. A callback the script already returns is greyed out.
- In a function, press a + under Add inside. Choose a statement, such as If or While, or a call under Sunrise API.
The list holds the calls that a value in scope can make, such as context and what it reaches. A
call that needs a slot type the mission does not have is not listed.
A new call has a placeholder for each required argument. The placeholder is valid: the default, the first matching SDK entry, or zero moved into the allowed range. Change it before you save. A callback node shows the fields its event carries.
Helpers and SDK modules
| file | example path in the project | loaded with |
|---|---|---|
| helper module | scripts/lib/tools.lua | require("lib.tools") |
| own SDK module | sdk/my_mod/bindings.lua | require("my_mod.bindings") |
Folder and module names use ASCII letters, digits and underscores. A controller must fit 128 KiB. Other modules can be up to 16 MiB. Edit helpers in a text editor. The Mission Graph lists controllers. Studio does not copy the generated SDK into projects.
Legacy scripts
Mod scripts lists a project’s .sunrise-mission.json scripts under Legacy scripts. Open one
and save it to create its Lua file. To get its exact source, right-click the entry and choose
Export original Lua.