繁體中文
English 简体中文 繁體中文
Methodox 首頁

指令碼語言

Novella 指令碼是副檔名為 .novella 的 UTF-8 文字檔案。該語言的形式與 Ren'Py 相似,因此熟悉 Ren'Py 的人會感到得心應手。條件和賦值使用 Novella 自己的 表示式語言,遊戲所需的其他任何功能,則是用 C# 或 Divooka 實現的 run 命令,或者在選擇啟用 Python 的遊戲中,是一個 Python 程式碼塊。

本頁面是參考文件。概念 解釋語句所作用的模型,主題設定 列出每個 config. 和 theme. 設定,樣式 完整介紹 style 語句。

佈局

縮排構成程式碼塊:以冒號結尾的行開啟一個塊,縮排在其下的行是它的主體。製表符按四個空格計算。註解以字串之外的 # 開頭,一直到行尾。

# 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."

在括號內、行尾反斜槓之後或三引號字串內,一個邏輯行可以跨越換行繼續。在三引號字串中,公共縮排會被去除,單個換行會像散文自動換行一樣合併為空格,而空行表示換行,因此長段落可以按段落書寫:

    """
    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.
    """

字串使用單引號或雙引號;\n、\t、\\、\" 和 \' 是轉義序列。

多個指令碼按路徑順序載入,合併為一個故事。一個標籤會落入同一檔案中緊隨其後的下一個標籤;從檔案的最後一個標籤末尾落出會結束遊戲,或者返回到呼叫者。

定義

定義位於指令碼的頂層,在任何標籤之外。它們的順序無關緊要:定義在遊戲開始之前就已就位。

label

label start:
    "The story begins."

label chapter_title(number, title="Untitled"):
    centered "[number]\n[title]"
    return

標籤是一個具名的塊。參數由 call chapter_title("One", "The Teahouse") 繫結,僅在該次呼叫中有效,並且可以有預設值。沒有參數的標籤同樣可以被跳轉或呼叫。

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] 定義一個說話者。名字中可以插入變數;用 none 代替名字則不顯示名字;省略名字則使用 id。選項:

選項 含義
color "#hex"(或 name_color) 名字的顏色。
text_color "#hex"(或 what_color) 對話的顏色。
image tag 角色的影像標記,這樣 q smile "..." 就會更換其圖片。
side 在對話方塊內以側邊影像顯示角色的影像。
kind adv\|nvl\|centered 對話方塊、全屏 NVL 頁面,或不帶對話方塊的置中文字。單獨的 nvl 是 kind nvl 的簡寫。
prefix "..."、suffix "..." 加在每句臺詞前後的文字,例如引號。
font "Family"、size 30 該角色臺詞的字型族和字型大小。
voice tag 該角色在自動語音模式(config.auto_voice)中使用的標籤。
beep "file" 文字逐字顯示時播放的聲音。
window_style "name"、namebox_style "name"、who_style "name"、what_style "name" 用於角色臺詞某個部件的 樣式,代替內建樣式:依次為對話方塊、名字標籤、名字和臺詞。
window_background "#200000"、what_size 40、who_color "#ffff00"、... 部件名與任意 樣式屬性 連在一起,即為該部件設定該屬性,僅對這個角色有效。

narrator、centered 和 nvl_narrator 是內建的。define character ... 是另一種寫法。

style ghost_window is say_window:
    background "#10202cd0"

character ghost "Ghost" window_style "ghost_window" what_size 40 who_color "#a8d8ff"

在較早的選項有所指定時,它們優先於部件的樣式:color 為名字著色,除非 who 樣式設定了自己的顏色;font、size 和 text_color 設定臺詞的字型、字型大小和顏色,覆蓋 what 樣式。what_color 是 text_color 的另一種寫法,而不是部件屬性。

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 定義一個影像。大多數遊戲都不需要這些定義,因為 影像檔案會自行命名。來源是以下之一:

來源 含義
"path/file.png" 遊戲資源中的一個檔案。
"#rgb"、"#rrggbb"、"#rrggbbaa" 填滿螢幕的純色。
movie("file"[, loop]) 作為影像播放的影片;除非給出 false,否則循環播放。
placeholder("Label"[, background]) 引擎的替代圖:一個剪影,或在 background 為 true 時為漸層。
grayscale(s)、sepia(s) 去色,或去色後加暖色調。
tint(s, "#ffd0d0") 與某種顏色相乘。
flip(s[, vertical]) 水平鏡像,或在為 true 時垂直鏡像。
zoom(s, 0.5) 縮放。
crop(s, x, y, w, h) 來源中的一個矩形區域,以其像素為單位。
blur(s, 4)、alpha(s, 0.5)、brightness(s, 0.2)、saturation(s, 0.5) 經濾鏡處理。
composite(w, h, x, y, s, x, y, s, ...) 在指定尺寸的畫布上,按偏移繪製的多個來源。
switch(condition, s, condition, s, ..., default) 第一個條件成立的來源,每次繪製該影像時重新選擇。
frame(s, left, top[, right, bottom]) 按自身邊框作為九宮格拉伸的圖片,與 Ren'Py 的 Frame 相同:用於 樣式的背景;在舞臺上則按圖片原樣顯示。
solid("#203040") 一種顏色,與 Ren'Py 的 Solid 相同;等同於帶引號的顏色。

