script.txt syntax
Mode A stories are plain text under story/script.txt. Blank lines separate nodes. The parser builds the same objects as JSON — script is sugar, not a second runtime. Story nodes.
:: park
[bg street fade 0.5]
HANA [happy]:
Hello.
> Help. -> help
> Leave. -> END
→ roughly:
{
"id": "park",
"talking": "hana",
"text": "Hello.",
"commands": [
{ "type": "bg", "value": "street", "transition": "fade", "duration": 0.5 },
{ "type": "char", "name": "hana", "expression": "happy" }
],
"choices": {
"items": [
{ "text": "Help.", "target": "help" },
{ "text": "Leave.", "target": "END" }
]
}
}
Usually you need
| Construct | Form | Effect |
|---|---|---|
| Label | :: id | Node id (:: start = New Game entry; omit → first moment) |
| Speaker | NAME: / NAME [expr]: | Talking + optional enter char |
| Text | following lines | Dialogue (joined with {@w}) |
| Choice | > text / > text -> target | Choice item; bare > → NEXT |
| Jump | -> target / -> END | Node next |
| Flag | [flag = …] / += / -= | Node set |
| Command | [bg …], [hana left], … | Enter commands |
Full construct list
| Construct | Form | Effect |
|---|---|---|
| Blank line | empty line | Ends the current node |
| Comment | # … | Ignored |
| Label | :: id | Sets node id |
| Speaker | NAME: | talking = character id (folder name) |
| Expression | NAME [expr]: | Also queues enter char with that expression |
| Custom label | Label|NAME: | label + talking |
| Joint | NAME & NAME: / NAME [expr] & NAME (silent): | Multi-speaker talking; per-segment expr / silent |
| Silent | NAME (silent): | That speaker’s mouth drive off; on joints, only names marked (silent) |
| Text | non-special lines | Joined; speaker change inserts {@talk:id} |
| Mid-line cmds | […] after text started | flow_commands + {@cN} markers |
| Choice | > text / > text -> target | choices.items[] |
| Text input | [input flag …] | input on the node |
| Jump | -> target | next |
| End | -> END | next: null |
| Flag | [name = lit|expr] / += / -= | set |
| Branch | [if expr -> target] | next_if[] |
| Auto-advance | [auto N] | auto: N |
| Panel sugar | [panel …], [panel_id …], [hide_panel], … | Node fields or panel commands |
| Exit | [exit: …], [exit_delay N] | exit_commands / exit_delay |
| Locale helpers | [text_ref key], [text_from id] | Those node fields |
| Commands | [keyword …] / [charname …] | Enter commands (or flow if after text) |
Keyword commands: bg, bgm, sfx, panel, typing_sfx, jump, jump_if, call, return, set_flag, char_layout, char_pack, choice_style, input_style, screen, plus bare character folder names → char. ([input …] writes the node’s input field, like > writes choices.)
A line that is a whole BBCode wrap ([b]…[/b], [right]…[/right], [color=…]…[/color], …) stays as text, not a command.
Line breaks inside text: a new line in the file is a pause (the player advances). To break the line on screen without a pause, write \n in the text — [right]You\nOn my way.[/right]. Write \\n for a literal \n.