Script Language
Novella scripts are UTF-8 text files with the .novella extension. The language is shaped like Ren'Py's, so people who know Ren'Py are at home, but it has no embedded Python: conditions and assignments use Novella's own expression language, and anything else a game needs is a run command implemented in C# or Divooka.
This page is the reference. Concepts explains the model the statements act on, and Theming lists every config. and theme. setting.
Layout
Indentation makes blocks: a line ending in a colon opens one, and the lines indented under it are its body. Tabs count as four spaces. A comment starts with # outside a string and runs to the end of the line.
# The garden scene.
label garden:
scene bg garden
if affection > 2: # a block under the if
q smile "Back again?"
else:
q "Oh. It's you."
A logical line continues past a newline inside brackets, after a trailing backslash, or inside a triple-quoted string. In a triple-quoted string, the common indentation is removed, single newlines join into spaces as prose wraps, and a blank line is a line break, so long passages can be written as paragraphs:
"""
There is a lake the maps do not show. A lotus as wide as a palace
floats on it, white and faintly gold.
Every night a small boat crosses it.
"""
Strings use single or double quotes; \n, \t, \\, \" and \' are escapes.
Several scripts merge into one story, loaded in path order. A label falls through to the next label written in the same file; falling off the last label of a file ends the game, or returns to a caller.
Definitions
Definitions sit at a script's top level, outside any label. Their order does not matter: a definition is in place before the game starts.
label
label start:
"The story begins."
label chapter_title(number, title="Untitled"):
centered "[number]\n[title]"
return
A label is a named block. Parameters are bound by call chapter_title("One", "The Teahouse"), are local to that call, and may have defaults. A label without parameters can still be jumped to or called.
character
character q "Su Qinglan" color "#c0392b" image qinglan
character wenjing "[player_name]" color "#7fb3c8" image wenjing side
character letter none kind nvl
character ghost "???" text_color "#b0c4de" prefix "“" suffix "”" font "Ma Shan Zheng" size 40 beep "audio/sfx/blip.ogg"
character id "Name" [options] defines a speaker. The name may interpolate variables; none instead of a name shows no name; leaving the name out uses the id. Options:
| Option | Meaning |
|---|---|
color "#hex" (or name_color) |
The name's colour. |
text_color "#hex" (or what_color) |
The dialogue's colour. |
image tag |
The character's image tag, so q smile "..." changes their picture. |
side |
Show the character's image as a side image inside the dialogue window. |
kind adv\|nvl\|centered |
The dialogue window, the full-screen NVL page, or centered text with no window. nvl alone is short for kind nvl. |
prefix "...", suffix "..." |
Text put around every line, such as quotation marks. |
font "Family", size 30 |
The font family and text size for this character's lines. |
voice tag |
The tag used for this character in an automatic voice pattern (config.auto_voice). |
beep "file" |
A sound played as the text types out. |
narrator, centered and nvl_narrator are built in. define character ... is an alternative spelling.
image
image paper = "#f4ecd8"
image overlay dusk = "#20101880"
image bg garden night = "bg/garden.jpg"
image qinglan sepia = sepia("sprites/qinglan/smile.png")
image rain = movie("video/rain.webm")
image sign = placeholder("Shop sign")
image card = composite(800, 600, 0, 0, "gui/card.png", 40, 40, "sprites/qinglan/smile.png")
image window = switch(hour < 18, "bg/window_day.jpg", "bg/window_night.jpg")
image name words = source defines an image. Most games need none of these, because image files name themselves. A source is one of:
| Source | Meaning |
|---|---|
"path/file.png" |
A file among the game's assets. |
"#rgb", "#rrggbb", "#rrggbbaa" |
A solid colour filling the screen. |
movie("file"[, loop]) |
A video playing as an image; loops unless false is given. |
placeholder("Label"[, background]) |
The engine's stand-in: a silhouette, or a gradient when background is true. |
grayscale(s), sepia(s) |
Desaturated, or desaturated and tinted warm. |
tint(s, "#ffd0d0") |
Multiplied by a colour. |
flip(s[, vertical]) |
Mirrored horizontally, or vertically when true. |
zoom(s, 0.5) |
Scaled. |
crop(s, x, y, w, h) |
A rectangle of the source, in its pixels. |
blur(s, 4), alpha(s, 0.5), brightness(s, 0.2), saturation(s, 0.5) |
Filtered. |
composite(w, h, x, y, s, x, y, s, ...) |
Several sources drawn at offsets on a canvas of one size. |
switch(condition, s, condition, s, ..., default) |
The first source whose condition holds, chosen whenever the image is drawn. |
Filters nest: blur(grayscale("bg/garden.jpg"), 6). define image ... is an alternative spelling. black and white are built in as solid colours and need no definition.
layeredimage
layeredimage qinglan:
size 900 1600
always "sprites/qinglan/base.png"
group outfit:
attribute dress "sprites/qinglan/dress.png" default
attribute festival "sprites/qinglan/festival.png"
group face:
attribute neutral "sprites/qinglan/neutral.png" default
attribute smile "sprites/qinglan/smile.png"
attribute sad "sprites/qinglan/sad.png"
group extras multiple:
attribute blush "sprites/qinglan/blush.png" if affection > 3
attribute tears "sprites/qinglan/tears.png"
A layered image is drawn from layers: every always layer, then for each group the attribute asked for, or the group's default. Attributes in a group exclude each other unless the group is multiple. An attribute with if is drawn only while its condition holds; an attribute line outside any group is a group of its own. size w h fixes the canvas, which is otherwise the first layer's size. Any combination can be shown, and attributes not mentioned are kept: after show qinglan festival smile, show qinglan sad keeps the festival outfit.
transform
transform bob(height=20):
yoffset 0
ease 1.0 yoffset (-height)
ease 1.0 yoffset 0
repeat
A named animation timeline, optionally with parameters, used with show ... at bob or at bob(40). See Animation.
transition
transition slow = dissolve(2.0)
transition longfade = fade(1.0, 0.5, 1.5)
transition flashback = fade(0.3, 0.0, 0.3, "#ffffff")
A named transition built from a built-in one with arguments, or from another named one. See Transitions.
audio
audio theme = "audio/bgm/main_theme.ogg"
define audio.bell = "audio/sfx/bell.ogg"
An audio alias, so scripts can write play music theme and play sound bell.
channel
channel rain mixer sfx loop fadeout 1.0
channel thunder mixer sfx noloop norestore
channel name [mixer music|sfx|voice] [loop|noloop] [fadeout n] [norestore] defines an audio channel. The mixer decides which volume slider controls it; loop makes it loop by default; fadeout is its default fade when replaced or stopped; norestore keeps what it plays out of saves and rollback. Built in:
| Channel | Mixer | Loops | Default fade-out | Restored on load |
|---|---|---|---|---|
music |
music | yes | 0.5 s | yes |
sound |
sfx | no | none | no |
voice |
voice | no | none | no |
ambience |
sfx | yes | 1.0 s | yes |
audio |
sfx | no | none | no |
A channel name that is neither built in nor defined plays as a non-looping sound effect.
default and define
default affection = 0
default inventory = []
default persistent.endings_seen = []
define max_affection = 10
define config.title = "The Red Thread"
define theme.text_size = 40
default name = expression is a variable's value when a new game starts; for a persistent. name it is set only if the persistent variable is not set already. define name = expression is a constant, set when a game starts and again after every load, so a change to it reaches old saves. define config.key = value and define theme.key = value set the configuration and theme; their values are evaluated once, without game variables.
gallery, track and achievement
gallery guqin "Resting the Qin" "cg guqin"
gallery lanterns "Lanterns on the River" "cg river_lanterns" "cg river_lanterns night"
gallery secret "After Hours" "cg secret" if persistent.finished
track theme "Ripples"
track "audio/bgm/romance.ogg" "Lotus Pond"
achievement lanterns "Brighter than Lanterns" "Guess all three lantern riddles."
achievement dull "Correct, and Very Dull" "Give the plum grove its dullest true answer." hidden
gallery id "Title" "image name" [...] [if condition] is a gallery entry, unlocked when every image in it has been seen (or when the condition holds); its images are shown in order and the first is its thumbnail. track file "Title" [if condition] is a music room track, unlocked when heard; the file can be an alias. achievement id "Title" ["Description"] [hidden] declares an achievement, granted with achieve id; a hidden one shows its description only once earned. The Gallery, Music Room and Achievements menu entries appear only when the story defines entries for them.
translate
translate zh-Hans pro_yuelao_01:
yuelao "天下每一个魂灵,在我的簿子上都有一行。"
translate zh-Hans tea_narr_02:
pass
translate zh-Hans strings:
old "Su Qinglan"
new "苏清岚"
old "Bow and answer with a verse"
new "作揖,以诗相答"
translate language id: translates one line: its body is one or more say statements, joined with paragraph breaks, and may give the line to a different speaker; pass drops the line in that language. translate language strings: translates text by its exact original: menu choices, character names, prompts, notifications, gallery and achievement titles, translate("...") calls, and the engine's own interface words. See Translations.
Statements
Statements go inside labels.
Dialogue
"Narration: a line with no speaker."
q "Qinglan speaks."
q smile "She smiles as she speaks, and keeps smiling."
q @ surprised "Surprised for this line only."
q annoyed @ blush "Annoyed from now on, blushing for this line."
"Pedlar" "A one-off speaker, named in quotes."
q "A line with its own id." id garden_q_01
extend " And a little more, appended to the line before."
A say statement is an optional speaker, optional image attributes, and the text. Attributes before @ change the speaker's image and stay; attributes after it last only while the line is up. A line may end with id name, which gives it an explicit line id. extend continues the previous line: the earlier text stays and the new text types on after it, inside the character's closing quotation mark. A line may also end with with transition: the speaker's image, if the character has one, changes to the line's attributes, the transition plays, and then the line appears.
show, scene and hide
show qinglan smile
show qinglan sad at left with dissolve
show xiaotao grin at left, hop with easeinleft
show lantern at truecenter behind qinglan zorder 2
show qinglan smile as twin at right
show fog onlayer overlay
show qinglan:
xalign 0.0
linear 2.0 xalign 1.0
scene bg teahouse with fade
scene
scene onlayer overlay
hide qinglan with dissolve
hide fog onlayer overlay
show name [at t1, t2] [behind tag, tag] [zorder n] [as tag] [onlayer layer] [with transition][:] shows an image, or changes the one showing with the same tag. at applies transforms in order; behind places it under the named tags; zorder orders it within its layer; as gives it a different tag, so the same image can be shown twice; onlayer picks the layer; a block under the line is an inline animation. With attributes left out, show qinglan keeps what is showing and only changes the clauses.
scene [onlayer layer] [name [clauses]] [with transition] clears a layer (master by default) and optionally shows a background; its transition covers both. hide tag [onlayer layer] [with transition] removes an image; attributes after the tag are allowed and ignored.
with
show qinglan smile at right
show xiaotao grin at left
with dissolve
with transition on its own line runs a transition over everything that changed since the player last interacted. with none makes the change instant.
pause
pause
pause 1.5
pause 3.0 hard
Waits for a click, or for a time; hard cannot be clicked through. A pause with no time is a checkpoint, like a line.
menu
menu:
q "Which do you choose?"
"The hairpin":
$ gift = "hairpin"
"The fan" if affection >= 2:
$ gift = "fan"
"The book of poems" id market_choice_book:
$ gift = "book"
jump bookstall
A menu holds an optional line first (shown with the choices), then choices: quoted text, an optional id name, an optional if condition, a colon and a block. A choice whose condition is false is hidden; a menu with no choice left to show is skipped. After a choice, _choice holds its text and _choice_index its index, and the choice is recorded in the history. Choices taken in an earlier playthrough are drawn dimmer. Number keys pick choices.
menu set variable: records each chosen choice's text in a list variable and hides choices already in it, which is the "ask about each topic once" pattern:
$ asked = []
while len(asked) < 3:
menu set asked:
"Ask about the lanterns":
keeper "Three riddles, from Master Su's own brush."
"Ask about the bridge":
keeper "Lit for the festival."
"Ask about the river":
keeper "Every lantern on it is a wish."
A word after menu names it, and a named menu can be jumped to or called like a label, from anywhere in the story; the lint accepts it as a target. The menu is shown as if the story had reached it, and after a choice the story goes on from there, through what follows the menu in its label. When a label has the same name, the label is the target. A menu takes no arguments: calling one with arguments is an error, which the lint reports.
label market:
menu gifts:
"Which gift for Qinglan?"
"The hairpin":
$ gift = "hairpin"
"Nothing yet":
jump wander
jump teahouse
label wander:
"You walk the stalls once more."
jump gifts
jump, call and return
jump teahouse
jump expression "ending_" + outcome
call chapter_title("Chapter One", "The Teahouse")
call expression next_scene
return
return affection * 10
jump continues at a label, or at a named menu; inside a called label, a jump stays inside the call, so a later return still comes back. call runs a label and continues after the call when it returns, binding arguments to the label's parameters. return value puts the value in _return. jump expression and call expression compute the label's name; a computed call takes no arguments.
if, elif, else and while
if riddles_solved == 3:
achieve lanterns
elif riddles_solved > 0:
q "Not bad."
else:
q "Not even the moon?"
while candles > 0:
"A candle goes out."
$ candles -= 1
An empty block is an error; write pass when nothing should happen. A loop must reach a line, menu or pause, or the story stops with an error after a million statements.
$ assignments
$ affection += 1
$ player_name = trim(player_name) or "Lu Wenjing"
$ inventory += "fan"
$ inventory -= "fan"
$ persistent.endings_seen += "good"
$ score //= 2
$ name = expression assigns; +=, -=, *=, /=, //= and %= combine. $ expression evaluates an expression for its effect.
play, queue, stop and voice
play music theme fadein 3.0
play music "audio/bgm/town_day.ogg" if_changed
play ambience crowd fadein 2.0 volume 0.3
play sound ["audio/sfx/door.ogg", "audio/sfx/bell.ogg"]
queue music romance
stop music fadeout 3.0
voice "voice/qinglan_014.ogg"
q "Listen."
voice sustain
q "The voice from the line before keeps playing."
play channel file [fadein n] [fadeout n] [loop|noloop] [volume v] [if_changed] plays a quoted file, an alias, or a bracketed list played in order. fadeout fades out what was playing; if_changed does nothing if the same file is already playing. queue plays after what is playing. stop channel [fadeout n] stops. voice file sets the voice for the next line, and voice sustain keeps the current voice playing through the next line. With config.auto_voice set (for example voice/{id}.ogg), a line plays the voice file named by its id when the file exists; {character} in the pattern is the speaker's voice tag or id.
movie
movie "video/opening.mp4"
movie "video/credits.webm" noskip keepmusic
Plays a full-screen movie. It stops the music unless keepmusic is given, and a click skips it unless noskip is given.
input
input player_name "What is your name?" default "Lu Wenjing" length 16
input code "The lock has four digits." allow "0123456789" length 4
input variable "Prompt" [default "text"] [length n] [allow "characters"] asks the player to type. The default may interpolate (default "[translate('Lu Wenjing')]"); an empty answer keeps the default.
window and nvl clear
window hide
scene bg night_sky with fade
window show with dissolve
window auto
nvl clear
window show|hide|auto [with transition] sets the dialogue window's visibility; auto shows it for dialogue and hides it otherwise. nvl clear starts a fresh NVL page.
run
run fortune
run riddle answer, 3
run lantern("red", 3)
$ slip = _return
run name [arguments] runs a command the game implements in C# or Divooka, with arguments as comma-separated expressions; its result goes into _return. Every command a story uses must be registered, or the story stops with an error (and the lint reports it). See Development and Novella in Divooka.
Custom screens
show screen hud(gold)
hide screen hud
call screen gift_picker(["hairpin", "fan", "book"])
$ gift = _return
show screen name(arguments) puts a custom screen over the game, where it stays (and is restored by loading) until hide screen name. call screen name(arguments) shows one and waits for it to return a value, which goes into _return; if the player closes it without answering (Escape, or the screen's own Close), _return is none. Screens are written in C# and registered by the game; there is no screen language yet. show screen and hide screen with with transition play the transition over the stage. A called screen is not part of the stage, so call screen ... with transition fades the screen in over the transition's duration, whatever kind of transition it names.
notify, achieve and pass
notify "Added to the gallery: Lanterns on the River"
achieve end_good
pass
notify shows a notification for a few seconds; achieve grants an achievement (once) and announces it; pass does nothing.
Text tags
Lines of dialogue accept Ren'Py's text tags. Style tags are closed by the matching {/tag}; closing a tag also closes any opened inside it.
| Tag | Effect |
|---|---|
{b}, {i}, {u}, {s} |
Bold, italic, underline, strikethrough. |
{plain} |
Turns bold, italic, underline and strikethrough off. |
{color=#f88} |
Text colour. |
{outlinecolor=#000} |
An outline around the text. |
{size=40}, {size=+8}, {size=-4}, {size=*1.5} |
An absolute size, or one relative to the surrounding text. |
{font=Family} |
A font family. |
{alpha=0.5} |
Opacity. |
{a=target} |
A link, clickable once the line has finished showing: jump:label, label:label or a bare label name jumps there; call:label runs the label and then shows the same line again; an http or https address opens in the browser. |
{rb}漢字{/rb}{rt}かんじ{/rt}, {ruby=pīnyīn}字{/ruby} |
Ruby text (furigana, pinyin) above the base. |
{w}, {w=0.5} |
Wait for a click, or for a time, before typing on. |
{p}, {p=1.0} |
A line break, then wait. |
{nw} |
Do not wait at the end of the line: advance as soon as it has typed out. |
{fast} |
Show everything before this at once. |
{cps=20}, {cps=*2} ... {/cps} |
Typing speed in characters per second, or as a multiple of the player's. |
{image=gui/heart.png} |
An image inline with the text. |
{space=20} |
Horizontal space. |
{#comment} |
Ignored: a note for translators. |
[expression] interpolates a value: "Welcome, [player_name].", "[len(inventory)] items". {{ and [[ are a literal brace and bracket. A tag Novella does not know is shown as text, except {vspace}, {k} and {clear}, which are accepted and ignored.
Expressions
Expressions appear in $ lines, conditions, default and define, arguments, [interpolation] and if clauses. They are a small, safe language with Python's grammar where writers reach for it and the C spellings accepted beside it.
Values are numbers, strings (single or double quotes), true and false (also True, False), none (also None, null), and lists ([1, "fan", true]). Variables are names; persistent.name reads the persistent store. Lists and strings index from zero, and negative indices count from the end: inventory[0], name[-1]. A missing index is none.
Operators, from loosest to tightest:
| Operators | Notes |
|---|---|
x if c else y, c ? x : y |
Conditional. |
or, \|\| |
Returns the deciding operand, so name or "stranger" is a default. |
and, && |
Returns the deciding operand. |
not, ! |
|
==, !=, <, <=, >, >=, in, not in |
Comparisons chain: 1 < x <= 5. in tests membership of a list or a substring. |
+, - |
+ joins strings, appends to a list (inventory + "fan") or concatenates lists; - removes every occurrence from a list. |
*, /, //, % |
/ is exact division, // floor division; "ab" * 3 repeats a string. |
unary -, + |
|
** |
Power. |
Functions
| Function | Result |
|---|---|
len(x) |
The length of a list or string. |
min(...), max(...), sum(...) |
Of the arguments, or of the items of one list argument. |
abs(x), round(x[, digits]), floor(x), ceil(x), clamp(x, low, high) |
Arithmetic. |
int(x), number(x), str(x), bool(x) |
Conversions; int truncates. |
upper(s), lower(s), trim(s), capitalize(s) |
Text. |
join(list[, separator]), split(text[, separator]) |
Joining defaults to ", ", splitting to a space. |
contains(x, item), has(x, item) |
Membership, as in. |
count(list, item), append(list, item), remove(list, item), unique(list), sorted(list) |
List helpers; each returns a new list. |
range(n), range(a, b) |
The integers from 0 (or a) up to but not including the end. |
random(), random(n), random(a, b) |
A number in 0..1, an integer in 0..n-1, or an integer from a to b inclusive. |
chance(p) |
True with probability p. |
choice(list) or choice(a, b, ...) |
A random item. |
defined(name), get(name[, default]) |
Whether a variable (named as a string) has a value; its value or a default. |
visits(label), visited(label) |
How many times, and whether, a label has been entered in this playthrough. |
seen_label(label) |
Whether a label has been reached in any playthrough. |
seen_image(name), heard(file) |
Whether an image has been shown, or audio played, in any playthrough. |
achieved(id) |
Whether an achievement has been granted. |
showing(tag) |
Whether an image with the tag is on the stage. |
language() |
The current language, or an empty string for the original. |
translate(text) |
Text through the current language's string translations. |
Random draws come from the game's own generator, which is part of the saved and rolled-back state, so rolling back and choosing again draws the same result. Games add functions from C# with RegisterFunction.
Animation
A transform's body, or a block under show or scene, is a timeline in the style of Ren'Py's ATL. Each line is one of:
- A property setting, taking effect at once. Several may share a line:
xalign 0.5 yalign 1.0 alpha 0.0. - An interpolation: a warper, a duration and properties,
ease 1.0 xalign 1.0 alpha 1.0, which animates from the current values to these. The properties may also go in a block under the warper line. - A pause:
pause 0.5, or a bare number. repeatorrepeat n: start the enclosing block again, forever, or until it has runntimes in all.block:: a nested timeline, so arepeatcan loop part of an animation.parallel:: consecutiveparallelblocks run at the same time, each animating its own properties.- Another transform's name, with arguments if it takes them, included in place:
left,bob(30).
transform drift:
alpha 0.0
ease 2.5 alpha 1.0
block:
ease 3.0 yoffset -14
ease 3.0 yoffset 0
repeat
transform hop:
yoffset 0
easeout 0.12 yoffset -28
easein 0.14 yoffset 0
repeat 2
transform breathe_and_sway:
parallel:
ease 2.0 zoom 1.02
ease 2.0 zoom 1.0
repeat
parallel:
ease 3.0 xoffset 6
ease 3.0 xoffset -6
repeat
Properties: xpos, ypos, pos, xanchor, yanchor, anchor, xalign, yalign, align, xcenter, ycenter, center, xycenter, xoffset, yoffset, offset, zoom, xzoom, yzoom, alpha, rotate, blur, saturation, brightness, tint, xsize, ysize, size. Pair properties take two numbers or a bracketed pair: pos 100 200, align (0.5, 1.0). tint takes a colour: tint "#ffd0d0". A negative xzoom or yzoom mirrors.
Positions: a number written with a decimal point is a fraction (of the screen for positions and alignments, of the image for anchors), and an integer is pixels: xpos 0.25 is a quarter of the way across, xpos 320 is 320 pixels. Built-in positions: left, right, center, midleft, midright, truecenter, top, topleft, topright, offscreenleft, offscreenright, fullscreen, default and reset.
Warpers: linear, ease, easein, easeout, pause (holds, then jumps at the end), and the families ease_quad, ease_cubic, ease_quart, ease_quint, ease_expo, ease_circ, ease_back, ease_elastic and ease_bounce, each also as easein_... and easeout_....
Values: a duration or value may be a number, a parameter's name, or a bracketed expression: ease (speed * 2) yoffset (-height). Timeline expressions see the transform's parameters and the built-in functions, not the game's variables.
Not supported: the ATL statements choice, contains, time, on, event and function, which the parser reports as errors, and Ren'Py's polar, crop, tiling and shader properties.
Transitions
A transition goes after with: show qinglan with dissolve, scene bg night with fade, with pixellate. Built-in transitions:
| Name | Effect | Default | Arguments |
|---|---|---|---|
dissolve, crossfade |
Cross-fade. | 0.5 s | (seconds[, warper]) |
fade |
Fade to a colour, hold, fade in. | 0.5 s out, 0.5 s in, black | (out[, hold[, in[, "#colour"]]]); with one argument, in equals out |
flash |
Flash to white and back. | 0.1 s out, 0.4 s in | as fade |
pixellate |
Pixellate out, then in. | 1.0 s, 5 steps | (seconds[, steps]) |
move, ease |
Images that stayed glide to their new places; others dissolve. | 0.5 s, eased | (seconds[, warper]) |
moveinleft, moveinright, moveintop, moveinbottom |
Entering images slide in from that side. | 0.5 s | (seconds[, warper]) |
moveoutleft, moveoutright, moveouttop, moveoutbottom |
Leaving images slide out to that side. | 0.5 s | (seconds[, warper]) |
easeinleft ... easeoutbottom |
As movein/moveout, eased. |
0.5 s | (seconds[, warper]) |
slideleft, slideright, slideup, slidedown |
The new picture slides in over the old. | 1.0 s | (seconds[, warper]) |
slideawayleft ... slideawaydown |
The old picture slides away. | 1.0 s | (seconds[, warper]) |
pushleft ... pushdown |
The new picture pushes the old one off. | 1.0 s | (seconds[, warper]) |
wipeleft ... wipedown |
A hard edge sweeps across. | 1.0 s | (seconds[, warper]) |
irisin, irisout |
A circle opens, or closes. | 1.0 s | (seconds[, warper]) |
zoomin, zoomout |
The new picture zooms. | 0.5 s | (seconds[, warper]) |
vpunch, hpunch |
Shakes the screen. | 0.4 s, 10 px | (seconds[, pixels]) |
blinds, squares |
Venetian blinds, or a grid of squares. | 1.0 s, 8 blinds or 12 squares | (seconds[, count]) |
imagedissolve |
Pixels change in the order of a grayscale mask's brightness. | 1.0 s, ramp 32 | ("mask.png"[, seconds[, ramp[, reverse]]]) |
none |
Instant. |
Named transitions (transition slow = dissolve(2.0)) can be used with or without further arguments.
Translations
Write the game in its original language (config.original_language names it in the preferences), then add a script per language. The language picker in the preferences lists every language the story translates.
# zh_hans.novella
translate zh-Hans strings:
old "Su Qinglan"
new "苏清岚"
old "Chapter One"
new "第一章"
translate zh-Hans tea_wang_03:
wang "我已经替你相中了一位好姑娘!"
translate zh-Hans tea_narr_07:
"茶馆里一下子安静了。"
"连掌柜都停下了算盘。"
- Lines are translated by id. Giving every line an explicit id (
id tea_wang_03) keeps translations attached when the original text is edited; without one, the id is the label's name plus a hash of the speaker and text, which changes when the text does. Several say statements in one block join with paragraph breaks; a translation may name a different speaker;passdrops the line in that language. - Strings are translated by exact original text: menu choices, character names, prompts, notifications, gallery, music room and achievement titles, and anything passed through
translate("..."). A string table can also override the engine's own interface words, such asold "Start"/new "开篇". - The engine's interface (menus, preferences, confirmations, the help screen) is already translated into Simplified Chinese, Traditional Chinese and Japanese; see Theming.
- Fonts: put a CJK font after the Latin one in a theme's font list so both scripts render, as described in Theming.
The lint reports how many lines each language translates. config.default_language sets the language a fresh install starts in.