濾鏡可以巢狀:blur(grayscale("bg/garden.jpg"), 6)。define image ... 是另一種寫法。black 和 white 作為純色內建,無需定義。

影像也可以是一條時間線,這就是逐幀動畫的做法:image name: 下面跟一個 動畫 塊,塊中的圖片行說明它顯示什麼、何時顯示。

image candle:
    "candle a"
    pause 0.5
    "candle b" with dissolve
    pause 0.5
    repeat

show candle at left 會顯示閃爍的蠟燭,無論它在哪裡顯示、變換如何移動它,都會持續閃爍。

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"

分層影像由多個圖層繪製而成:先繪製每個 always 圖層,然後對每個分組繪製所請求的屬性,或該分組的 default。同一分組中的屬性互斥,除非該分組是 multiple。帶 if 的屬性只在其條件成立時繪製;位於任何分組之外的 attribute 行自成一個分組。size w h 固定畫布尺寸,否則畫布尺寸為第一個圖層的尺寸。任何組合都可以顯示,未提及的屬性會被保留:在 show qinglan festival smile 之後,show qinglan sad 仍保留節日服裝。

transform

transform bob(height=20):
    yoffset 0
    ease 1.0 yoffset (-height)
    ease 1.0 yoffset 0
    repeat

一條具名的動畫時間線,可選擇帶參數,通過 show ... at bob 或 at bob(40) 使用。參見 動畫。

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")

一個具名轉場,由某個內建轉場加實參構建,或由另一個具名轉場構建。參見 轉場。

audio

audio theme = "audio/bgm/main_theme.ogg"
define audio.bell = "audio/sfx/bell.ogg"

音訊別名,這樣指令碼就可以寫 play music theme 和 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] 定義一個音訊通道。混音器決定由哪個音量滑塊控制它;loop 使其預設迴圈;fadeout 是它被替換或停止時的預設淡出時長;norestore 使它播放的內容不進入存檔和回滾。內建通道:

通道 混音器 迴圈 預設淡出 讀檔時恢復
music music 是 0.5 秒 是
sound sfx 否 無 否
voice voice 否 無 否
ambience sfx 是 1.0 秒 是
audio sfx 否 無 否

既非內建也未定義的通道名,會作為不迴圈的音效播放。

default 和 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 是新遊戲開始時變數的值;對於 persistent. 名稱,只有在該持久變數尚未設定時才會設定。define name = expression 是一個常量,在遊戲開始時設定,並在每次讀檔後再次設定,因此對它的更改也會作用於舊存檔。define config.key = value 和 define theme.key = value 設定 配置和主題;它們的值只求值一次,且不使用遊戲變數。

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] 是一個畫廊條目,在其中每個影像都被看到後(或在條件成立時)解鎖;其影像按順序顯示,第一個作為縮圖。track file "Title" [if condition] 是一首音樂空間曲目,被聽到後解鎖;檔案可以是別名。achievement id "Title" ["Description"] [hidden] 宣告一個成就,用 achieve id 授予;隱藏成就只有在獲得後才顯示其描述。只有當故事為 Gallery、Music Room 和 Achievements 定義了條目時,這些選單項才會出現。

screen

screen riddle(question="What has roots that nobody sees?"):
    dim
    text "[question]" at (0.5, 0.3) size 44
    button "A mountain" at (0.35, 0.6) return "mountain"
    button "A river" at (0.65, 0.6) return "river" if visited("river_bank")
    button image "gui/map_village.png" hover "gui/map_village_hover.png" at (420, 300) set went_village true jump village
    bar wisdom max 3 at (0.5, 0.05) size (300, 18)
    timer 20 return "timeout"

screen name[(parameters)]: 定義一個介面,show screen 將其疊加在遊戲之上,call screen 則等待它作答。參數的用法與標籤相同,可選擇帶預設值,由 show screen 或 call screen 的實參繫結。介面是故事資料,因此會被 lint 檢查,並像其他定義一樣以指令碼形式寫出。遊戲在 C# 中以相同名稱註冊的介面優先於已定義的介面,因此遊戲無需改動指令碼就能換上手工製作的介面。

介面每幀都會根據故事的變數和它自己的變數重新計算,因此 if 會在其條件成立的那一刻出現,for 會為列表中的每個條目生成一行,進度條會跟隨其變數變化。玩家所觸及的內容——按鈕的懸停和焦點、輸入框中的輸入、視口的滾動——按元素在介面中的位置儲存,因此在介面重新佈局後依然保留。元素可以像上面那樣逐個放置在舞臺上,也可以用容器佈局:

screen inventory:
    modal
    default sorted_view = false
    frame align (0.5, 0.5) padding 30 size (700, 560):
        vbox spacing 14:
            text "Your bag" size 44 align (0.5, 0.0)
            viewport size (640, 300) spacing 8:
                for item in bag:
                    button "[item]" size (600, 56) set chosen item close
            if not bag:
                text "Nothing yet."
            hbox spacing 20:
                button "Sort" toggle sorted_view
                button "Close" close
    key "escape" close

screen hud:
    hbox align (1.0, 0.0) spacing 12 padding 14 background "#00000090":
        text "Trust [trust]" size 28
        bar trust max 10 size (200, 18)
        button "Save" open save

位於介面頂部、在其元素之前或之間的行:

行 含義
dim 使介面後面的遊戲變暗。
modal 顯示的介面會攔截所有點選,因此在它顯示期間,下面的故事無法推進。被呼叫的介面總是如此。
background "image" 覆蓋介面元素後方舞臺的影像名稱或檔案。
style_prefix "name" 介面元素 樣式 的字首:使用 "gold" 時,如果故事中有相應樣式,按鈕用 gold_button 繪製,其標籤用 gold_button_text 繪製。
default name = expression 介面自己的變數,在介面開啟時設定,在介面存在期間一直有效。介面的按鈕可以設定和切換它,介面的文字和條件可以讀取它。

元素:

行 含義
text "..." [size n] [color "#hex"] [width n] 文字,可以插入 [variable] 和介面的參數;width 是換行前文字延伸的像素寬度。
image "name, file or colour" [zoom z] 一張圖片。
button "Text" [size (w, h)] [sensitive value] actions 使用主題按鈕框的按鈕,除非給出以像素為單位的尺寸,否則按其文字確定大小。使用 sensitive 時,只有在該值成立時才能按下,否則顯示為暗色。
button image "file" [hover "file"] actions 可按下的圖片,例如地圖上的某個地點,指標懸停時顯示另一張圖片(或將同一張圖片調亮)。
bar value max maximum [size (w, h)] 顯示某個值相對於最大值的進度條,例如好感度計量條;未指定尺寸時為 300×20 像素。
input variable [length n] [allow "characters"] [width n] 向變數中輸入內容的文字方塊,例如輸入名字。
space (w, h) 空白區域,用於在容器中隔開元素。
timer seconds [repeat] actions 時間到後執行一次,就像限時選項到時一樣;使用 repeat 時每次到時都執行。它不繪製任何內容。
key "name" actions 在介面顯示期間按下某個鍵時執行:escape、enter、space、left、某個字母、f1 等。它不繪製任何內容。

容器包含其下方塊中的元素,並接受 spacing n(子元素之間的像素數)、padding n(邊緣與子元素之間的像素數)、background "colour or image" 和 size (w, h)(像素;不指定時容器會適應其內容):

容器 子元素的佈局方式
vbox 上下排列。
hbox 左右並排。
grid columns 按指定列數分行排列。
frame 位於使用主題顏色的面板上,各自位於自身位置所指定之處。
fixed 各自位於自身位置所指定之處。
viewport 上下排列,按其尺寸裁剪,放不下時可用滑鼠滾輪滾動。

樣式。 每個元素都用一個 樣式 繪製:如果用 style "name" 指定了樣式,就用該樣式;否則用其型別在包含它的介面或容器的字首下的樣式(容器與介面一樣,接受 style_prefix "name",作用於其所含內容);否則用其型別自己的樣式:text、button 和 button_text、frame、vbox、hbox、grid、fixed、viewport、bar、input 和 input_text。任何樣式屬性也可以寫在元素本身上、位於其其他子句之後,僅作用於該元素:text "Hi" outline_width 2、vbox background "#14202ae8" corner_radius 8:。在按鈕上,帶 text_ 字首的屬性設定其標籤的樣式:button "Buy" text_color "#ffffff" return 1。容器未自行設定內邊距、間距和尺寸時,會從其樣式中取得。介面上的文字測量時向上取整到下一個整像素,因此能在一行內放下的標題,不會因其自身寬度的舍入而被換成兩行。

流程:

行 含義
if condition:、elif condition:、else: 第一個條件成立的分支中的元素。
for name in list: 對列表中的每個條目,將其下方的元素各生成一次,條目存放在 name 中:袋子裡的每件禮物一行。
use other_screen(arguments) 在此處放入另一個介面的元素,並繫結其參數,例如多個介面共用的狀態列。

定位。 在介面本身、fixed 或 frame 中,元素的定位方式有三種:用 at (x, y),使其以該點為中心;用 align (x, y),使元素的相同比例處對齊區域的相同比例處,因此 align (1.0, 1.0) 是右下角,align (0.0, 0.0) 是左上角;或者用 pos (x, y) 配合 anchor (x, y),分別指定兩者。與 動畫 中一樣,帶小數點的數字是比例,整數是像素:(0.5, 0.3) 是靠近頂部的中間位置,(420, 300) 是橫向 420 像素、向下 300 像素。不使用其中任何一種時,元素位於中間。在 vbox、hbox 或 grid 中,由容器放置其子元素,align 只說明子元素在容器橫向上的位置。任何元素都可以以 if condition 結尾,這樣只在條件成立時顯示。

按鈕、計時器或按鍵的動作按順序執行:

動作 作用
return value 回答被呼叫的介面;故事以 _return 讀取該值。在顯示的介面上,它會關閉介面。
jump label 關閉介面,並在該標籤處繼續故事。正在進行的呼叫仍在進行,因此之後的 return 仍會返回。
set variable value 設定一個變數:如果介面自己有同名變數,就設定它,否則設定故事的變數。
toggle variable 把為真的變數變為假,為假的變為真。
notify "text" 顯示一條通知。
play "sound" 播放一次聲音。
open menu 開啟遊戲的某個選單:save、load、preferences、history、gallery、music_room、achievements 或 main_menu。
close 關閉介面;被呼叫的介面回答 none。
show name、hide name 按名稱顯示或隱藏另一個介面,不帶實參。
run command 執行遊戲註冊的命令,不帶實參;其結果放入 _return。

值可以是帶引號的字串、數字、名稱或括號中的表示式:set gold (gold - 5),在 for 中則如 set chosen item。顯示的介面只響應它自己的 key 行,因此其下的故事仍可使用方向鍵和 Enter。每個介面,無論是內建的還是遊戲自己的,都可以用鍵盤和 遊戲手柄 操作,因為手柄的按鈕就是介面已經響應的那些鍵。無頭遊玩時,按下被呼叫介面的按鈕所用的位置,與選單所用的位置相同。

lint 會檢查介面的跳轉、它顯示、隱藏和使用的介面(以及 use 傳遞的實參個數是否正確,或介面是否使用了自身)、它執行的命令、其表示式呼叫的函式和讀取的未設預設值的變數、它顯示的影像和其容器的背景,以及 show screen 和 call screen 傳給它的實參是否多於其參數。

lint 還會報告元素所指定、但故事未定義的樣式,以及元素上不是樣式屬性或值型別錯誤的屬性。

介面不涵蓋的是 Ren'Py 的拖放和影像對映(imagemap)的熱點裁剪;需要這些功能的介面,應寫成 C# 類。

style

style gold_button is button:
    background "#3a2a10"
    hover_background "#6a4a18"
    corner_radius 12

style gold_button_text is button_text:
    color "#e8dcc6"
    hover_color "#ffd27a"
    insensitive_color "#7d7568"

style say_dialogue:
    outline_width 2
    outline_color "#1a0f0a"

style name [is parent]: 定義一個樣式:一種具名外觀,包含用於文字和容器的屬性,每行一個,未設定的屬性從父樣式繼承。帶 idle_、hover_、selected_、insensitive_ 或 visited_ 字首的屬性在相應狀態下生效。同名的第二個 style 塊會補充第一個;與 Novella 自有樣式(say_dialogue、say_window、choice_button 等)同名的樣式,會細化每個遊戲介面的相應部分,而不是替換它。介面用 style 和 style_prefix 指定樣式,角色用 window_style 等指定,臺詞用 {style=name} 指定。樣式 是完整的參考:沿父樣式和狀態的尋找、每個屬性、內建樣式,以及 lint 檢查的內容。

init python

init python:
    def title_for(level):
        titles = ["stranger", "friend", "confidant"]
        return titles[min(int(level), len(titles) - 1)]

在選擇啟用 Python 的遊戲中,遊戲開始時執行一次的 Python;它定義的函式可以像內建函式一樣,從故事中的任何表示式呼叫:"You are a [title_for(trust)]."。參見 Python。

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: 翻譯一句臺詞:其主體是一個或多個說話語句,以段落分隔符連線,並且可以把這句臺詞交給另一個說話者;pass 表示在該語言中刪去這句臺詞。translate language strings: 按原文精確匹配翻譯文字:選單選項、角色名、提示、通知、畫廊和成就標題、translate("...") 呼叫,以及引擎自己的介面用語。參見 翻譯。

語句

語句位於標籤內部。

對話

    "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."

說話語句由可選的說話者、可選的影像屬性和文字組成。@ 之前的屬性會更改說話者的影像並保持不變;它之後的屬性只在這句臺詞顯示期間有效。臺詞可以以 id name 結尾,為它指定一個顯式的 臺詞 id。extend 延續上一句臺詞:先前的文字保留,新文字緊接其後逐字顯示,位於角色的後引號之內。臺詞也可以以 with transition 結尾:如果角色有影像,說話者的影像會先更換為該臺詞的屬性,接著播放轉場,然後臺詞才出現。

show、scene 和 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][:] 顯示一個影像,或更換正在顯示的同標籤影像。at 按順序應用變換;behind 把它放在指定標籤的下面;zorder 決定它在所在圖層內的順序;as 為它指定另一個標籤,使同一個影像可以顯示兩次;onlayer 選擇 圖層;該行下方的塊是一段內聯動畫。省略屬性時,show qinglan 保留正在顯示的內容,只更改各子句。

scene [onlayer layer] [name [clauses]] [with transition] 清空一個圖層(預設為 master),並可選擇顯示一個背景;其轉場同時作用於兩者。hide tag [onlayer layer] [with transition] 移除一個影像;標籤之後允許寫屬性,但會被忽略。

with

    show qinglan smile at right
    show xiaotao grin at left
    with dissolve

單獨一行的 with transition 會對自玩家上次互動以來發生變化的所有內容執行轉場。with none 使變化立即生效。

pause

    pause
    pause 1.5
    pause 3.0 hard

等待一次點選,或等待一段時間;hard 無法通過點選跳過。不帶時間的暫停與一句臺詞一樣,是一個檢查點。

    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

帶標題臺詞的選單

選單首先包含一句可選的臺詞(與選項一起顯示),然後是選項:帶引號的文字、可選的 id name、可選的 if condition、一個冒號和一個塊。條件為假的選項會被隱藏;沒有可顯示選項的選單會被跳過。做出選擇後,_choice 儲存其文字,_choice_index 儲存其索引,該選擇會記錄到歷史記錄中。在之前的遊玩中選過的選項會顯示得暗一些。數字鍵可以選擇選項。

menu set variable: 把每個已選選項的文字記錄到一個列表變數中,並隱藏其中已有的選項,這就是“每個話題只問一次”的模式:

    $ 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."

menu 後面的單詞為選單命名,具名選單可以在故事的任何地方像標籤一樣被跳轉或呼叫;lint 接受它作為目標。選單會像故事執行到它那樣顯示,做出選擇後,故事從那裡繼續,執行該選單在其標籤中後面的內容。如果有同名標籤,則以標籤為目標。選單不接受實參:帶實參呼叫選單是一個錯誤,lint 會報告它。

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 和 return

    jump teahouse
    jump expression "ending_" + outcome
    call chapter_title("Chapter One", "The Teahouse")
    call expression next_scene
    return
    return affection * 10

jump 在某個標籤或某個 具名選單 處繼續;在被呼叫的標籤內部,跳轉仍留在該呼叫之內,因此之後的 return 仍會返回。call 執行一個標籤,在它返回後從呼叫之後繼續,並把實參繫結到標籤的參數。return value 把值放入 _return。jump expression 和 call expression 通過計算得到標籤名稱;計算得出的呼叫不接受實參。

if、elif、else 和 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

空塊是錯誤;在不需要做任何事時,寫 pass。迴圈必須執行到某句臺詞、選單或暫停,否則故事會在執行一百萬條語句後因錯誤而停止。

$ 賦值

    $ affection += 1
    $ player_name = trim(player_name) or "Lu Wenjing"
    $ inventory += "fan"
    $ inventory -= "fan"
    $ persistent.endings_seen += "good"
    $ score //= 2

$ name = expression 進行賦值;+=、-=、*=、/=、//= 和 %= 進行復合賦值。$ expression 為了副作用而對錶達式求值。在任何遊戲中,$ 行都是 Novella 自己的表示式語言,而不是 Python。

python

    python:
        trust += 1
        gifts = sorted(set(gifts + ["fan"]))

在選擇啟用 Python 的遊戲中,一個在故事執行到它時、針對故事變數執行的 Python 塊。它所設定的、故事能夠容納的值會回到故事的變數中。參見 Python。

play、queue、stop 和 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] 播放一個帶引號的檔案、一個別名,或一個按順序播放的方括號列表。fadeout 讓正在播放的內容淡出;如果同一檔案已在播放,if_changed 不做任何事。queue 在目前播放的內容之後播放。stop channel [fadeout n] 停止播放。voice file 為下一句臺詞設定語音,voice sustain 讓目前語音在下一句臺詞期間繼續播放。設定了 config.auto_voice(例如 voice/{id}.ogg)時,如果以臺詞 id 命名的語音檔案存在,臺詞就會播放它;模式中的 {character} 是說話者的語音標籤或 id。

movie

    movie "video/opening.mp4"
    movie "video/credits.webm" noskip keepmusic

播放全屏影片。除非給出 keepmusic,否則它會停止音樂;除非給出 noskip,否則點選即可跳過。

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"] 請玩家輸入文字。預設值中可以插值(default "[translate('Lu Wenjing')]");空回答會保留預設值。

window 和 nvl clear

    window hide
    scene bg night_sky with fade
    window show with dissolve
    window auto
    nvl clear

window show|hide|auto [with transition] 設定對話方塊的可見性;auto 在有對話時顯示對話方塊,其他時候隱藏。nvl clear 開始一個新的 NVL 頁面。

run

    run fortune
    run riddle answer, 3
    run lantern("red", 3)
    $ slip = _return

run name [arguments] 執行遊戲用 C# 或 Divooka 實現的命令,實參為逗號分隔的表示式;其結果放入 _return。故事用到的每個命令都必須已註冊,否則故事會因錯誤而停止(lint 也會報告)。參見 開發 和 在 Divooka 中使用 Novella。

介面

    show screen hud(gold)
    hide screen hud
    call screen riddle("What has a face but no mouth?")
    if _return == "timeout":
        q "Too slow."

show screen name(arguments) 把一個介面放在遊戲之上,它會一直保留(讀檔時也會恢復),直到 hide screen name;故事在它下面繼續進行。call screen name(arguments) 顯示一個介面並等待它作答,答案放入 _return;如果玩家未作答就關閉了它(按 Escape、執行 close 動作,或 C# 介面自己的 Close),_return 為 none。該介面可以是故事用 screen 定義的介面,也可以是遊戲在 C# 中註冊的介面,後者優先於同名的已定義介面。帶 with transition 的 show screen 和 hide screen 會在舞臺上播放轉場。被呼叫的介面不屬於舞臺,因此 call screen ... with transition 會在該轉場的時長內讓介面淡入,無論它指定的是哪種轉場。

notify、achieve 和 pass

    notify "Added to the gallery: Lanterns on the River"
    achieve end_good
    pass

notify 顯示一條通知,持續幾秒鐘;achieve 授予一個成就(僅一次)並加以宣告;pass 不做任何事。

文字標籤

對話臺詞接受 Ren'Py 的文字標籤。樣式標籤由對應的 {/tag} 關閉;關閉一個標籤也會關閉在其內部開啟的所有標籤。

標籤 效果
{b}、{i}、{u}、{s} 粗體、斜體、底線、刪除線。
{plain} 關閉粗體、斜體、底線和刪除線。
{color=#f88} 文字顏色。
{outlinecolor=#000} 文字周圍的描邊,作為光暈繪製在字母后面,寬度至少兩個像素。
{style=name} 樣式 所設定的文字屬性:字型、字型大小、顏色、字重、描邊和陰影。樣式未設定的屬性沿用臺詞本身的,因此只設定顏色的樣式會保留臺詞的字型大小和字型。不支援 Ren'Py 的 {=name} 寫法。
{size=40}、{size=+8}、{size=-4}、{size=*1.5} 絕對字型大小,或相對於周圍文字的字型大小。
{font=Family} 字型族。
{alpha=0.5} 不透明度。
{a=target} 連結,在臺詞顯示完畢後可點選:jump:label、label:label 或單獨的標籤名會跳轉到該處;call:label 執行該標籤,然後再次顯示同一句臺詞;http 或 https 地址會在瀏覽器中開啟。
{rb}漢字{/rb}{rt}かんじ{/rt}、{ruby=pīnyīn}字{/ruby} 顯示在基礎文字上方的注音文字(振假名、拼音)。
{w}、{w=0.5} 在繼續逐字顯示之前,等待一次點選或一段時間。
{p}、{p=1.0} 換行,然後等待。
{nw} 不在臺詞末尾等待:逐字顯示完畢後立即推進。
{fast} 立即顯示此處之前的所有內容。
{cps=20}、{cps=*2} ... {/cps} 以每秒字元數表示的顯示速度,或玩家設定速度的倍數。
{image=gui/heart.png} 與文字同行的內聯影像。
{space=20} 水平空白。
{#comment} 被忽略:寫給譯者的備註。

[expression] 插入一個值:"Welcome, [player_name]."、"[len(inventory)] items"。{{ 和 [[ 分別表示字面的花括號和方括號。Novella 不認識的標籤會作為文字顯示,但 {vspace}、{k} 和 {clear} 除外,它們會被接受並忽略。

表示式

表示式出現在 $ 行、條件、default 和 define、實參、[interpolation] 以及 if 子句中。它們是一種小巧、安全的語言,在作者習慣使用的地方採用 Python 的語法,同時也接受 C 風格的寫法。

值包括數字、字串(單引號或雙引號)、true 和 false(也可寫作 True、False)、none(也可寫作 None、null)以及列表([1, "fan", true])。變數就是名稱;persistent.name 讀取持久儲存。列表和字串從零開始索引,負索引從末尾倒數:inventory[0]、name[-1]。不存在的索引為 none。

運算子,按優先順序從低到高排列:

運算子 說明
x if c else y、c ? x : y 條件表示式。
or、\|\| 返回起決定作用的運算元,因此 name or "stranger" 可用作預設值。
and、&& 返回起決定作用的運算元。
not、!
==、!=、<、<=、>、>=、in、not in 比較運算可以鏈式書寫:1 < x <= 5。in 測試列表成員關係或子字串。
+、- + 連線字串、向列表追加元素(inventory + "fan")或連線列表;- 從列表中移除所有匹配項。
*、/、//、% / 是精確除法,// 是向下取整除法;"ab" * 3 重複字串。
一元 -、+
** 冪運算。

函式

函式 結果
len(x) 列表或字串的長度。
min(...)、max(...)、sum(...) 對各實參求值,或對唯一的列表實參中的各項求值。
abs(x)、round(x[, digits])、floor(x)、ceil(x)、clamp(x, low, high) 算術。
int(x)、number(x)、str(x)、bool(x) 型別轉換;int 截斷取整。
upper(s)、lower(s)、trim(s)、capitalize(s) 文字。
join(list[, separator])、split(text[, separator]) 連線預設使用 ", ",拆分預設使用空格。
contains(x, item)、has(x, item) 成員關係,與 in 相同。
count(list, item)、append(list, item)、remove(list, item)、unique(list)、sorted(list) 列表輔助函式;每個都返回一個新列表。
range(n)、range(a, b) 從 0(或 a)開始、直到但不包括終點的整數。
random()、random(n)、random(a, b) 0..1 之間的數、0..n-1 之間的整數,或 a 到 b(含兩端)之間的整數。
chance(p) 以概率 p 為真。
choice(list) 或 choice(a, b, ...) 一個隨機項。
defined(name)、get(name[, default]) 某個變數(以字串命名)是否有值;其值或預設值。
visits(label)、visited(label) 本次遊玩中某個標籤被進入的次數,以及是否被進入過。
seen_label(label) 某個標籤是否在任何一次遊玩中被到達過。
seen_image(name)、heard(file) 某個影像是否在任何一次遊玩中被顯示過,或某段音訊是否被播放過。
achieved(id) 某個成就是否已被授予。
showing(tag) 帶該標籤的影像是否在舞臺上。
language() 目前語言;如果是原始語言,則為空字串。
translate(text) 經目前語言的字串翻譯處理後的文字。

隨機抽取來自遊戲自己的隨機數生成器,它是存檔和回滾狀態的一部分,因此回滾後再次選擇會抽到相同的結果。遊戲可以從 C# 用 RegisterFunction、從 Divooka 圖用 Register Expression Function,或從指令碼用 init python: 新增函式。

Python

init python:
    def title_for(level):
        titles = ["stranger", "friend", "confidant"]
        return titles[min(int(level), len(titles) - 1)]

label start:
    "You are a [title_for(trust)]."
    python:
        trust += 1
        gifts = sorted(set(gifts + ["fan"]))

對於 Novella 表示式無法覆蓋的內容——含有迴圈的輔助函式、對列表的計算——遊戲可以選擇啟用 Python。Novella 自己的表示式仍是故事的語言,因為它們可以被驗證、轉換為節點並列印回指令碼,而 Python 做不到這些;Python 用於其餘的部分。

  • init python: 位於指令碼的頂層,在遊戲開始時執行一次。它定義的函式可以像內建函式一樣,從任何表示式中呼叫,包括條件、$ 行和 [interpolation]。
  • python: 位於標籤的語句之間,在故事執行到它時執行。執行之前,它會獲得故事的變數;執行之後,它所設定的每個故事能夠容納的變數——數字、文字、布林值、None,或由這些組成的列表——都會回到故事中,與其餘變數一起存檔和回滾。
  • 故事無法容納的內容同樣會被保留。 字典、集合、由 init python: 塊定義的類的物件,以及 init 塊設定的值,在任何程式碼塊更改它們時,都會一起被 pickle 到一個隱藏的故事變數 _python 中,因此它們會與其餘內容一起存檔、讀檔和回滾:讀檔或回滾之後,下一個程式碼塊或函式呼叫看到的 Python 狀態,與那個時間點完全一致。Ren'Py 也是這樣保留它的 store 的。
  • 函式和類應放在 init python: 中。 函式、類和模組是程式碼而不是狀態,因此不會被 pickle;init 塊在每次遊戲啟動時都會重新定義它們,而正是在那裡定義的類,才使存檔中該類的物件得以恢復。pickle 無法寫出的值(如生成器或已開啟的檔案)不會被保留。
  • 程式碼完全按原樣保留,包括註解和空行,Python 錯誤會指出它來自哪個指令碼的哪一行。
  • 標準庫可用。 Python 通過 Parcel NExT 的 Divooka.Scripting.Python 在 IronPython 3.4.2 上執行,標準庫位於遊戲旁的 PythonLib/ 中,因此 import random、os、json、pickle、datetime、collections 等都與在任何 Python 中一樣可用。在故事本身中需要隨機性時,仍應在表示式中使用故事自己的 random、chance 和 choice:它們的抽取來自遊戲狀態,因此回滾會重放相同的結果,而 Python 的 random 會重新抽取。

Python 來自 Novella.Python。從不編寫 Python 的遊戲無需攜帶它;當故事含有 Python 時,引擎會在遊戲旁按名稱找到它。Divooka 包和 NovellaPlayer 都攜帶它,C# 遊戲則需引用它(參見 安裝)。lint 會讀取 init python: 塊定義的函式和 python: 塊賦值的變數,因此不會把它們報告為未知;如果故事含有 Python 而遊戲沒有 Python,lint 也會報告。

動畫

變換的主體、show 或 scene 下的塊,或者 image name: 塊,都是 Ren'Py ATL 風格的時間線。每一行是以下之一:

  • 屬性設定,立即生效。多個設定可以寫在同一行:xalign 0.5 yalign 1.0 alpha 0.0。
  • 插值:一個緩動函式(warper)、一個時長和若干屬性,如 ease 1.0 xalign 1.0 alpha 1.0,從目前值動畫過渡到這些值。屬性也可以寫在緩動函式行下方的塊中。
  • 暫停:pause 0.5,或單獨一個數字。
  • 圖片行:用引號括起的影像名稱、檔案或顏色,可選擇帶 with 轉場,如 "eileen blink" with dissolve。從此處起,影像顯示該圖片,在轉場的時長內交叉淡化,但不會阻塞時間線。圖片行之間穿插暫停就構成逐幀動畫。
  • repeat 或 repeat n:重新開始外層塊,永遠重複,或直到它總共執行了 n 次。
  • block::巢狀的時間線,使 repeat 可以只迴圈動畫的一部分。
  • parallel::連續的多個 parallel 塊同時執行,各自為自己的屬性製作動畫。
  • choice [weight]::連續的多個 choice 塊構成一次選擇,每次時間線到達這裡時,按權重(未給出時為 1)隨機選取其中一個執行。
  • time seconds:其後的內容在塊開始後的指定秒數時開始,並中斷仍在執行的任何內容。
  • on show:、on replace:、on hide:,或同時指定多個,on show, replace::在影像發生相應事件時播放的時間線。變換處理 hide 的影像會一直留在舞臺上,直到處理程式結束,因此它可以淡出或滑走。
  • contains::在本影像內部繪製的一張圖片,帶有自己的時間線,放置在本影像的範圍內;塊的第一行是該圖片。單行的 contains "sparkle" 是簡寫形式。
  • function name:把影像交給遊戲在 C# 中用 RegisterTransformFunction 註冊的函式,持續時間由該函式決定(參見 開發)。
  • 另一個變換的名稱,如果它接受參數則帶上實參,就地引入: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

transform entrance:
    on show, replace:
        alpha 0.0
        linear 0.5 alpha 1.0
    on hide:
        linear 0.5 alpha 0.0
    contains:
        "sparkle"
        linear 2.0 alpha 0.0

image lantern:
    "lantern bright"
    choice 3:
        pause 0.8
    choice:
        pause 0.2
    "lantern dim" with dissolve
    pause 0.15
    repeat

時間線是時間的純函式:同一影像在同一時刻顯示相同的內容,隨機選擇也不例外,因為每個顯示的影像都帶有自己的種子,存檔會儲存它。因此,讀檔或回滾會精確顯示當時所顯示的內容。

屬性: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。成對屬性接受兩個數字或一個括號中的數對:pos 100 200、align (0.5, 1.0)。tint 接受一個顏色:tint "#ffd0d0"。負的 xzoom 或 yzoom 表示鏡像。

位置:帶小數點的數字是比例(對位置和對齊而言是螢幕的比例,對錨點而言是影像的比例),整數是像素:xpos 0.25 是橫向四分之一處,xpos 320 是 320 像素。內建位置:left、right、center、midleft、midright、truecenter、top、topleft、topright、offscreenleft、offscreenright、fullscreen、default 和 reset。

緩動函式:linear、ease、easein、easeout、pause(保持不變,到結束時跳變),以及 ease_quad、ease_cubic、ease_quart、ease_quint、ease_expo、ease_circ、ease_back、ease_elastic 和 ease_bounce 系列,每個系列也有 easein_... 和 easeout_... 形式。

值:時長或值可以是數字、參數名,或括號中的表示式:ease (speed * 2) yoffset (-height)。時間線表示式可以看到變換的參數和內建函式,但看不到遊戲的變數。

不支援:ATL 語句 event,解析器會將其報告為錯誤(影像自身的事件用 on show、on replace 和 on hide 處理);用於任何其他事件的 on;以及 Ren'Py 的極座標、裁剪、平鋪和著色器屬性。

轉場

轉場寫在 with 之後:show qinglan with dissolve、scene bg night with fade、with pixellate。內建轉場:

名稱 效果 預設值 實參
dissolve、crossfade 交叉淡化。 0.5 秒 (seconds[, warper])
fade 淡出到某種顏色,保持,再淡入。 淡出 0.5 秒,淡入 0.5 秒,黑色 (out[, hold[, in[, "#colour"]]]);只有一個實參時,淡入時長等於淡出時長
flash 閃白後恢復。 淡出 0.1 秒,淡入 0.4 秒 同 fade
pixellate 先像素化淡出,再像素化淡入。 1.0 秒,5 步 (seconds[, steps])
move、ease 留在場上的影像滑行到新位置;其他影像溶解。 0.5 秒,緩動 (seconds[, warper])
moveinleft、moveinright、moveintop、moveinbottom 進入的影像從該側滑入。 0.5 秒 (seconds[, warper])
moveoutleft、moveoutright、moveouttop、moveoutbottom 離開的影像向該側滑出。 0.5 秒 (seconds[, warper])
easeinleft ... easeoutbottom 與 movein/moveout 相同,但帶緩動。 0.5 秒 (seconds[, warper])
slideleft、slideright、slideup、slidedown 新畫面滑入並覆蓋舊畫面。 1.0 秒 (seconds[, warper])
slideawayleft ... slideawaydown 舊畫面滑走。 1.0 秒 (seconds[, warper])
pushleft ... pushdown 新畫面把舊畫面推出去。 1.0 秒 (seconds[, warper])
wipeleft ... wipedown 一條硬邊掃過畫面。 1.0 秒 (seconds[, warper])
irisin、irisout 圓形展開或收攏。 1.0 秒 (seconds[, warper])
zoomin、zoomout 新畫面縮放。 0.5 秒 (seconds[, warper])
vpunch、hpunch 震動螢幕。 0.4 秒,10 像素 (seconds[, pixels])
blinds、squares 百葉窗,或方格網。 1.0 秒,8 片百葉或 12 個方格 (seconds[, count])
imagedissolve 像素按灰度遮罩的亮度順序變化。 1.0 秒,漸層範圍 32 ("mask.png"[, seconds[, ramp[, reverse]]])
none 立即完成。

具名轉場(transition slow = dissolve(2.0))可以帶或不帶更多實參使用。

翻譯

用原始語言編寫遊戲(config.original_language 在偏好設定中為它命名),然後為每種語言新增一個指令碼。偏好設定中的語言選擇器會列出故事所翻譯的每種語言。

翻譯後的臺詞、名字標籤和快捷選單

# 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:
    "茶馆里一下子安静了。"
    "连掌柜都停下了算盘。"
  • 臺詞按 id 翻譯。為每句臺詞指定顯式 id(id tea_wang_03),可以在原文被編輯後仍讓譯文保持關聯;沒有顯式 id 時,id 是標籤名加上說話者和文字的雜湊值,文字一變它就會改變。一個塊中的多個說話語句以段落分隔符連線;譯文可以指定不同的說話者;pass 表示在該語言中刪去這句臺詞。
  • 字串按原文精確匹配翻譯:選單選項、角色名、提示、通知、畫廊、音樂空間和成就標題,以及通過 translate("...") 傳入的任何內容。字串表還可以覆蓋引擎自己的介面用語,例如 old "Start" / new "开篇"。
  • 引擎介面(選單、偏好設定、確認、幫助介面)已翻譯成簡體中文、繁體中文和日文;參見 主題設定。
  • 字型:在主題的字型列表中,把 CJK 字型放在拉丁字型之後,使兩種文字都能正常渲染,具體見 主題設定。

lint 會報告每種語言翻譯了多少句臺詞。config.default_language 設定全新安裝時的初始語言。