这是用户在 2025-4-12 7:32 为 https://www.motoslave.net/sugarcube/2/docs/#introduction 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

 Introduction   简介

This documentation is a reference for SugarCube v2, a free (gratis and libre) story format for Twine/Twee.
本文档是 SugarCube v2 的参考,SugarCube v2 是一个免费的(免费且自由)Twine/Twee 故事格式。

Tip: This document is a single page, so you may use your browser's find-in-page functionality—CTRL+F, CMD+F, F3—to search for specific terms.
提示:本文档是一页,您可以使用浏览器中的查找功能—— CTRL + FCMD + FF3 ——来搜索特定术语。

Note: If you believe that you've found a bug in SugarCube, or simply wish to make a suggestion, you may do so by creating a new issue at its source code repository.
注意:如果您认为在 SugarCube 中发现了错误,或者只是想提出建议,您可以通过在其源代码仓库中创建新问题来这样做。


Contributions to this documentation have been graciously made by:
本文档的贡献者包括:

 Markup   标记

Note: Except where noted, all markup has been available since v2.0.0.
注意:除非另有说明,否则所有标记都自 v2.0.0 以来一直可用。

 Naked Variable   裸变量

In addition to using one of the print macros (<<print>>, <<=>>, <<->>) to print the values of TwineScript variables, SugarCube's naked variable markup allows printing them simply by including them within your normal passage text—i.e., variables in passage text are interpolated into a string representation of their values.
除了使用打印宏( <<print>><<=>><<->> )来打印 TwineScript 变量的值之外,SugarCube 的裸变量标记还允许通过在正常段落文本中包含它们来简单地打印它们——即,段落文本中的变量将被插入到它们的字符串表示形式中。

The following forms are supported by the naked variable markup:
裸变量标记支持以下形式:

Type  类型 Syntax  语法 Example  示例
Simple variable  简单变量
$variable
$name
Property access,  属性访问,
dot notation  点表示法
$variable.property
$thing.name
Index/property access,  索引/属性访问,
square bracket notation  方括号表示法
$variable[numericIndex]
$variable["property"]
$variable['property']
$variable[$indexOrPropertyVariable]
$thing[0]
$thing["name"]
$thing['name']
$thing[$member]

If you need to print anything more complex—e.g., using a calculation, $variable[_i + 1], or a method call, $variable.someMethod()—then you will still need to use one of the print macros.
如果您需要打印更复杂的内容——例如,使用计算、 $variable[_i + 1] 或方法调用, $variable.someMethod() ——那么您仍然需要使用其中一个打印宏。

For example:  例如:

/* Explicitly printing the value of $name via the <<print>> macro */
Well hello there, <<print $name>>.

/* Implicitly printing the value of $name via the naked variable markup */ Well hello there, $name.

/* Assuming $name is set to "Mr. Freeman", both should yield the following */ Well hello there, Mr. Freeman.

Because variables within your passage text are transformed into their values automatically, if you actually want to output a variable as-is—i.e., without interpolation; e.g., for a tutorial, debug output, or whatever—then you'll need to escape it in some fashion. For example:
由于段落文本中的变量会自动转换为它们的值,如果您实际上想以原始形式输出一个变量——即不进行插值;例如,用于教程、调试输出或任何其他用途——那么您需要以某种方式转义它。例如:

/* Using the nowiki markup: """…""" (triple double-quotes) */
The variable """$name""" is set to: $name

/* Using the nowiki markup: <nowiki>…</nowiki> */ The variable <nowiki>$name</nowiki> is set to: $name

/* Using the double dollar-sign markup (which escapes the $-sigil): $$ */ The variable $$name is set to: $name

/* Assuming $name is set to "Mr. Freeman", all of the above should yield the following */ The variable $name is set to: Mr. Freeman

Additionally, you could use the inline code markup to escape the variable, though it will also wrap the escaped variable within a <code> element, so it's probably best used for examples and tutorials. For example:
此外,您还可以使用内联代码标记来转义变量,尽管这也会将转义的变量包裹在 <code> 元素中,因此它可能最好用于示例和教程。例如:

/* Using the inline code markup: {{{…}}} (triple curly braces) */
The variable {{{$name}}} is set to: $name

/* Assuming $name is set to "Mr. Freeman", it should yield the following */ The variable <code>$name</code> is set to: Mr. Freeman

SugarCube's link markup consists of a required Link component and optional Text and Setter components.
SugarCube 的链接标记由一个必需的 Link 组件和可选的 TextSetter 组件组成。

The Link component may be either plain text or any valid TwineScript expression, which will be evaluated early—i.e., when the link is initially processed. Its value should be the title of a passage or any valid URL to a resource (local or remote).
Link 组件可以是纯文本或任何有效的 TwineScript 表达式,它将在链接最初处理时进行评估——即,当链接最初被处理时。其值应该是篇章的标题或任何有效的资源(本地或远程)的 URL。

The optional Text component may be either plain text or any valid TwineScript expression, which will be evaluated early—i.e., when the link is initially processed.
可选的 Text 组件可以是纯文本或任何有效的 TwineScript 表达式,它将在链接最初处理时进行评估——即,当链接最初被处理时。

The optional Setter component, which only works with passage links, must be a valid TwineScript expression, of the <<set>> macro variety, which will be evaluated late—i.e., when the link is clicked on. If you need to specify multiple expressions, then they should be separated by semi-colons (;)—e.g., $a to 5; $b to true.
可选的 Setter 组件,仅适用于篇章链接,必须是一个有效的 TwineScript 表达式,属于 <<set>> 宏类型,它将在链接点击时进行评估——即,当链接被点击时。如果您需要指定多个表达式,则应使用分号( ; )分隔——例如, $a to 5; $b to true

In addition to the standard pipe separator (|) used to separate the Link and Text components (as seen below), SugarCube also supports the arrow separators (-> & <-). Particular to the arrow separators, the arrows' direction determines the order of the components, with the arrow always pointing at the Link component—i.e., the right arrow works like the pipe separator, Text->Link, while the left arrow is reversed, Link<-Text.
除了用于分隔 LinkText 组件的标准管道分隔符( | )外,SugarCube 还支持箭头分隔符( -> & <- )。对于箭头分隔符,箭头的方向决定了组件的顺序,箭头始终指向 Link 组件——即右箭头的作用类似于管道分隔符, Text->Link ,而左箭头则是相反的, Link<-Text

Warning (Twine 2): Due to how the Twine 2 automatic passage creation feature currently works, using any TwineScript expression for the Link component will cause a passage named after the expression to be created that will need to be deleted. To avoid this problem, it's suggested that you use the separate argument form of the <<link>> macro in Twine 2 when you need to use an expression.
警告(TWINE 2):由于 Twine 2 自动创建段落功能当前的工作方式,使用任何 TwineScript 表达式作为 Link 组件将导致创建一个以表达式命名的段落,该段落需要被删除。为了避免这个问题,建议在需要使用表达式时,使用 Twine 2 中 <<link>> 宏的单独参数形式。

For the following examples assume: $go is "Grocery" and $show is "Go buy milk"
在以下示例中假设: $go"Grocery"$show"Go buy milk"
Syntax  语法 Example  示例
[[Link]]
[[Grocery]]
[[$go]]
[[Text|Link]]
[[Go buy milk|Grocery]]
[[$show|$go]]
[[Link][Setter]]
[[Grocery][$bought to "milk"]]
[[$go][$bought to "milk"]]
[[Text|Link][Setter]]
[[Go buy milk|Grocery][$bought to "milk"]]
[[$show|$go][$bought to "milk"]]

 Image   图片

SugarCube's image markup consists of a required Image component and optional Title, Link, and Setter components.
SugarCube 的图像标记由一个必需的 Image 组件和可选的 TitleLinkSetter 组件组成。

The Image component may be either plain text or any valid TwineScript expression, which will be evaluated early—i.e., when the link is initially processed. Its value may be any valid URL to an image resource (local or remote) or the title of an media (image) passage.
Image 组件可以是纯文本或任何有效的 TwineScript 表达式,该表达式将在链接最初处理时进行评估。其值可以是任何有效的图像资源(本地或远程)的 URL 或媒体(图像)篇章的标题。

The optional Title component may be either plain text or any valid TwineScript expression, which will be evaluated early—i.e., when the link is initially processed. Its value will be used as the alt text of the image.
可选的 Title 组件可以是纯文本或任何有效的 TwineScript 表达式,该表达式将在链接最初处理时进行评估。其值将用作图像的 alt 文本。

The optional Link component may be either plain text or any valid TwineScript expression, which will be evaluated early—i.e., when the link is initially processed. Its value may be the title of a passage or any valid URL to a resource (local or remote).
可选的 Link 组件可以是纯文本或任何有效的 TwineScript 表达式,它将在链接最初处理时被评估——即,当链接最初被处理时。它的值可以是篇章的标题或任何有效的资源(本地或远程)URL。

The optional Setter component, which only works with passage links, must be a valid TwineScript expression, of the <<set>> macro variety, which will be evaluated late—i.e., when the link is clicked on. If you need to specify multiple expressions, then they should be separated by semi-colons (;)—e.g., $a to 5; $b to true.
可选的 Setter 组件仅适用于篇章链接,必须是一个有效的 TwineScript 表达式,属于 <<set>> 宏类型,它将在点击链接时被评估——即,当链接被点击时。如果您需要指定多个表达式,则它们应该由分号( ; )分隔——例如, $a to 5; $b to true

In addition to the standard pipe separator (|) used to separate the Image and Title components (as seen below), SugarCube also supports the arrow separators (-> & <-). Particular to the arrow separators, the arrows' direction determines the order of the components, with the arrow always pointing at the Image component—i.e., the right arrow works like the pipe separator, Title->Image, while the left arrow is reversed, Image<-Title.
除了用于分隔 ImageTitle 组件的标准管道分隔符( | )之外,SugarCube 还支持箭头分隔符( -> & <- )。对于箭头分隔符,箭头的方向决定了组件的顺序,箭头始终指向 Image 组件——即,右箭头的作用类似于管道分隔符 Title->Image ,而左箭头是相反的, Image<-Title

Warning (Twine 2): Due to how the Twine 2 automatic passage creation feature currently works, using any TwineScript expression for the Link component will cause a passage named after the expression to be created that will need to be deleted. To avoid this problem, it's suggested that you use the separate argument form of the <<link>> macro in Twine 2 when you need to use an expression.
警告(TWINE 2):由于 Twine 2 自动段落创建功能当前的工作方式,使用任何 TwineScript 表达式为 Link 组件将导致创建一个以表达式命名的段落,该段落需要被删除。为了避免这个问题,建议在 Twine 2 中需要使用表达式时,使用 <<link>> 宏的单独参数形式。

For the following examples assume: $src is home.png, $go is "Home", and $show is "Go home"
假设以下示例: $srchome.png$go"Home"$show"Go home"
Syntax  语法 Example  示例
[img[Image]]
[img[home.png]]
[img[$src]]
[img[Image][Link]]
[img[home.png][Home]]
[img[$src][$go]]
[img[Image][Link][Setter]]
[img[home.png][Home][$done to true]]
[img[$src][$go][$done to true]]
[img[Title|Image]]
[img[Go home|home.png]]
[img[$show|$src]]
[img[Title|Image][Link]]
[img[Go home|home.png][Home]]
[img[$show|$src][$go]]
[img[Title|Image][Link][Setter]]
[img[Go home|home.png][Home][$done to true]]
[img[$show|$src][$go][$done to true]]

Within stylesheets  在样式表中

A restricted subset of the image markup, allowing only the Image component, may be used within stylesheets—primarily to allow the easy use of media (image) passages. For example:
图像标记的受限子集,仅允许使用 Image 组件,可以在样式表中使用——主要用于允许轻松使用媒体(图像)段落。例如:

/* Using the external image "forest.png" as the <body> background. */
body {
	background-image: [img[forest.png]];
}

/* Using the image passage "lagoon" as the <body> background. */ body { background-image: [img[lagoon]]; }

 HTML & SVG Attribute  HTML & SVG 属性

Warning: None of these features work within the verbatim HTML markup.
警告:这些功能在直接 HTML 标记中都无法工作。

 Special Attribute  特殊属性

SugarCube provides a few special HTML & SVG attributes, which you may add to tags to enable special behaviors. There are attributes for passage links, media passages, and setters.
SugarCube 提供了一些特殊的 HTML 和 SVG 属性,您可以将这些属性添加到标签中以启用特殊行为。这些属性包括段落链接、媒体段落和设置器。

Type  类型 Attribute  属性 Example  示例
Passage, Link  段落,链接
data-passage
<a data-passage="PassageName">Do the thing</a>
<area shape="rect" coords="25,25,75,75" data-passage="PassageName">
<button data-passage="PassageName">Do the thing</button>
Passage, Audio  段落,音频
data-passage
<audio data-passage="AudioPassageName">
Passage, Image  文本,图片
data-passage
<img data-passage="ImagePassageName">
<image data-passage="ImagePassageName" />
Passage, Source  文本,来源
data-passage
<source data-passage="AudioOrVideoPassageName">
Passage, Video  文本,视频
data-passage
<video data-passage="VideoPassageName">
Setter  设置者
data-setter
<a data-passage="PassageName" data-setter="$thing to 'done'">Do the thing</a>
<area shape="rect" coords="25,25,75,75" data-passage="PassageName"
	data-setter="$thing to 'done'">
<button data-passage="PassageName" data-setter="$thing to 'done'">Do the thing</button>

History:  历史:

 Attribute Directive  属性指令

HTML & SVG attributes may be prefixed with directives, special text, which trigger special processing of such attributes.
HTML 和 SVG 属性可以带有指令,这些指令是特殊的文本,可以触发对这些属性的特定处理。

Evaluation directive: sc-eval:, @
评估指令: sc-eval:@

The evaluation directive causes the attribute's value to be evaluated as TwineScript. Post-evaluation, the directive will be removed from the attribute's name and the result of the evaluation will be used as the actual value of the attribute.
评估指令会导致将属性的值评估为 TwineScript。评估后,指令将从属性名称中删除,并将评估结果用作属性的真正值。

Warning: The evaluation directive is not allowed on the data-setter attribute—as its function is to evaluate its contents upon activation of its own element—and any such attempt will cause an error.
警告:评估指令不允许用于 data-setter 属性——因为它的功能是在其自身元素激活时评估其内容——任何此类尝试都将导致错误。

For the following examples assume: _id is "foo"
以下示例假设: _id"foo"
Syntax  语法 Example  示例 Rendered As  渲染为
sc-eval:attribute-name
<span sc-eval:id="_id">…</span>
<span id="foo">…</span>
sc-eval:attribute-name
<span sc-eval:id="'pre-' + _id + '-suf'">…</span>
<span id="pre-foo-suf">…</span>
@attribute-name
<span @id="_id">…</span>
<span id="foo">…</span>
@attribute-name
<span @id="'pre-' + _id + '-suf'">…</span>
<span id="pre-foo-suf">…</span>

History:  历史:

 Line Continuation   行续

See Also: The various no-break features—<<nobr>> macro, nobr special tag, and Config.passages.nobr setting—all perform a similar, though slightly different, function.
参考以下各种不间断特性—— <<nobr>> 宏、 nobr 特殊标签和 Config.passages.nobr 设置,它们都执行类似但略有不同的功能。

Warning: Line continuations, or any markup that relies on line positioning, are incompatible with the no-break features because of how the latter function.
警告:由于不间断特性的工作方式,行续或任何依赖于行定位的标记都不兼容,因为它们的功能。

A backslash (\) that begins or ends a line is the line continuation markup. Upon processing the backslash, associated line break, and all whitespace between them are removed—thus, joining the nearby lines together. This is mostly useful for controlling whitespace when you want to wrap lines for readability, but not generate extra whitespace upon display, and the <<silently>> macro isn't an option because you need to generate output.
一个以反斜杠( \ )开始或结束的行是行续标记。在处理反斜杠、相关的换行符以及它们之间的所有空白后,将附近的行连接在一起。这主要用于控制空白,当您想要为了可读性而换行时,但又不希望在显示时产生额外的空白,并且当 <<silently>> 宏不可用时,因为您需要生成输出。

For example, all of the following: (n.b., · represents whitespace that will be removed, ¬ represents line breaks)
例如,以下所有内容:(注意, · 代表将被删除的空白, ¬ 代表换行符)

The rain in Spain falls \¬
mainly on the plain.

The rain in Spain falls \····¬ mainly on the plain.

The rain in Spain falls¬ \ mainly on the plain.

The rain in Spain falls¬ ····\ mainly on the plain.

Yield the single line in the final output:
输出单行文本:产生最终输出单行

The rain in Spain falls mainly on the plain.

 Heading   标题

An exclamation point (!) that begins a line defines the heading markup. It consists of one to six exclamation points, each additional one beyond the first signifying a lesser heading.
以感叹号( ! )开始的行定义标题标记。它由一个到六个感叹号组成,每个额外的感叹号都表示比第一个更小的标题。

Type  类型 Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
Level 1  一级
!Level 1 Heading
<h1>Level 1 Heading</h1>

Level 1 Heading  一级标题

Level 2  二级
!!Level 2 Heading
<h2>Level 2 Heading</h2>

Level 2 Heading  二级标题

Level 3  三级
!!!Level 3 Heading
<h3>Level 3 Heading</h3>

Level 3 Heading  三级标题

Level 4  四级
!!!!Level 4 Heading
<h4>Level 4 Heading</h4>

Level 4 Heading  四级标题

Level 5  五级
!!!!!Level 5 Heading
<h5>Level 5 Heading</h5>
Level 5 Heading  五级标题
Level 6  六级
!!!!!!Level 6 Heading
<h6>Level 6 Heading</h6>
Level 6 Heading  六级标题

 Style   风格

Warning: Because the style markups use the same tokens to begin and end each markup, the same style cannot be nested within itself.
警告:由于样式标记使用相同的标记来开始和结束每个标记,相同的样式不能嵌套在其自身内部。

Type  类型 Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
Emphasis  强调
//Emphasis//
<em>Emphasis</em>
Emphasis  强调
Strong  强烈
''Strong''
<strong>Strong</strong>
Strong  强烈
Underline  下划线
__Underline__
<u>Underline</u>
Underline  下划线
Strikethrough  删除线
==Strikethrough==
<s>Strikethrough</s>
Strikethrough  删除线
Superscript  上标
Super^^script^^
Super<sup>script</sup>
Superscript  上标 script
Subscript  下标
Sub~~script~~
Sub<sub>script</sub>
Subscript  script

 List   列表

An asterisk (*) or number sign (#) that begins a line defines a member of the unordered or ordered list markup, respectively.
以星号( * )或井号( # )开头的行分别定义无序列表或有序列表的标记。

Type  类型 Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
Unordered  无序列表
* A list item
* Another list item
<ul>
<li>A list item</li>
<li>Another list item</li>
</ul>
  • A list item  一个列表项
  • Another list item  另一个列表项
Ordered  有序的
# A list item
# Another list item
<ol>
<li>A list item</li>
<li>Another list item</li>
</ol>
  1. A list item  一个列表项
  2. Another list item  另一个列表项

 Blockquote   引用块

A right angle bracket (>) that begins a line defines the blockquote markup. It consists of one or more right angle brackets, each additional one beyond the first signifying a level of nested blockquote.
一个右尖括号( > )开始一行,定义了引用标记。它由一个或多个右尖括号组成,第一个之后的每个额外的右尖括号表示嵌套引用标记的级别。

Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
>Line 1
>Line 2
>>Nested 1
>>Nested 2
<blockquote>Line 1<br>
Line 2<br>
<blockquote>Nested 1<br>
Nested 2<br>
</blockquote></blockquote>
Line 1  行 1
Line 2  行 2
Nested 1  嵌套 1
Nested 2  嵌套 2

 Code   代码

Type  类型 Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
Inline  行内
{{{Code}}}
<code>Code</code>
Code
Block  区块
{{{
Code
More code
}}}
<pre><code>Code
More code
</code></pre>
Code
More code

 Horizontal Rule   水平线

A set of four hyphen/minus characters (-) that begins a line defines the horizontal rule markup.
一行开头的四个连字符( - )定义了水平线标记。

Type  类型 Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
Horizontal rule  水平线
----
<hr>

 Verbatim Text  纯文本

The verbatim text markup disables processing of all markup contained within—both SugarCube and HTML—passing its contents directly into the output as plain text.
原文文本标记禁用了所有标记的处理,包括 SugarCube 和 HTML,直接将其内容作为纯文本传递到输出中。

Type  类型 Syntax & Example  语法 & 示例 Rendered As  渲染为 Displays As (roughly)  显示为(大致)
Triple double quotes  三重双引号
"""No //format//"""
No //format//
No //format//  没有 //格式//
<nowiki> tag   标签
<nowiki>No //format//</nowiki>
No //format//
No //format//  没有 //格式//

 Verbatim HTML   直接 HTML

A set of opening and closing <html> tags—i.e., <html></html>—defines the verbatim HTML markup. The verbatim HTML markup disables processing of all markup contained within—both SugarCube and HTML—passing its contents directly into the output as HTML markup for the browser. Thus, you should only use plain HTML markup within the verbatim markup—meaning using none of SugarCube's special HTML attributes or directives.
一组开始和结束的标签——即 <html></html> ——定义了原始 HTML 标记。原始 HTML 标记禁用了对其中包含的所有标记的处理——无论是 SugarCube 还是 HTML——直接将其内容作为 HTML 标记传递到输出中。因此,您只能在原始标记内使用纯 HTML 标记——这意味着不要使用 SugarCube 的特殊 HTML 属性或指令。

Note: You should virtually never need to use the verbatim HTML markup.
注意:您几乎永远不需要使用原始 HTML 标记。

 Custom Style   自定义样式

Warning: Because the custom style markup uses the same tokens to begin and end the markup, it cannot be nested within itself.
警告:由于自定义样式标记使用相同的标记开始和结束标记,因此它不能嵌套在其自身内部。

Type  类型 Syntax  语法 Example  示例 Rendered As  渲染为
Inline  行内
@@style-list1;Text@@
@@#alfa;.bravo;Text@@
<span id="alfa" class="bravo">Text</span>
@@color:red;Text@@
<span style="color:red">Text</span>
Block  区块
@@style-list1;
Text
@@
@@#alfa;.bravo;
Text
@@
<div id="alfa" class="bravo">Text</div>
@@color:red;
Text
@@
<div style="color:red">Text</div>
  1. The style-list should be a semi-colon (;) separated list consisting of one or more of the following:
    样式列表应为一个分号( ; )分隔的列表,包含以下之一或多个:
    As of v2.31.0, the ID and class names components may be conjoined without need of extra semi-colons—e.g., #alfa;.bravo;.charlie; may also be written as #alfa.bravo.charlie;.
    截至 v2.31.0 ,ID 和类名组件可以合并使用,无需额外分号——例如, #alfa;.bravo;.charlie; 也可以写成 #alfa.bravo.charlie;

 Template   模板

A text replacement markup. The template markup begins with a question mark (?) followed by the template name—e.g., ?yolo—and are set up as functions-that-return-strings, strings, or arrays of either—from which a random member is selected whenever the template is processed. They are defined via the Template API.
文本替换标记。模板标记以问号( ? )开头,后跟模板名称——例如, ?yolo ——并设置为返回字符串、字符串或数组的函数,从其中随机选择一个成员,每当处理模板时。它们通过 Template API 定义。

For example, consider the following markup:
例如,考虑以下标记:

?He was always willing to lend ?his ear to anyone.

Assuming that ?He resolves to She and ?his to her, then that will produce the following output:
假设 ?He 解析为 She?his 解析为 her ,那么这将产生以下输出:

She was always willing to lend her ear to anyone.

History:  历史:

 Comment   注释

Note: Comments used within passage markup are not rendered into the page output.
注意:段落标记内使用的注释不会渲染到页面输出中。

Type  类型 Syntax & Example  语法 & 示例 Supported Within…  支持在...
C-style, Block  C 样式,块
/* This is a comment. */
Passage markup, JavaScript, Stylesheets
段落标记,JavaScript,样式表
TiddlyWiki, Block  TiddlyWiki,块
/% This is a comment. %/
Passage markup  段落标记
HTML, Block  HTML,块
<!-- This is a comment. -->
Passage markup  段落标记

 TwineScript

TwineScript in SugarCube is, essentially, JavaScript with an extra spoonful of sugar on top to make it a bit nicer for the uninitiated.
SugarCube 中的 TwineScript 实质上是带有额外一勺糖的 JavaScript,使其对初学者更加友好。

 Variables   变量

Note: Temporary variables were added in v2.3.0.
注意:已添加临时变量 v2.3.0

A variable is a bit of storage where you may stash a value for later use. In SugarCube, they come in two types: story variables and temporary variables. Story variables are a part of the story history and exist for the lifetime of a playthrough session. Temporary variables do not become part of the story history and only exist for the lifetime of the moment/turn that they're created in. You'll likely use story variables most often throughout your project—though, temporary variables are perfect candidates for things like loop variables, if you're using the <<for>> macro.
变量是一种存储空间,您可以将其用于存储值以供以后使用。在 SugarCube 中,变量分为两种:故事变量和临时变量。故事变量是故事历史的一部分,存在于整个游戏会话期间。临时变量不会成为故事历史的一部分,仅存在于它们被创建的那个时刻/回合。您可能会在项目中更频繁地使用故事变量——尽管,如果您使用 <<for>> 宏,临时变量是循环变量等事物的完美候选。

For example, you might use the story variable $name to store the main player character's name or the story variable $cash to store how much money the player has on hand.
例如,您可以使用故事变量 $name 来存储主要玩家角色的名字,或者使用故事变量 $cash 来存储玩家手中的金钱。

Values may be of most primitive types and some object types, see Supported Types for more information.
值可以是大多数基本类型和一些对象类型,更多详细信息请参阅“支持类型”。

Variable Names  变量名称

The names of both story and temporary variables have a certain format that they must follow—which signifies that they are variables and not some other kind of data.
故事变量和临时变量的命名都遵循一定的格式,这表明它们是变量,而不是其他类型的数据。

The very first, and mandatory, character is their sigil, which denotes whether they are a story or temporary variable. The sigil must be a dollar sign ($) for story variables or an underscore (_) for temporary variables.
第一个且必须的字符是它们的符号,它表示它们是故事变量还是临时变量。符号必须是美元符号( $ )用于故事变量,或者下划线( _ )用于临时变量。

The second, and also mandatory, character of the variable name may be one of the following: the letters A though Z (in upper or lower case), the dollar sign, and the underscore (i.e., A-Za-z$_)—after their initial use as the sigil, the dollar sign and underscore become regular variable characters.
第二个字符,也是变量名必须遵守的规则之一,可以是以下之一:大写或小写的字母 A 到 Z、美元符号和下划线(即 A-Za-z$_ )——在它们作为符号的初始使用之后,美元符号和下划线成为常规变量字符。

Subsequent, optional, characters have the same set as the second with the addition of numerals (i.e., 0-9, so the full set is A-Za-z0-9$_). No other characters are allowed.
后续可选字符与第二个字符集相同,并增加了数字(例如 0-9 ,所以完整集合为 A-Za-z0-9$_ )。不允许其他字符。

A few examples of valid names:
一些有效的名称示例:

/* Story variables */
$cash
$hasKeyCard5
$met_alice
$TIMES_POKED_MR_BEAR

/* Temporary variables */ _i _something2 _some_loop_value _COUNT

Using Variables  使用变量

Note: This is not an exhaustive list. There are many ways to use and interact with variables.
注意:这不是一个详尽的列表。有许多使用和交互变量的方式。

To modify the values contained within variables, see the <<set>> macro and setter links.
要修改变量中包含的值,请参阅 <<set>> 宏和设置器链接。

To print the values contained within variables, see the naked variable markup and the <<print>>, <<=>>, and <<->> macros.
要打印变量中包含的值,请参阅裸变量标记和 <<print>><<=>><<->> 宏。

To control aspects of your project based on the values contained within variables, see the <<if>> and <<switch>> macros.
要根据变量中包含的值控制项目的一些方面,请参阅 <<if>><<switch>> 宏。

 Supported Types   支持的类型

The following types of values are natively supported by SugarCube and may be safely used within story and temporary variables.
以下类型的数据由 SugarCube 原生支持,可以在故事和临时变量中安全使用。

Primitives  基本类型

Objects  对象

Any supported object type may itself contain any supported primitive or object type.
任何支持的对象类型本身可以包含任何支持的原始或对象类型。

Unsupported object types, either native or custom, can be made compatible by implementing .clone() and .toJSON() methods for them—see the Non-generic object types (classes) guide for more information.
不支持的对象类型,无论是原生还是自定义,都可以通过实现 .clone().toJSON() 方法使其兼容——有关更多信息,请参阅“非泛型对象类型(类)”指南。

Warning:  警告:

Due to how SugarCube stores the state history a few constructs are not supported within story variables.
由于 SugarCube 存储状态历史的方式,一些结构不支持在故事变量中使用。

 Expressions   表达式

Expressions are simply units of code that yield values when evaluated. For example:
表达式是代码单元,当它们被评估时会产生值。例如:

// Yields: true
true

// Yields: 1 (assuming that it is the first turn) turns()

// Yields: 4 2 + 2

// Yields: "22" "2" + "2"

Basic expressions simply consist of identifiers and literals—e.g., $a, 69, and "hello". Complex expressions consist of basic expressions joined together by operators—e.g., = and +.
基本表达式仅由标识符和字面量组成——例如: $a69 ,和 "hello" 。复杂表达式由基本表达式通过运算符连接而成——例如: =+

While every valid expression—even those you might not expect—yields a value, there are essentially two types of expressions: those with side effects and those without. A side effect simply means that the evaluation of the expression modifies some state. For example:
虽然每个有效的表达式——甚至是你可能没有预料到的——都会产生一个值,但本质上存在两种类型的表达式:带有副作用和不带有副作用的表达式。副作用简单来说就是表达式的评估会修改某些状态。例如:

// Yields: 5; Side effect: assigns 5 to the story variable $a
$a = 5

// Yields: 25 (assuming $x is 15); No side effects $x + 10

In general, you can group expressions into categories based on what kind of value they yield and/or what side effects they cause. For example: (not an exhaustive list)
通常,你可以根据表达式产生的值类型和/或它们引起的副作用将表达式分组。例如:(以下非详尽列表)

Using Expressions  使用表达式

You will, in all likelihood, use expressions most often within macros—e.g., <<set>>, <<print>>, <<if>>, <<for>>.
你很可能会在宏中频繁使用表达式——例如, <<set>><<print>><<if>><<for>>

 Operators   运算符

Operators join together operands, which are formed from either basic or complex expressions.
运算符将操作数连接起来,操作数由基本或复杂表达式形成。

In both TwineScript and JavaScript there are binary and unary operators—n.b., Javascript also includes a ternary operator, the conditional operator. Binary operators require two operands, one before and one after the operator, while unary operators only require one operand, either before or after the operator.
在 TwineScript 和 JavaScript 中都有二元和一元运算符——请注意,JavaScript 还包括三元运算符,即条件运算符。二元运算符需要两个操作数,一个在运算符之前,一个在运算符之后,而一元运算符只需要一个操作数,可以在运算符之前或之后。

Binary operator examples:
二元运算符示例:

// operand1 OPERATOR operand2
2 + 2
$a = 5

Unary operator examples:
一元运算符示例:

// operand OPERATOR
$i++

// OPERATOR operand ++$x not $hasKey

Assignment operators  赋值运算符

Assignment operators assign a value to their left-hand operand based on the value of their right-hand operand.
赋值运算符根据其右操作数的值将其左操作数赋值。

TwineScript assignment operators:
TwineScript 赋值运算符:

Operator  操作员 Description  描述 Example  示例
to Assigns the value on the right-hand side of the operator to the left-hand side.
将运算符右侧的值分配给左侧。
$apples to 6

JavaScript assignment operators: (not an exhaustive list)
JavaScript 赋值运算符:(非详尽列表)

Operator  操作员 Description  描述 Example  示例
= Assigns the value on the right-hand side of the operator to the left-hand side.
将运算符右侧的值分配给左侧。
$apples = 6
+= Adds the value on the right-hand side of the operator to the current value on the left-hand side and assigns the result to the left-hand side.
将运算符右侧的值与左侧的当前值相加,并将结果分配给左侧。
$apples += 1
-= Subtracts the value on the right-hand side of the operator from the current value on the left-hand side and assigns the result to the left-hand side.
从左侧的当前值中减去运算符右侧的值,并将结果分配给左侧。
$apples -= 1
*= Multiplies the current value on the left-hand side of the operator by the value on the right-hand side and assigns the result to the left-hand side.
将运算符左侧的当前值乘以右侧的值,并将结果分配给左侧。
$apples *= 2
/= Divides the current value on the left-hand side of the operator by the value on the right-hand side and assigns the result to the left-hand side.
将运算符左侧的当前值除以右侧的值,并将结果分配给左侧。
$apples /= 2
%= Divides the current value on the left-hand side of the operator by the value on the right-hand side and assigns the remainder to the left-hand side.
将运算符左侧的当前值除以右侧的值,并将余数分配给左侧。
$apples %= 10

Conditional operators  条件运算符

Comparison operators compare their operands and return a boolean value based on whether the comparison is true.
比较运算符比较它们的操作数,并根据比较是否为真返回布尔值。

TwineScript conditional operators:
TwineScript 条件运算符:

Operator  操作员 Description  描述 Example  示例
is Evaluates to true if both sides are strictly equal.
如果两边严格相等,则评估为 true
$bullets is 6
isnot Evaluates to true if both sides are strictly not equal.
如果两边严格不相等,则评估为 true
$pie isnot "cherry"
eq Evaluates to true if both sides are equivalent. Not recommended, use the is operator.
如果两边等价,则评估为 true 。不推荐使用,请使用 is 运算符。
$bullets eq 6
neq Evaluates to true if both sides are not equivalent. Not recommended, use the isnot operator.
如果两边不相等,则返回 true 。不推荐使用,请使用 isnot 运算符。
$pie neq "cherry"
gt Evaluates to true if the left side is greater than the right side.
判断结果为 true ,如果左侧大于右侧。
$cash gt 5
gte Evaluates to true if the left side is greater than or equal to the right side.
计算结果为 true ,如果左侧大于或等于右侧。
$foundStars gte $neededStars
lt Evaluates to true if the left side is less than the right side.
等于 true 如果左侧小于右侧。
$shoeCount lt ($peopleCount * 2)
lte Evaluates to true if the left side is less than or equal to the right side.
如果左侧小于或等于右侧,则返回 true
$level lte 30
not Flips a true evaluation to false, and vice versa.
true 的评估结果翻转至 false ,反之亦然。
not $hungry
and Evaluates to true if all subexpressions evaluate to true.
如果所有子表达式都评估为 true ,则返回 true
$age gte 20 and $age lte 30
or Evaluates to true if any subexpressions evaluate to true.
如果任何子表达式评估为 true ,则返回 true
$friend is "Sue" or $friend is "Dan"
def Evaluates to true if the right side is defined. See the precedence warning below.
如果右侧已定义,则评估为 true 。下面是优先级警告。
def $mushrooms
ndef Evaluates to true if the right side is not defined. See the precedence warning below.
如果右侧未定义,则评估为 true 。下面是优先级警告。
ndef $bottlecaps

Warning: The def and ndef operators have very low precedence, so it is strongly recommended that if you mix them with other operators, that you wrap them in parentheses—e.g., (def $style) and ($style is "girly").
警告: defndef 运算符的优先级非常低,因此强烈建议如果您将它们与其他运算符混合使用,请将它们放在括号内——例如, (def $style) and ($style is "girly")

JavaScript conditional operators: (not an exhaustive list)
JavaScript 条件运算符:(非详尽列表)

Operator  操作员 Description  描述 Example  示例
=== Evaluates to true if both sides are strictly equal.
如果两边严格相等,则返回 true
$bullets === 6
!== Evaluates to true if both sides are strictly not equal.
如果两边严格不相等,则返回 true
$pie !== "cherry"
== Evaluates to true if both sides are equivalent. Not recommended, use the === operator.
等于 true 如果两边相等。不推荐,请使用 === 操作符。
$bullets == 6
!= Evaluates to true if both sides are not equivalent. Not recommended, use the !== operator.
如果两边不相等,则返回 true 。不推荐使用,请使用 !== 运算符。
$pie != "cherry"
> Evaluates to true if the left side is greater than the right side.
判断结果为 true ,如果左侧大于右侧。
$cash > 5
>= Evaluates to true if the left side is greater than or equal to the right side.
计算结果为 true ,如果左侧大于或等于右侧。
$foundStars >= $neededStars
< Evaluates to true if the left side is less than the right side.
等于 true 如果左侧小于右侧。
$shoeCount < ($peopleCount * 2)
<= Evaluates to true if the left side is less than or equal to the right side.
等于 true ,如果左侧小于或等于右侧。
$level <= 30
! Flips a true evaluation to false, and vice versa.
翻转 true 评估为 false ,反之亦然。
!$hungry
&& Evaluates to true if all subexpressions evaluate to true.
如果所有子表达式都评估为 true ,则评估为 true
$age >= 20 && $age <= 30
|| Evaluates to true if any subexpressions evaluate to true.
如果任何子表达式评估为 true ,则评估为 true
$friend === "Sue" || $friend === "Dan"

 Macros   

 Macro Arguments   宏参数

Macros fall into two broad categories based on the kind of arguments they accept: those that want an expression—e.g., <<set>> and <<print>>—and those that want discrete arguments separated by whitespace—e.g., <<link>> and <<audio>>. The documentation for each macro will tell you what it expects.
宏分为两大类,根据它们接受的参数类型:那些想要一个表达式——例如, <<set>><<print>> ——以及那些想要由空格分隔的离散参数——例如, <<link>><<audio>> 。每个宏的文档将告诉您它期望的内容。

Those that want an expression are fairly straightforward, as you simply supply an expression.
那些想要表达式的人相当简单,因为你只需提供一个表达式即可。

The discrete argument type of macros are also fairly straightforward, most of the time, as you simply supply the requisite arguments separated by whitespace, which may include variables—as SugarCube automatically yields their values to the macro. There are cases, however, where things get a bit more complicated, namely: instances where you need to pass the name of a variable as an argument, rather than its value, and those where you want to pass the result of an expression as argument.
宏的离散参数类型通常也很简单,你只需用空格分隔所需参数即可,这些参数可能包括变量——因为 SugarCube 会自动将它们的值传递给宏。然而,有时事情会变得稍微复杂一些,具体来说:当你需要传递变量的名称作为参数而不是其值时,以及当你想要传递表达式结果作为参数时。

Argument type macros: passing a variable's name as an argument
参数类型宏:将变量名作为参数传递

Passing the name of a variable as an argument is problematic because variable substitution occurs automatically in SugarCube macros. Meaning that when you pass a variable as an argument, its value is passed to the macro rather than its name.
传递变量名称作为参数是有问题的,因为在 SugarCube 宏中变量替换是自动发生的。这意味着当你传递一个变量作为参数时,传递的是它的值而不是它的名称。

Normally, this is exactly what you want to happen. Occasionally, however, macros will need the name of a variable rather than its value—e.g., data input macros like <<textbox>>—so that they may modify the variable. To resolve these instances, you will need to quote the name of the variable—i.e., instead of passing $pie as normal, you'd pass "$pie". These, rare, instances are noted in the macros' documentation and shown in their examples.
通常情况下,这正是你希望发生的事情。然而,有时宏需要变量的名称而不是其值——例如,像 <<textbox>> 这样的数据输入宏——以便它们可以修改变量。为了解决这些情况,你需要引用变量的名称——也就是说,你将像传递 "$pie" 一样传递 $pie 。这些罕见的实例在宏的文档中有说明,并在它们的示例中展示。

Argument type macros: passing an expression as an argument
参数类型宏:将表达式作为参数传递

Passing the result of an expression as an argument is problematic for a couple of reasons: because the macro argument parser doesn't treat arguments as expressions by default and because it separates arguments with whitespace.
传递表达式结果作为参数存在几个问题:因为宏参数解析器默认不将参数视为表达式,并且它使用空格来分隔参数。

Normally, those aren't issues as you should not need to use the result of an expression as an argument terribly often. To resolve instances where you do, however, you'll want to use either a temporary variable or a backquote expression.
通常情况下,这些问题并不严重,因为你通常不需要非常频繁地将表达式的结果用作参数。然而,当你确实需要这样做时,你将想要使用一个临时变量或反引号表达式来解决这个问题。

For example, the following will not work because the macro parser will think that you're passing five discrete arguments, rather than a single expression:
例如,以下代码将无法正常工作,因为宏解析器会认为您传递了五个独立的参数,而不是一个单独的表达式:

<<link "Wake " + $friend + ".">> … <</link>>

You could solve the problem by using a temporary variable to hold the result of the expression, then pass that to the macro. For example:
您可以通过使用一个临时变量来保存表达式的结果,然后将该结果传递给宏来解决该问题。例如:

<<set _text to "Wake " + $friend + ".">>\
<<link _text>> … <</link>>

A better solution, however, would be to use a backquote1 (`) expression, which is really just a special form of quoting available in macro arguments that causes the contents of the backquotes to be evaluated and then yields the result as a singular argument. For example:
更好的解决方案是使用反引号 1( ` )表达式,这实际上是一种在宏参数中可用的特殊引用形式,它会导致反引号内的内容被评估,然后作为单个参数输出。例如:

<<link `"Wake " + $friend + "."`>> … <</link>>
  1. A backquote is also known as a grave and is often paired with the tilde (~) on keyboards.
    反引号也称为重音符,通常与波浪号( ~ )一起出现在键盘上。

 Variables Macros   变量 宏

 <<capture variableList>> … <</capture>>

Captures story $variables and temporary _variables, creating localized versions of their values within the macro body.
捕获故事变量和临时变量,在宏体内部创建其值的本地化版本。

Note: Use of this macro is only necessary when you need to localize a variable's value for use with an asynchronous macro—i.e., a macro whose contents are executed at some later time, rather than when it's invoked; e.g., interactive macros, <<repeat>>, <<timed>>. Generally, this means only when the variable's value will change between the time the asynchronous macro is invoked and when it's activated—e.g., a loop variable.
注意:仅在需要为异步宏(即内容将在稍后时间执行的宏)本地化变量值时使用此宏——即,在调用时而不是在调用时执行的宏;例如,交互式宏, <<repeat>><<timed>> 。通常,这意味着只有当变量值在异步宏被调用和激活之间会发生变化时——例如,循环变量。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Using <<capture>> to localize a temporary loop variable for use within a <<linkappend>>
<<set _what to [
	"a crab rangoon",
	"a gaggle of geese",
	"an aardvark",
	"the world's smallest violin"
]>>
<<for _i to 0; _i lt _what.length; _i++>>
	<<capture _i>>
		I spy with my little <<linkappend "eye" t8n>>, _what[_i]<</linkappend>>.
	<</capture>>
<</for>>

→ Capturing several variables at once <<capture $aStoryVar, $anotherStoryVar, _aTempVar>> … <</capture>>

 <<set expression>>

Sets story $variables and temporary _variables based on the given expression.
根据给定的表达式设置故事变量和临时变量。

History:  历史:

Arguments:  参数:

Examples:  示例:

Using the TwineScript "to" operator
使用 TwineScript 的"to"运算符
<<set $cheese to "a nice, sharp cheddar">>  → Assigns "a nice, sharp cheddar" to story variable $cheese
<<set $chestEmpty to true>>                 → Assigns boolean true to story variable $chestEmpty
<<set $sum to $a + $b>>                     → Assigns the summation of story variables $a and $b to $sum
<<set $gold to $gold + 5>>                  → Adds 5 to the value of story variable $gold
<<set _counter to _counter + 1>>            → Adds 1 to the value of temporary variable _counter
Using the standard JavaScript operators
使用标准的 JavaScript 运算符
<<set $cheese = "a nice, sharp cheddar">>   → Assigns "a nice, sharp cheddar" to story variable $cheese
<<set $chestEmpty = true>>                  → Assigns boolean true to story variable $chestEmpty
<<set $sum = $a + $b>>                      → Assigns the summation of story variables $a and $b to $sum
<<set $gold += 5>>                          → Adds 5 to the value of story variable $gold
<<set _counter += 1>>                       → Adds 1 to the value of temporary variable _counter

 <<unset variableList>>

Unsets story $variables, temporary _variables, and properties of objects stored within either.
取消设置故事变量、临时变量以及存储在其中的对象的属性

History:  历史:

Arguments:  参数:

Examples:  示例:

Basic usage, unsetting story and temporary variables.
基本用法,取消设置故事和临时变量。

<<unset $rumors>>
<<unset _npc>>

<<unset $rumors, _npc, _choices, $job>>

Unsetting object properties.
取消设置对象属性。

<<unset _choices.b>>
<<unset $towns['port ulster'].rumors>>

<<unset _choices.b, $towns['port ulster'].rumors, $pc.notes, _park.rides['wheel of death']>>

 Scripting Macros   脚本宏

 <<run expression>>

Functionally identical to <<set>>. Intended to be mnemonically better for uses where the expression is arbitrary code, rather than variables to set—i.e., <<run>> to run code, <<set>> to set variables.
<<set>> 功能相同。旨在在表达式为任意代码而非设置变量时提供更好的记忆性——即 <<run>> 运行代码, <<set>> 设置变量。

 <<script [language]>> … <</script>>

Silently executes its contents as either JavaScript or TwineScript code (default: JavaScript).
静默执行其内容作为 JavaScript 或 TwineScript 代码(默认:JavaScript)。

Note: The predefined variable output, which is a reference to a local content buffer, is available for use within the macro's code contents. Once the code has been fully executed, the contents of the buffer, if any, will be output.
注意:预定义变量 output ,它是对本地内容缓冲区的引用,可以在宏的代码内容中使用。一旦代码完全执行,缓冲区的内容(如果有)将被输出。

History:  历史:

Arguments:  参数:

Examples:  示例:

Basic usage  基本用法
<<script>>
	/* JavaScript code */
<</script>>
<<script TwineScript>>
	/* TwineScript code */
<</script>>
Accessing managed variables
访问管理变量
<<script>>
	/*
		When accessing managed variables in JavaScript, it's often a good idea
		to cache references to whichever variable store you happen to be using.
	*/
	const svars = State.variables;
	const tvars = State.temporary;

/* Access the `$items` story variable. */ if (svars.items.includes('bloody knife')) { /* Has a bloody knife. */ }

/* Access the `_hit` temporary variable. */ tvars.hit += 1; <</script>>
<<script TwineScript>>
	/* Access the `$items` story variable. */
	if ($items.includes('bloody knife')) {
		/* Has a bloody knife. */
	}

/* Access the `_hit` temporary variable. */ _hit += 1; <</script>>
Modifying the content buffer
修改内容缓冲区

There's no difference between JavaScript and TwineScript here.
在这里,JavaScript 和 TwineScript 没有区别。

<<script>>
	/* Parse some markup and append the result to the output buffer. */
	$(output).wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");
<</script>>

 Display Macros   显示宏

 <<= expression>>

Outputs a string representation of the result of the given expression. This macro is an alias for <<print>>.
输出给定表达式的字符串表示形式。此宏是 <<print>> 的别名。

Tip: If you only need to print the value of a TwineScript variable, then you may simply include it in your normal passage text and it will be printed automatically via the naked variable markup.
提示:如果您只需要打印 TwineScript 变量的值,那么您可以直接将其包含在正常段落文本中,它将通过裸变量标记自动打印。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Assuming $gold is 5
You found <<= $gold>> gold.             → Outputs: You found 5 gold.

→ Assuming $weight is 74.6466266 You weigh <<= $weight.toFixed(2)>> kg. → Outputs: You weigh 74.65 kg.

 <<- expression>>

Outputs a string representation of the result of the given expression. This macro is functionally identical to <<print>>, save that it also encodes HTML special characters in the output.
输出给定表达式的字符串表示形式。此宏在功能上与 <<print>> 相同,区别在于它还会在输出中编码 HTML 特殊字符。

Tip: If you only need to print the value of a TwineScript variable, then you may simply include it in your normal passage text and it will be printed automatically via the naked variable markup.
TIP:如果您只需要打印 TwineScript 变量的值,那么您只需将其包含在正常的段落文本中,它将通过裸变量标记自动打印。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Assuming $gold is 5
You found <<- $gold>> gold.             → Outputs: You found 5 gold.

→ Assuming $weight is 74.6466266 You weigh <<- $weight.toFixed(2)>> kg. → Outputs: You weigh 74.65 kg.

 <<do [tag tags] [element tag]>> … <</do>>

Displays its contents. Listens for <<redo>> macro commands upon which it updates its contents.
显示其内容。监听 <<redo>> 宏命令,根据这些命令更新其内容。

History:  历史:

Arguments:  参数:

Examples:  示例:

Basic usage  基本用法
<<set $money to 10>>

''Money:'' <<do>>$money<</do>>

<<link "Update money display">> <<set $money += 10>> <<redo>> <</link>>
<<set $key to "">> /* no key */

<<do>> <<if $key>> You have the $key key. <<else>> You do not have a key. <</if>> <</do>>

<<link "Update key display">> <<set $key to ["", "red", "blue", "skull"].random()>> <<redo>> <</link>>
Filtering updates  过滤更新
''Foo:'' <<do tag "foo foobar">><<= ["fee", "fie", "foe", "fum"].random()>><</do>>
''Bar:'' <<do tag "bar foobar">><<= ["alfa", "bravo", "charlie", "delta"].random()>><</do>>

<<link "Update foo">> <<redo "foo">> <</link>> <<link "Update bar">> <<redo "bar">> <</link>> <<link "Update foo & bar (1)">> <<redo "foo bar">> <</link>> <<link "Update foo & bar (2)">> <<redo "foobar">> <</link>>

 <<include passageName [elementName]>>
<<include linkMarkup [elementName]>>

Outputs the contents of the passage with the given name, optionally wrapping it within an HTML element. May be called either with the passage name or with a link markup.
输出具有给定名称的段落内容,可选地将其包裹在 HTML 元素中。可以使用段落名称或链接标记来调用。

History:  历史:

Arguments:  参数:

Passage name form  段落名称表单
Link markup form  链接标记形式

Examples:  示例:

<<include "Go West">>          → Include the passage "Go West"
<<include [[Go West]]>>        → Include the passage "Go West"
<<include "Go West" "div">>    → Include the passage "Go West", wrapping it within a <div>
<<include [[Go West]] "div">>  → Include the passage "Go West", wrapping it within a <div>

 <<nobr>> … <</nobr>>

Executes its contents and outputs the result, after removing leading/trailing newlines and replacing all remaining sequences of newlines with single spaces.
执行其内容并输出结果,在删除首尾换行符后,将所有剩余的换行序列替换为单个空格。

Note: The nobr special tag and Config.passages.nobr setting applies the same processing to an entire passage or all passages, respectively. The line continuation markup performs a similar function, though in a slightly different way.
注意: nobr 特殊标记和 Config.passages.nobr 设置将对整个段落或所有段落应用相同的处理。行续标记执行类似功能,但方式略有不同。

History:  历史:

Arguments: none  无参数

Examples:  示例:

→ Given: $feeling eq "blue", outputs: I'd like a blueberry pie.
I'd like a <<nobr>>
<<if $feeling eq "blue">>
blueberry
<<else>>
cherry
<</if>>
<</nobr>> pie.

 <<print expression>>

Outputs a string representation of the result of the given expression.
输出给定表达式的字符串表示形式。

Tip: If you only need to print the value of a TwineScript variable, then you may simply include it in your normal passage text and it will be printed automatically via the naked variable markup.
提示:如果您只需要打印 TwineScript 变量的值,那么您可以直接将其包含在正常段落文本中,它将通过裸变量标记自动打印。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Assuming $gold is 5
You found <<print $gold>> gold.             → Outputs: You found 5 gold.

→ Assuming $weight is 74.6466266 You weigh <<print $weight.toFixed(2)>> kg. → Outputs: You weigh 74.65 kg.

 <<redo [tags]>>

Causes one or more <<do>> macros to update their contents.
导致一个或多个 <<do>> 宏更新其内容。

History:  历史:

Arguments:  参数:

Examples:  示例:

See: <<do>> macro for examples.
查看: <<do>> 宏示例。

 <<silent>> … <</silent>>

Causes any output generated within its body to be discarded, except for errors (which will be displayed). Generally, only really useful for formatting blocks of macros for ease of use/readability, while ensuring that no output is generated, from spacing or whatnot.
导致其体内生成的任何输出都被丢弃,除了错误(将显示)。通常,仅对格式化宏块以方便使用/可读性非常有用,同时确保不生成任何输出,从间距等。

History:  历史:

Arguments: none  无参数

Examples:  示例:

→ Basic
<<silent>>

You'll never see any of this!

<</silent>>

→ Hiding the guts of a countdown timer <<set $seconds to 10>>\ Countdown: <span id="countdown">$seconds seconds remaining</span>!\ <<silent>> <<repeat 1s>> <<set $seconds to $seconds - 1>> <<if $seconds gt 0>> <<replace "#countdown">>$seconds seconds remaining<</replace>> <<else>> <<replace "#countdown">>Too Late<</replace>> /* do something useful here */ <<stop>> <</if>> <</repeat>> <</silent>>

 <<type speed [start delay] [class classes] [element tag] [id ID] [keep|none] [skipkey key]>>

<</type>>

Outputs its contents a character—technically, a code point—at a time, mimicking a teletype/typewriter. Can type most content: links, markup, macros, etc.
输出其内容,一个字符——技术上,一个码点——一次,模仿电传打字机/打字机。可以输入大多数内容:链接、标记、宏等。

Warning: Interactions with macros or other code that inject content only after some external action or period—e.g., <<linkreplace>>, <<timed>>, etc.—may or may not behave as you'd expect. Testing is strongly advised.
警告:与在某个外部动作或一段时间后仅注入内容的宏或其他代码(例如, <<linkreplace>><<timed>> 等)的交互可能不会按预期行为。强烈建议进行测试。

See Also: Config.macros.typeSkipKey, Config.macros.typeVisitedPassages, <<type>> Events.
参见: Config.macros.typeSkipKeyConfig.macros.typeVisitedPassages<<type>> 事件。

History:  历史:

Arguments:  参数:

Examples:  示例:

<<type 40ms>>
	Type characters from this content every 40 milliseconds.  Including [[links]] and ''other markup''!
<</type>>

<<type 40ms start 2s>> Type characters from this content every 40 milliseconds, starting after a 2 second delay. <</type>>

<<type 40ms class "foo bar">> Type characters from this content every 40 milliseconds, adding classes to the typing container. <</type>>

<<type 40ms element "span">> Type characters from this content every 40 milliseconds, using a <span> as the typing container. <</type>>

<<type 40ms id "type01">> Type characters from this content every 40 milliseconds, assigning an ID to the typing container. <</type>>

<<type 40ms keep>> Type characters from this content every 40 milliseconds, keeping the cursor after typing is complete. <</type>>

<<type 40ms skipkey "Control">> Type characters from this content every 40 milliseconds, using the Control (CTRL) key as the skip key. <</type>>

CSS styles:  CSS 样式:

The typed text has no default styling. If you want to change the font or color, then you'll need to change the styling of the macro-type class. For example:
打字文本默认没有样式。如果您想更改字体或颜色,则需要更改 macro-type 类的样式。例如:

.macro-type {
	color: limegreen;
	font-family: monospace, monospace;
}

There's also a macro-type-done class that is added to text that has finished typing, which may be used to style it differently from actively typing text.
此外,还有一个 macro-type-done 类会被添加到已完成的打字文本上,可以用来将其与正在打字的文本进行不同的样式处理。

The default cursor is the block element character Right Half Block (U+2590) and it has no default font or color styling. If you want to change the font, color, or character, then you'll need to change the styling of the :after pseudo-element of the macro-type-cursor class. For example:
默认光标是字符块元素右半块(U+2590),它没有默认的字体或颜色样式。如果您想更改字体、颜色或字符,则需要更改 macro-type-cursor 类的 :after 伪元素的样式。例如:

.macro-type-cursor:after {
	color: limegreen;
	content: "\269C\FE0F"; /* Fleur-de-lis emoji */
	font-family: monospace, monospace;
}

 <<silently>> … <</silently>>

Deprecated: This macro has been deprecated and should no longer be used. See the <<silent>> macro for its replacement.
已弃用:此宏已被弃用,不应再使用。请参阅 <<silent>> 宏以获取其替代方案。

History:  历史:

 Control Macros   控制宏

 <<if conditional>> … [<<elseif conditional>> …] [<<else>> …] <</if>>

Executes its contents if the given conditional expression evaluates to true. If the condition evaluates to false and an <<elseif>> or <<else>> exists, then other contents can be executed.
如果给定的条件表达式评估为 true ,则执行其内容。如果条件评估为 false ,并且存在 <<elseif>><<else>> ,则可以执行其他内容。

Note: SugarCube does not trim whitespace from the contents of <<if>> macros, so that authors don't have to resort to various kludges to get whitespace where they want it. This means, however, that extra care must be taken when writing them to ensure that unwanted whitespace is not created within the final output.
注意:SugarCube 不会从 <<if>> 宏的内容中删除空白字符,这样作者就不必求助于各种技巧来获取他们想要的空白字符。然而,这也意味着在编写时必须格外小心,以确保最终输出中不会产生不想要的空白字符。

History:  历史:

Arguments:  参数:

Examples:  示例:

<<if $daysUntilLoanDue is 0>><<include "Panic">><</if>>

<<if $cash lt 5>> I'm sorry, ma'am, but you don't have enough for the pie. <<else>> <<set $cash -= 5, $hasMeatPie = true>> One meat pie, fresh out of the oven, coming up! <</if>>

<<if $affection gte 75>> I love you! <<elseif $affection gte 50>> I like you. <<elseif $affection gte 25>> I'm okay with you. <<else>> Get out of my face. <</if>>

<<if $hullBreached>> <<if $wearingHardSuit>> <<include "That was close">> <<elseif $wearingSoftSuit>> <<include "Hole in suit">> <<else>> <<include "You die">> <</if>> <</if>>

 <<for [conditional]>> … <</for>>
<<for [init] ; [conditional] ; [post]>> … <</for>>
<<for [[keyVariable ,] valueVariable] range collection>> … <</for>>

Repeatedly executes its contents. There are three forms: a conditional-only form, a 3-part conditional form, and a range form.
反复执行其内容。有三种形式:仅条件形式、三部分条件形式和范围形式。

See Also: <<break>> and <<continue>>.
参见: <<break>><<continue>>

History:  历史:

Notes  备注

Conditional forms (both conditional-only and 3-part)
条件表单(包括仅条件表单和三部分表单)

Executes its contents while the given conditional expression evaluates to true. If no conditional expression is given, it is equivalent to specifying true.
当给定的条件表达式评估为 true 时执行其内容。如果没有提供条件表达式,则相当于指定 true

Note: The maximum number of loop iterations in the conditional forms is not unlimited by default, however, it is configurable. See Config.macros.maxLoopIterations for more information.
注意:默认情况下,条件表单中的最大循环迭代次数不是无限的,但是可以配置。请参阅 Config.macros.maxLoopIterations 获取更多信息。

Arguments:  参数:
Examples: (only 3-part conditional form shown)
示例:(仅显示 3 部分条件形式)
→ Example setup
<<set $dwarves to ["Doc", "Dopey", "Bashful", "Grumpy", "Sneezy", "Sleepy", "Happy"]>>

→ Loop <<for _i to 0; _i lt $dwarves.length; _i++>> <<print _i + 1>>. $dwarves[_i] <</for>>

→ Result 1. Doc 2. Dopey 3. Bashful 4. Grumpy 5. Sneezy 6. Sleepy 7. Happy

Range form  范围形式

Iterates through all enumerable entries of the given collection. For each iteration, it assigns the key/value pair of the associated entry in the collection to the iteration variables and then executes its contents. Valid collection types are: arrays, generic objects, integers, maps, sets, and strings.
遍历给定集合的所有可枚举条目。对于每次迭代,它将集合中相关条目的键/值对分配给迭代变量,然后执行其内容。有效的集合类型包括:数组、泛型对象、整数、映射、集合和字符串。

Arguments:  参数:
Iteration Values:  迭代值:
Collection type  收藏类型 Iteration: key, value  迭代:键,值
Arrays, Integers, & Sets
数组、整数和集合
Member: index, value  成员:索引,值
Generic objects  泛型对象 Property: name, value  属性:名称,值
Maps  映射 Entry: key, value  条目:键,值
Strings  字符串 Code point: start index, value
代码点:起始索引,值

Note: Strings are iterated by Unicode code point, however, due to historic reasons they are comprised of, and indexed by, individual UTF-16 code units. This means that some code points may span multiple code units—e.g., the character 💩 is one code point, but two code units.
注意:字符串按 Unicode 代码点迭代,但由于历史原因,它们由单个 UTF-16 代码单元组成,并按单个代码单元索引。这意味着某些代码点可能跨越多个代码单元——例如,字符💩是一个代码点,但由两个代码单元组成。

Examples:  示例:
Range over array  遍历数组范围
→ Example setup
<<set $dwarves to ["Doc", "Dopey", "Bashful", "Grumpy", "Sneezy", "Sleepy", "Happy"]>>

→ Loop <<for _i, _name range $dwarves>> <<print _i + 1>>. _name <</for>>

→ Result 1. Doc 2. Dopey 3. Bashful 4. Grumpy 5. Sneezy 6. Sleepy 7. Happy
Range over integer  遍历整数
→ Loop
<<for _value range 7>>
<<print _value + 1>>.
<</for>>

→ Result 1. 2. 3. 4. 5. 6. 7.

 <<break>>

Used within <<for>> macros. Terminates the execution of the current <<for>>.
<<for>> 宏中使用。终止当前 <<for>> 的执行。

History:  历史:

Arguments: none  无参数

 <<continue>>

Used within <<for>> macros. Terminates the execution of the current iteration of the current <<for>> and begins execution of the next iteration.
用于 <<for>> 宏中。终止当前 <<for>> 的当前迭代执行,并开始执行下一个迭代。

Note: May eat line-breaks in certain situations.
注意:在某些情况下可能会吃掉换行符。

History:  历史:

Arguments: none  无参数

 <<switch expression>>
[<<case valueList>> …]
[<<default>> …]

<</switch>>

Evaluates the given expression and compares it to the value(s) within its <<case>> children. The value(s) within each case are compared to the result of the expression given to the parent <<switch>>. Upon a successful match, the matching case will have its contents executed. If no cases match and an optional <<default>> case exists, which must be the final case, then its contents will be executed. At most one case will execute.
评估给定的表达式,并将其与 <<case>> 子项中的值进行比较。每个案例中的值与提供给父项的表达式结果进行比较。如果匹配成功,则执行匹配的案例的内容。如果没有案例匹配,并且存在可选的 <<default>> 案例,则必须是最終案例,其内容将被执行。最多执行一个案例。

Note: SugarCube does not trim whitespace from the contents of <<case>>/<<default>> macros, so that authors don't have to resort to various kludges to get whitespace where they want it. However, this means that extra care must be taken when writing them to ensure that unwanted whitespace is not created within the final output.
注意:SugarCube 不会从 <<case>> / <<default>> 宏的内容中删除空白字符,这样作者就不必求助于各种技巧来获取他们想要的空白字符。然而,这意味着在编写时必须格外小心,以确保最终输出中不会创建不想要的空白字符。

History:  历史:

Arguments:  参数:

<<switch>>
<<case>>

Examples:  示例:

→ Without a default case
<<switch $hairColor>>
<<case "red" "auburn">>
	You ginger.
<<case "black" "brown">>
	Dark haired, eh?
<<case "blonde">>
	You may have more fun.
<</switch>>

→ With a default case (assume the passage is about a waterfall) <<switch visited()>> <<case 1>> You gaze in wonder at the magnificent waterfall for the first time, awestruck by its natural beauty. <<case 2 3>> You once again gaze upon the magnificent waterfall. <<case 4 5>> Yet again, you find yourself looking upon the waterfall. <<default>> Oh, look. It's that waterfall again. Meh. <</switch>>

 Interactive Macros   交互式宏

 Warning   警告

Interactive macros are both asynchronous and require interaction from the player. Thus, there are some potential pitfalls to consider:
交互式宏既是异步的,又需要玩家的交互。因此,有一些潜在的陷阱需要考虑:

  1. If you plan on using interactive macros within a loop you will likely need to use the <<capture>> macro due to their asynchronous nature.
    如果您计划在循环中使用交互式宏,由于它们的异步性质,您可能需要使用 <<capture>> 宏。
  2. Reloading the page or revisiting a passage may not restore the state of some interactive macros, so it is recommended that you only use them in instances where this will not be an issue or where you can work around it.
    页面重新加载或重新访问一个段落可能无法恢复某些交互式宏的状态,因此建议您仅在不会出现此类问题或可以解决此问题的实例中使用它们。

 <<button linkText [passageName]>> … <</button>>
<<button linkMarkup>> … <</button>>
<<button imageMarkup>> … <</button>>

Creates a button that silently executes its contents when clicked, optionally forwarding the player to another passage. May be called with either the link text and passage name as separate arguments, a link markup, or an image markup.
创建一个按钮,当点击时,会静默执行其内容,并可选择将玩家转发到另一个段落。可以使用链接文本和段落名称作为单独的参数,也可以使用链接标记或图像标记来调用。

See: Interactive macro warning.
查看:交互宏警告。

Note: This macro is functionally identical to <<link>>, save that it uses a button element (<button>) rather than an anchor element (<a>).
注意:此宏在功能上与 <> 相同,区别在于它使用按钮元素( <button> )而不是锚点元素( <a> )。

History:  历史:

Arguments:  参数:

Separate argument form  分离参数形式
Link markup form  链接标记形式
Image markup form  图片标记表单

Examples:  示例:

→ Without forwarding: a very basic statistic setting example
Strength: <<set $pcStr to 10>><span id="stats-str"><<print $pcStr>></span> \
( <<button "[+]">><<set $pcStr++>><<replace "#stats-str">><<print $pcStr>><</replace>><</button>> \
| <<button "[-]">><<set $pcStr-->><<replace "#stats-str">><<print $pcStr>><</replace>><</button>> )

→ With forwarding: execute a script, then go to the specified passage <<button "Onward, Reginald!" "On with the story">><<script>>/* (code) */<</script>><</button>> <<button [[Onward, Reginald!|On with the story]]>><<script>>/* (code) */<</script>><</button>> <<button [img[onward.jpg][On with the story]]>><<script>>/* (code) */<</script>><</button>>

 <<checkbox receiverName uncheckedValue checkedValue [autocheck|checked]>>

Creates a checkbox, used to modify the value of the variable with the given name.
创建一个复选框,用于修改给定名称的变量的值。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

Basic usage  基本用法
What pies do you enjoy?
* <<checkbox "$pieBlueberry" false true autocheck>> Blueberry?
* <<checkbox "$pieCherry" false true autocheck>> Cherry?
* <<checkbox "$pieCoconutCream" false true autocheck>> Coconut cream?
What pies do you enjoy?
* <<checkbox "$pieBlueberry" false true checked>> Blueberry?
* <<checkbox "$pieCherry" false true>> Cherry?
* <<checkbox "$pieCoconutCream" false true checked>> Coconut cream?
With a <label> element  使用 <label> 元素

Tip: For accessibility reasons, it's recommended that you wrap each <<checkbox>> and its accompanying text within a <label> element. Doing so allows interactions with the text to also trigger its <<checkbox>>.
提示:出于可访问性的考虑,建议将每个 <<checkbox>> 及其伴随的文本包裹在 <label> 元素内。这样做可以使与文本的交互同时触发其 <<checkbox>>

What pies do you enjoy?
* <label><<checkbox "$pieBlueberry" false true autocheck>> Blueberry?</label>
* <label><<checkbox "$pieCherry" false true autocheck>> Cherry?</label>
* <label><<checkbox "$pieCoconutCream" false true autocheck>> Coconut cream?</label>
What pies do you enjoy?
* <label><<checkbox "$pieBlueberry" false true checked>> Blueberry?</label>
* <label><<checkbox "$pieCherry" false true>> Cherry?</label>
* <label><<checkbox "$pieCoconutCream" false true checked>> Coconut cream?</label>

 <<cycle receiverName [once] [autoselect]>>
[<<option label [value [selected]]>> …]
[<<optionsfrom collection>> …]

<</cycle>>

Creates a cycling link, used to modify the value of the variable with the given name. The cycling options are populated via <<option>> and/or <<optionsfrom>>.
创建循环链接,用于修改给定名称的变量的值。循环选项通过 <<option>> 和/或 <<optionsfrom>> 填充。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

<<cycle>>
<<option>>
<<optionsfrom>>

Examples:  示例:

Using <<option>>  使用 <<option>>
The answer to the //Ultimate Question of Life, the Universe, and Everything// is?
<<cycle "$answer" autoselect>>
	<<option "Towel">>
	<<option "π" 3.14159>>
	<<option 42>>
	<<option 69>>
	<<option "∞" Infinity>>
<</cycle>>
Using <<optionsfrom>> with an array
使用 <<optionsfrom>> 与数组
→ Given: _pieOptions = ["blueberry", "cherry", "coconut cream"]
What's your favorite pie?
<<cycle "$pie" autoselect>>
	<<optionsfrom _pieOptions>>
<</cycle>>
Using <<optionsfrom>> with an generic object
使用 <<optionsfrom>> 与通用对象
→ Given: _pieOptions = { "Blueberry" : "blueberry", "Cherry" : "cherry", "Coconut cream" : "coconut cream" }
What's your favorite pie?
<<cycle "$pie" autoselect>>
	<<optionsfrom _pieOptions>>
<</cycle>>
Using the once keyword  使用 once 关键字
You see a large red, candy-like button.
<<cycle "$presses" once>>
	<<option "Should you press it?" 0>>
	<<option "Nothing happened.  Press it again?" 1>>
	<<option "Again?" 2>>
	<<option "That time it locked into place with a loud click and began to glow ominously." 3>>
<</cycle>>

Creates a link that silently executes its contents when clicked, optionally forwarding the player to another passage. May be called with either the link text and passage name as separate arguments, a link markup, or an image markup.
创建一个在点击时静默执行其内容的链接,可选地将玩家转发到另一个段落。可以使用链接文本和段落名称作为单独的参数,或使用链接标记或图像标记来调用。

See: Interactive macro warning.
查看:交互宏警告。

Note: If you simply need a passage link that modifies variables, both the link markup and image markup offer setter variants.
注意:如果您只需要一个修改变量的段落链接,链接标记和图像标记都提供设置器变体。

History:  历史:

Arguments:  参数:

Separate argument form  分离参数形式
Link markup form  链接标记形式
Image markup form  图片标记表单

Examples:  示例:

→ Without forwarding: a very basic statistic setting example
Strength: <<set $pcStr to 10>><span id="stats-str"><<print $pcStr>></span> \
( <<link "[+]">><<set $pcStr++>><<replace "#stats-str">><<print $pcStr>><</replace>><</link>> \
| <<link "[-]">><<set $pcStr-->><<replace "#stats-str">><<print $pcStr>><</replace>><</link>> )

→ With forwarding: execute a script, then go to the specified passage <<link "Onward, Reginald!" "On with the story">><<script>>/* (code) */<</script>><</link>> <<link [[Onward, Reginald!|On with the story]]>><<script>>/* (code) */<</script>><</link>> <<link [img[onward.jpg][On with the story]]>><<script>>/* (code) */<</script>><</link>>

 <<linkappend linkText [transition|t8n]>> … <</linkappend>>

Creates a single-use link that deactivates itself and appends its contents to its link text when clicked. Essentially, a combination of <<link>> and <<append>>.
创建一个一次性链接,点击后会自动失效,并将内容附加到链接文本中。本质上,是 <<link>><<append>> 的结合。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Without a transition
We—We should <<linkappend "take">> away their METAL BAWKSES<</linkappend>>!

→ With a transition I spy with my little <<linkappend "eye" t8n>>, a crab rangoon<</linkappend>>.

 <<linkprepend linkText [transition|t8n]>> … <</linkprepend>>

Creates a single-use link that deactivates itself and prepends its contents to its link text when clicked. Essentially, a combination of <<link>> and <<prepend>>.
创建一个一次性链接,点击后会自动失效,并将内容添加到链接文本前。本质上,是 <<link>><<prepend>> 的组合。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Without a transition
You see a <<linkprepend "robot">>GIANT <</linkprepend>>.

→ With a transition I <<linkprepend "like" t8n>>do not <</linkprepend>> lemons.

 <<linkreplace linkText [transition|t8n]>> … <</linkreplace>>

Creates a single-use link that deactivates itself and replaces its link text with its contents when clicked. Essentially, a combination of <<link>> and <<replace>>.
创建一个一次性链接,点击后会自动失效,并将链接文本替换为其内容。本质上,是 <<link>><<replace>> 的结合。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Without a transition
I'll have a <<linkreplace "cupcake">>slice of key lime pie<</linkreplace>>, please.

→ With a transition <<linkreplace "You'll //never// take me alive!" t8n>>On second thought, don't hurt me.<</linkreplace>>

 <<listbox receiverName [autoselect]>>
[<<option label [value [selected]]>> …]
[<<optionsfrom collection>> …]

<</listbox>>

Creates a listbox, used to modify the value of the variable with the given name. The list options are populated via <<option>> and/or <<optionsfrom>>.
创建一个列表框,用于修改给定名称的变量的值。列表选项通过 <<option>> 和/或 <<optionsfrom>> 填充。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

<<listbox>>
<<option>>
<<optionsfrom>>

Examples:  示例:

Using <<option>>  使用 <<option>>
The answer to the //Ultimate Question of Life, the Universe, and Everything// is?
<<listbox "$lbanswer" autoselect>>
	<<option "Towel">>
	<<option "π" 3.14159>>
	<<option 42>>
	<<option 69>>
	<<option "∞" Infinity>>
<</listbox>>
Using <<optionsfrom>> with an array
使用 <<optionsfrom>> 与数组
→ Given: _pieOptions = ["blueberry", "cherry", "coconut cream"]
What's your favorite pie?
<<listbox "$pie" autoselect>>
	<<optionsfrom _pieOptions>>
<</listbox>>
Using <<optionsfrom>> with an generic object
使用 <<optionsfrom>> 与通用对象
→ Given: _pieOptions = { "Blueberry" : "blueberry", "Cherry" : "cherry", "Coconut cream" : "coconut cream" }
What's your favorite pie?
<<listbox "$pie" autoselect>>
	<<optionsfrom _pieOptions>>
<</listbox>>

 <<numberbox receiverName defaultValue [passage] [autofocus]>>

Creates a number input box, used to modify the value of the variable with the given name, optionally forwarding the player to another passage.
创建一个数字输入框,用于修改给定名称的变量值,可选地引导玩家到另一个段落。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Creates a number box that modifies $wager
Wager how much on Buttstallion in the race? <<numberbox "$wager" 100>>

→ Creates an automatically focused number box that modifies $wager Wager how much on Buttstallion in the race? <<numberbox "$wager" 100 autofocus>>

→ Creates a number box that modifies $wager and forwards to the "Result" passage Wager how much on Buttstallion in the race? <<numberbox "$wager" 100 "Result">>

→ Creates an automatically focused number box that modifies $wager and forwards to the "Result" passage Wager how much on Buttstallion in the race? <<numberbox "$wager" 100 "Result" autofocus>>

 <<radiobutton receiverName checkedValue [autocheck|checked]>>

Creates a radio button, used to modify the value of the variable with the given name. Multiple <<radiobutton>> macros may be set up to modify the same variable, which makes them part of a radio button group.
创建一个单选按钮,用于修改给定名称的变量值。可以设置多个 <<radiobutton>> 宏来修改相同的变量,使它们成为单选按钮组的一部分。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

Basic usage  基本用法
What's your favorite pie?
* <<radiobutton "$pie" "blueberry" autocheck>> Blueberry?
* <<radiobutton "$pie" "cherry" autocheck>> Cherry?
* <<radiobutton "$pie" "coconut cream" autocheck>> Coconut cream?
What's your favorite pie?
* <<radiobutton "$pie" "blueberry" checked>> Blueberry?
* <<radiobutton "$pie" "cherry">> Cherry?
* <<radiobutton "$pie" "coconut cream">> Coconut cream?
With a <label> element  使用 <label> 元素

Tip: For accessibility reasons, it's recommended that you wrap each <<radiobutton>> and its accompanying text within a <label> element. Doing so allows interactions with the text to also trigger its <<radiobutton>>.
提示:出于可访问性的考虑,建议将每个 <<radiobutton>> 及其伴随的文本包裹在 <label> 元素内。这样做可以使与文本的交互同时触发其 <<radiobutton>>

What's your favorite pie?
* <label><<radiobutton "$pie" "blueberry" autocheck>> Blueberry?</label>
* <label><<radiobutton "$pie" "cherry" autocheck>> Cherry?</label>
* <label><<radiobutton "$pie" "coconut cream" autocheck>> Coconut cream?</label>
What's your favorite pie?
* <label><<radiobutton "$pie" "blueberry" checked>> Blueberry?</label>
* <label><<radiobutton "$pie" "cherry">> Cherry?</label>
* <label><<radiobutton "$pie" "coconut cream">> Coconut cream?</label>

 <<textarea receiverName defaultValue [autofocus]>>

Creates a multiline text input block, used to modify the value of the variable with the given name.
创建一个多行文本输入块,用于修改给定名称的变量的值。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Creates a text block that modifies $pieEssay
Write a short essay about pies:
<<textarea "$pieEssay" "">>

→ Creates an automatically focused text block that modifies $pieEssay Write a short essay about pies: <<textarea "$pieEssay" "" autofocus>>

 <<textbox receiverName defaultValue [passage] [autofocus]>>

Creates a text input box, used to modify the value of the variable with the given name, optionally forwarding the player to another passage.
创建一个文本输入框,用于修改给定名称的变量值,可选地引导玩家跳转到另一个段落。

See: Interactive macro warning.
查看:交互宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Creates a text box that modifies $pie
What's your favorite pie? <<textbox "$pie" "Blueberry">>

→ Creates an automatically focused text box that modifies $pie What's your favorite pie? <<textbox "$pie" "Blueberry" autofocus>>

→ Creates a text box that modifies $pie and forwards to the "Cakes" passage What's your favorite pie? <<textbox "$pie" "Blueberry" "Cakes">>

→ Creates an automatically focused text box that modifies $pie and forwards to the "Cakes" passage What's your favorite pie? <<textbox "$pie" "Blueberry" "Cakes" autofocus>>

 <<back [linkText [passageName]]>>
<<back linkMarkup>>
<<back imageMarkup>>

Creates a link that undoes past moments within the story history. May be called with, optional, the link text and passage name as separate arguments, a link markup, or an image markup.
创建一个链接,可以撤销故事历史中的过去时刻。可以带可选的链接文本和篇章名称作为单独的参数,也可以是链接标记或图像标记。

Note: If you want to return to a previously visited passage, rather than undo a moment within the history, see the <<return>> macro or the previous() function.
备注:如果您想返回之前访问过的篇章,而不是撤销历史中的某个时刻,请参阅 <<return>> 宏或 previous() 函数。

History:  历史:

Arguments:  参数:

Separate argument form  分离参数形式
Link markup form  链接标记形式
Image markup form  图片标记表单

Examples:  示例:

Visual aid  视觉辅助

Assume your story history consists of three moments:
假设你的故事历史由三个时刻组成:

A, B, [C]

N.b., the square brackets there denote the active moment.
注意,那里的方括号表示当前时刻。

Using <<back>> once upon that history will change it to be thus:
使用 <<back>> 在那个历史中会将其更改为:

A, [B], C

I.e., the history was rolled back to the previous moment.

Basic usage  基本用法
→ Creates a link that undoes the most recent moment, with default text
<<back>>
Separate argument form  分离参数形式
→ Creates a link that undoes the most recent moment, with text "Home."
<<back "Home.">>

→ Creates a link that undoes past moments until the most recent "HQ" moment is reached, with text "Home." <<back "Home." "HQ">>
Link markup form  链接标记形式
→ Creates a link that undoes past moments until the most recent "HQ" moment is reached, with default text
<<back [[HQ]]>>

→ Creates a link that undoes past moments until the most recent "HQ" moment is reached, with text "Home." <<back [[Home.|HQ]]>>
Image markup form  图片标记表单
→ Creates a link that undoes the most recent moment, with image "home.png"
<<back [img[home.png]]>>

→ Creates a link that undoes past moments until the most recent "HQ" moment is reached, with image "home.png" <<back [img[home.png][HQ]]>>

 <<return [linkText [passageName]]>>
<<return linkMarkup>>
<<return imageMarkup>>

Creates a link that navigates forward to a previously visited passage. May be called with, optional, the link text and passage name as separate arguments, a link markup, or an image markup.
创建一个导航到之前访问过的章节的链接。可以带有一个可选的链接文本和章节名称作为单独的参数,或者使用链接标记,或者使用图片标记。

Note: If you want to undo previous moments within the history, rather than return to a passage, see the <<back>> macro.
注意:如果您想撤销历史中的前几个时刻,而不是返回到某个章节,请参阅 <<back>> 宏。

History:  历史:

Arguments:  参数:

Separate argument form  分离参数形式
Link markup form  链接标记形式
Image markup form  图片标记表单

Examples:  示例:

Note: The versions that forward to a specific passage are largely unnecessary, as you could simply use a normal link, and exist solely for compatibility with the <<back>> macro.
注意:将版本转发到特定段落的大多数情况都是不必要的,因为您可以直接使用普通链接,并且仅为了与 <<back>> 宏的兼容性而存在。

Visual aid  视觉辅助

Assume your story history consists of three moments:
假设你的故事历史由三个时刻组成:

A, B, [C]

N.b., the square brackets there denote the active moment.
注意,那里的方括号表示当前时刻。

Using <<return>> once upon that history will change it to be thus:
使用 <<return>> 在那个历史中会将其更改为:

A, B, C, [B]

I.e., a new moment, to the same passage as the previous moment, was added to the history.
即,在历史中添加了一个新的时刻,与上一个时刻相同。

Basic usage  基本用法
→ Creates a link that forwards to the previous passage, with default text
<<return>>
Separate argument form  分离参数形式
→ Creates a link that forwards to the previous passage, with text "Home."
<<return "Home.">>

→ Creates a link that forwards to the "HQ" passage, with text "Home." <<return "Home." "HQ">>
Link markup form  链接标记形式
→ Creates a link that forwards to the "HQ" passage, with default text
<<return [[HQ]]>>

→ Creates a link that forwards to the "HQ" passage, with text "Home." <<return [[Home.|HQ]]>>
Image markup form  图片标记表单
→ Creates a link that forwards to the previous passage, with image "home.png"
<<return [img[home.png]]>>

→ Creates a link that forwards to the "HQ" passage, with image "home.png" <<return [img[home.png][HQ]]>>

 <<actions passageList>>
<<actions linkMarkupList>>
<<actions imageMarkupList>>

Deprecated: This macro has been deprecated and should no longer be used.
已弃用:此宏已被弃用,不应再使用。

History:  历史:

 <<choice passageName [linkText]>>
<<choice linkMarkup>>
<<choice imageMarkup>>

Deprecated: This macro has been deprecated and should no longer be used.
已弃用:此宏已被弃用,不应再使用。

History:  历史:

 DOM Macros   DOM 宏

Warning: All DOM macros require the elements to be manipulated to be on the page. As a consequence, you cannot use them directly within a passage to modify elements within said passage, since the elements they are targeting are still rendering, thus not yet on the page. You must, generally, use them with an interactive macro—e.g., <<link>> macro—the <<done>> macro, or within the PassageDone special passage. Elements that are already part of the page, on the other hand, present no issues.
警告:所有 DOM 宏都需要要操作的元素在页面上。因此,您不能直接在段落中使用它们来修改段落内的元素,因为这些元素仍在渲染中,尚未在页面上。通常,您必须与交互式宏(例如, <<link>> 宏)、 <<done>> 宏或 PassageDone 特殊段落一起使用。另一方面,已经是页面一部分的元素则没有问题。

 <<addclass selector classNames>>

Adds classes to the selected element(s).
添加类到所选元素(们)。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

<<addclass "body" "day rain">>  → Add the classes "day" and "rain" to the <body> element
<<addclass "#pie" "cherry">>    → Add the class "cherry" to the element with the ID "pie"
<<addclass ".joe" "angry">>     → Add the class "angry" to all elements containing the class "joe"

 <<append selector [transition|t8n]>> … <</append>>

Executes its contents and appends the output to the contents of the selected element(s).
执行其内容并将其输出追加到所选元素(s)的内容中。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

Without a transition  无过渡
→ Example setup
I saw a <span id="dog">dog</span>.

→ Append to the contents of the target element <<link "Doing">> <<append "#dog">> chasing a cat<</append>> <</link>>

→ Result, after clicking I saw a <span id="dog">dog chasing a cat</span>.
With a transition  带过渡效果
→ Example setup
I saw a <span id="dog">dog</span>.

→ Append to the contents of the target element <<link "Doing">> <<append "#dog" t8n>> chasing a cat<</append>> <</link>>

→ Result, after clicking I saw a <span id="dog">dog<span class="macro-append-insert"> chasing a cat</span></span>.

 <<copy selector>>

Outputs a copy of the contents of the selected element(s).
输出所选元素的内容副本。

Warning: Most interactive elements—e.g., passage links, interactive macros, etc.—cannot be properly copied via <<copy>>. Attempting to do so will, usually, result in something that's non-functional.
警告:大多数交互式元素(例如,篇章链接、交互式宏等)无法通过 <<copy>> 正确复制。通常,这样做会导致无法正常工作的内容。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Example setup
I'd like a <span id="snack-source">slice of Key lime pie</span>, please.

I'll have a <span id="snack-dest">breadstick</span>, thanks.

→ Replace the contents of the source target element with a copy of the destination target element <<link "Have the same">> <<replace "#snack-dest">><<copy "#snack-source">> too<</replace>> <</link>>

→ Result, after the click I'd like a <span id="snack-source">slice of Key lime pie</span>, please.

I'll have a <span id="snack-dest">slice of Key lime pie too</span>, thanks.

 <<prepend selector [transition|t8n]>> … <</prepend>>

Executes its contents and prepends the output to the contents of the selected element(s).
执行其内容并将输出内容添加到所选元素(s)的内容之前。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

Without a transition  没有过渡
→ Example setup
I saw a <span id="dog">dog</span>.

→ Prepend to the contents of the target element <<link "Size">> <<prepend "#dog">>big <</prepend>> <</link>>

→ Result, after clicking I saw a <span id="dog">big dog</span>.
With a transition  有过渡
→ Example setup
I saw a <span id="dog">dog</span>.

→ Prepend to the contents of the target element <<link "Size">> <<prepend "#dog" t8n>>big <</prepend>> <</link>>

→ Result, after clicking I saw a <span id="dog"><span class="macro-prepend-insert">big </span>dog</span>.

 <<remove selector>>

Removes the selected element(s).
删除选定的元素(们)。

See: DOM macro warning.
查看:DOM 宏警告。

Note: If you simply want to empty the selected element(s), not remove them outright, you should use an empty <<replace>> macro instead.
注意:如果您只想清空选定的元素(而不是直接删除它们),应使用一个空的 <<replace>> 宏。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Given the following
I'd like a <span id="huge-cupcake">humongous </span>cupcake, please.

→ Remove the target element <<link "Go small">> <<remove "#huge-cupcake">> <</link>>

→ Result, after the click I'd like a cupcake, please.

 <<removeclass selector [classNames]>>

Removes classes from the selected element(s).
从选定的元素(s)中删除类。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

<<removeclass "body" "day rain">>  → Remove the classes "day" and "rain" from the <body> element
<<removeclass "#pie" "cherry">>    → Remove the class "cherry" from the element with the ID "pie"
<<removeclass ".joe" "angry">>     → Remove the class "angry" from all elements containing the class "joe"
<<removeclass "#begone">>          → Remove all classes from the element with the ID "begone"

 <<replace selector [transition|t8n]>> … <</replace>>

Executes its contents and replaces the contents of the selected element(s) with the output.
执行其内容,并用输出替换所选元素的内容。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Usage  用法

Without a transition  无过渡
→ Example setup
I saw a <span id="dog">dog</span>.

→ Replace the contents of the target element <<link "Breed">> <<replace "#dog">>Catahoula Cur<</replace>> <</link>>

→ Result, after clicking I saw a <span id="dog">Catahoula Cur</span>.
With a transition  带过渡效果
→ Example setup
I saw a <span id="dog">dog</span>.

→ Replace the contents of the target element <<link "Breed">> <<replace "#dog" t8n>>Catahoula Cur<</replace>> <</link>>

→ Result, after clicking I saw a <span id="dog"><span class="macro-replace-insert">Catahoula Cur</span></span>.

 <<toggleclass selector classNames>>

Toggles classes on the selected element(s)—i.e., adding them if they don't exist, removing them if they do.
在选定的元素上切换类——即如果它们不存在则添加,如果存在则删除。

See: DOM macro warning.
查看:DOM 宏警告。

History:  历史:

Arguments:  参数:

Examples:  示例:

<<toggleclass "body" "day rain">>  → Toggle the classes "day" and "rain" on the <body> element
<<toggleclass "#pie" "cherry">>    → Toggle the class "cherry" on the element with the ID "pie"
<<toggleclass ".joe" "angry">>     → Toggle the class "angry" on all elements containing the class "joe"

 Audio Macros   音频宏

Warning: The audio subsystem that supports the audio macros comes with some built-in limitations and it is strongly recommended that you familiarize yourself with them.
注意:支持音频宏的音频子系统自带一些内置限制,强烈建议您熟悉这些限制。

 <<audio trackIdList actionList>>

Controls the playback of audio tracks, which must be set up via <<cacheaudio>>.
控制音频轨道的播放,必须通过 <<cacheaudio>> 设置。

See: Audio macro limitations.
注意:音频宏的限制。

Note: The <<audio>> macro cannot affect playlist tracks whose ownership has been transferred to their respective playlist. Meaning those set up via <<createplaylist>> with its own action, as owned playlist tracks are solely under the control of their playlist.
备注: <<audio>> 宏无法影响已将其所有权转让给各自播放列表的播放列表曲目。这意味着通过 <<createplaylist>> 及其 own 动作设置的,因为拥有播放列表曲目完全受其播放列表的控制。

Note: The Config.audio.pauseOnFadeToZero setting (default: true) controls whether tracks that have been faded to 0 volume (silent) are automatically paused.
注意: Config.audio.pauseOnFadeToZero 设置(默认: true )控制是否自动暂停已淡出到 0 音量(静音)的音轨。

History:  历史:

Arguments:  参数:

Group IDs:  组 ID:

Group IDs allow several tracks to be selected simultaneously without needing to specify each one individually. There are several predefined group IDs (:all, :looped, :muted, :paused, :playing, :stopped) and custom IDs may be defined via <<createaudiogroup>>. The :not() group modifier syntax (groupId:not(trackIdList)) allows a group to have some of its tracks excluded from selection.
组 ID 允许同时选择多个轨道,而无需逐个指定。存在多个预定义的组 ID( :all:looped:muted:paused:playing:stopped )以及可以通过 <<createaudiogroup>> 定义自定义 ID。 :not() 组修饰符语法( groupId:not(trackIdList) )允许组中的一些轨道被排除在选择之外。

Examples:  示例:

Basic usage with group IDs
使用组 ID 的基本用法
→ Start playback of paused tracks
<<audio ":paused" play>>

→ Pause playback of playing tracks <<audio ":playing" pause>>

→ Stop playback of playing tracks <<audio ":playing" stop>>

→ Stop playback of all tracks <<audio ":all" stop>>

→ Stop playback of playing tracks except those in the ":ui" group <<audio ":playing:not(:ui)" stop>>

→ Change the volume of all tracks except those in the ":ui" group → to 40%, without changing the current playback state <<audio ":all:not(:ui)" volume 0.40>>
Basic usage with track IDs
基本用法与跟踪 ID
→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "bgm_space" "media/audio/space_quest.mp3" "media/audio/space_quest.ogg">>

→ Start playback <<audio "bgm_space" play>>

→ Start playback at 50% volume <<audio "bgm_space" volume 0.5 play>>

→ Start playback at 120 seconds in <<audio "bgm_space" time 120 play>>

→ Start repeating playback <<audio "bgm_space" loop play>>

→ Start playback and fade from 0% to 100% volume <<audio "bgm_space" volume 0 fadein>>

→ Start playback and fade from 75% to 0% volume <<audio "bgm_space" volume 0.75 fadeout>>

→ Start playback and fade from 25% to 75% volume <<audio "bgm_space" volume 0.25 fadeto 0.75>>

→ Start playback and fade from 25% to 75% volume over 30 seconds <<audio "bgm_space" volume 0.25 fadeoverto 30 0.75>>

→ Start playback and goto the "Peace Moon" passage upon ending normally <<audio "bgm_space" play goto "Peace Moon">>

→ Pause playback <<audio "bgm_space" pause>>

→ Stop playback <<audio "bgm_space" stop>>

→ Mute playback, without changing the current playback state <<audio "bgm_space" mute>>

→ Unmute playback, without changing the current playback state <<audio "bgm_space" unmute>>

→ Change the volume to 40%, without changing the current playback state <<audio "bgm_space" volume 0.40>>

→ Seek to 90 seconds in, without changing the current playback state <<audio "bgm_space" time 90>>
Using the load and unload actions
使用 loadunload 动作

Warning: Be very careful with these if your audio sources are on the network, as you are forcing players to begin downloading them. Not everyone has blazing fast internet with unlimited data—especially true for mobile users. Pease, do not take your players' bandwidth and data usage lightly.
警告:如果您的音频源在网络上,请务必小心使用,因为您正在强制玩家开始下载它们。并非所有人都有无限数据的超快互联网——尤其是移动用户。请勿轻视玩家的带宽和数据使用。

→ If it's not currently loading, drop existing data buffers and load the track
<<audio "bgm_space" load>>

→ Unload the track, dropping existing data buffers <<audio "bgm_space" unload>>

 <<cacheaudio trackId sourceList>>

Caches an audio track for use by the other audio macros.
缓存音频轨道以供其他音频宏使用。

Note: The StoryInit special passage is normally the best place to set up tracks.
备注: StoryInit 特殊段落通常是设置轨道的最佳位置。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Cache a track with the ID "boom" and one source via relative URL
<<cacheaudio "boom" "media/audio/explosion.mp3">>

→ Cache a track with the ID "boom" and one source via audio passage <<cacheaudio "boom" "explosion">>

→ Cache a track with the ID "bgm_space" and two sources via relative URLs <<cacheaudio "bgm_space" "media/audio/space_quest.mp3" "media/audio/space_quest.ogg">>

→ Cache a track with the ID "what" and one source via URL with a format specifier <<cacheaudio "what" "mp3|http://an-audio-service.com/a-user/a-track-id">>

 <<createaudiogroup groupId>>
[<<track trackId>> …]
<</createaudiogroup>>

Collects tracks, which must be set up via <<cacheaudio>>, into a group via its <<track>> children. Groups are useful for applying actions to multiple tracks simultaneously and/or excluding the included tracks from a larger set when applying actions.
收集必须通过 <<cacheaudio>> 设置的轨道,并将其通过其 <<track>> 子项分组。组可用于同时应用操作到多个轨道,或者当应用操作时排除包含的轨道。

Note: The StoryInit special passage is normally the best place to set up groups.
注意: StoryInit 特殊段落通常是设置组的最理想位置。

History:  历史:

Arguments:  参数:

<<createaudiogroup>>
<<track>>

Examples:  示例:

→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "ui_beep"  "media/audio/ui/beep.mp3">>
<<cacheaudio "ui_boop"  "media/audio/ui/boop.mp3">>
<<cacheaudio "ui_swish" "media/audio/ui/swish.mp3">>

→ Set up a group ":ui" with the tracks: "ui_beep", "ui_boop", and "ui_swish" <<createaudiogroup ":ui">> <<track "ui_beep">> <<track "ui_boop">> <<track "ui_swish">> <</createaudiogroup>>

 <<createplaylist listId>>
[<<track trackId actionList>> …]
<</createplaylist>>

Collects tracks, which must be set up via <<cacheaudio>>, into a playlist via its <<track>> children.
收集必须通过 <<cacheaudio>> 设置,并通过其 <<track>> 子项创建播放列表的轨道。

Note: The StoryInit special passage is normally the best place to set up playlists.
注意: StoryInit 特殊段落通常是设置播放列表的最佳位置。

History:  历史:

Arguments:  参数:

<<createplaylist>>
<<track>>

Examples:  示例:

→ Given the following setup (best done in the StoryInit special passage)
<<cacheaudio "swamped"       "media/audio/Swamped.mp3">>
<<cacheaudio "heavens_a_lie" "media/audio/Heaven's_A_Lie.mp3">>
<<cacheaudio "closer"        "media/audio/Closer.mp3">>
<<cacheaudio "to_the_edge"   "media/audio/To_The_Edge.mp3">>

→ Create a playlist "bgm_lacuna" with the tracks: "swamped", "heavens_a_lie", "closer", and "to_the_edge" <<createplaylist "bgm_lacuna">> <<track "swamped" volume 1>> → Add "swamped" at 100% volume <<track "heavens_a_lie" volume 0.5>> → Add "heavens_a_lie" at 50% volume <<track "closer" own>> → Add an owned copy of "closer" at its current volume <<track "to_the_edge" volume 1 own>> → Add an owned copy of "to_the_edge" at 100% volume <</createplaylist>>

 <<masteraudio actionList>>

Controls the master audio settings.
控制主音频设置。

See: Audio macro limitations.
注意:音频宏的限制。

History:  历史:

Arguments:  参数:

Examples:  示例:

Basic usage  基本用法
→ Stop playback of all registered tracks, no exceptions
<<masteraudio stop>>

→ Change the master volume to 40% <<masteraudio volume 0.40>>

→ Mute the master volume <<masteraudio mute>>

→ Unmute the master volume <<masteraudio unmute>>

→ Enable automatic muting of the master volume when losing visibility <<masteraudio muteonhide>>

→ Disable automatic muting of the master volume when losing visibility <<masteraudio nomuteonhide>>
Using the load and unload actions
使用 loadunload 动作

Warning: Be very careful with these if your audio sources are on the network, as you are forcing players to begin downloading them. Not everyone has blazing fast internet with unlimited data—especially true for mobile users. Pease, do not take your players' bandwidth and data usage lightly.
警告:如果您的音频源在网络上,请务必小心使用,因为您正在强制玩家开始下载它们。并非所有人都有无限数据的超快互联网——尤其是移动用户。请勿轻视玩家的带宽和数据使用。

→ If they're not currently loading, drop existing data buffers and load all tracks
<<masteraudio load>>

→ Unload all tracks, dropping existing data buffers <<masteraudio unload>>

 <<playlist listId actionList>>

Controls the playback of the playlist, which must be set up via <<createplaylist>>.
控制播放列表的播放,必须通过 <<createplaylist>> 设置。

See: Audio macro limitations.
注意:音频宏的限制。

Note: The Config.audio.pauseOnFadeToZero setting (default: true) controls whether tracks that have been faded to 0 volume (silent) are automatically paused.
注意: Config.audio.pauseOnFadeToZero 设置(默认: true )控制是否自动暂停已淡出到 0 音量(静音)的音轨。

History:  历史:

Arguments:  参数:

<<createplaylist>>-compatible form   <<createplaylist>> - 兼容形式
<<setplaylist>>-compatible form   <<setplaylist>> -兼容格式

Examples: (only <<createplaylist>>-compatible form shown)
示例:(仅显示 <<createplaylist>> 兼容形式)

Basic usage  基本用法
→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "swamped"       "media/audio/Swamped.mp3">>
<<cacheaudio "heavens_a_lie" "media/audio/Heaven's_A_Lie.mp3">>
<<cacheaudio "closer"        "media/audio/Closer.mp3">>
<<cacheaudio "to_the_edge"   "media/audio/To_The_Edge.mp3">>
<<createplaylist "bgm_lacuna">>
	<<track "swamped"       volume 1>>
	<<track "heavens_a_lie" volume 1>>
	<<track "closer"        volume 1>>
	<<track "to_the_edge"   volume 1>>
<</createplaylist>>

→ Start playback
<<playlist "bgm_lacuna" play>>

→ Start playback at 50% volume
<<playlist "bgm_lacuna" volume 0.5 play>>

→ Start non-repeating playback
<<playlist "bgm_lacuna" unloop play>>

→ Start playback with a randomly shuffled playlist
<<playlist "bgm_lacuna" shuffle play>>

→ Start playback and fade from 0% to 100% volume
<<playlist "bgm_lacuna" volume 0 fadein>>

→ Start playback and fade from 75% to 0% volume
<<playlist "bgm_lacuna" volume 0.75 fadeout>>

→ Start playback and fade from 25% to 75% volume
<<playlist "bgm_lacuna" volume 0.25 fadeto 0.75>>

→ Start playback and fade from 25% to 75% volume over 30 seconds
<<playlist "bgm_lacuna" volume 0.25 fadeoverto 30 0.75>>

→ Pause playback
<<playlist "bgm_lacuna" pause>>

→ Stop playback
<<playlist "bgm_lacuna" stop>>

→ Mute playback, without changing the current playback state
<<playlist "bgm_lacuna" mute>>

→ Unmute playback, without changing the current playback state
<<playlist "bgm_lacuna" unmute>>

→ Change the volume to 40%, without changing the current playback state
<<playlist "bgm_lacuna" volume 0.40>>

→ Set the playlist to randomly shuffle, without changing the current playback state
<<playlist "bgm_lacuna" shuffle>>
Using the load and unload actions
使用 loadunload 动作

Warning: Be very careful with these if your audio sources are on the network, as you are forcing players to begin downloading them. Not everyone has blazing fast internet with unlimited data—especially true for mobile users. Pease, do not take your players' bandwidth and data usage lightly.
警告:如果您的音频源在网络上,请务必小心使用,因为您正在强制玩家开始下载它们。并非所有人都有无限数据的超快互联网——尤其是移动用户。请勿轻视玩家的带宽和数据使用。

→ If they're not currently loading, drop existing data buffers and load all of the playlist's tracks
<<playlist "bgm_lacuna" load>>

→ Unload all of the playlist's tracks, dropping existing data buffers <<playlist "bgm_lacuna" unload>>

 <<removeaudiogroup groupId>>

Removes the audio group with the given ID.
移除具有给定 ID 的音频组。

Note: You may not remove the predefined group IDs (:all, :looped, :muted, :paused, :playing, :stopped) or the :not group modifier.
注意:您不能移除预定义的组 ID( :all:looped:muted:paused:playing:stopped )或 :not 组修饰符。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Given a group set up via <<createaudiogroup ":ui">>…<</createplaylist>>
<<removeaudiogroup ":ui">>

 <<removeplaylist listId>>

Removes the playlist with the given ID.
移除具有给定 ID 的播放列表。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ Given a playlist set up via <<createplaylist "bgm_lacuna">>…<</createplaylist>>
<<removeplaylist "bgm_lacuna">>

 <<waitforaudio>>

Displays the loading screen until all currently registered audio has either loaded to a playable state or aborted loading due to errors. Requires tracks to be set up via <<cacheaudio>>.
显示加载界面,直到所有已注册的音频加载到可播放状态或由于错误而加载失败。需要通过 <<cacheaudio>> 设置音轨。

Note: This macro should be invoked once following any invocations of <<cacheaudio>> and <<createplaylist>>, if any <<track>> definitions used the copy keyword, for which you want the loading screen displayed.
注意:如果使用了 copy 关键字,并且需要显示加载界面,则应在调用 <<cacheaudio>><<createplaylist>> 之后(如果有的话)调用此宏一次。

History:  历史:

Arguments: none  无参数

Examples:  示例:

Basic usage  基本用法
<<cacheaudio "a" "a_track.…">>
<<cacheaudio "b" "b_track.…">>
<<cacheaudio "c" "c_track.…">>
<<cacheaudio "d" "d_track.…">>
<<waitforaudio>>
Load only selected audio at startup
仅加载启动时选定的音频
→ First, register the tracks that will be needed soon
<<cacheaudio "a" "a_track.…">>
<<cacheaudio "b" "b_track.…">>

→ Next, load all currently registered tracks (meaning: "a" and "b") <<waitforaudio>>

→ Finally, register any tracks that won't be needed until later <<cacheaudio "c" "c_track.…">> <<cacheaudio "d" "d_track.…">>

 Miscellaneous Macros   杂项宏

 <<done>> … <</done>>

Silently executes its contents when the incoming passage is done rendering and has been added to the page. Generally, only really useful for running code that needs to manipulate elements from the incoming passage, since you must wait until they've been added to the page.
静默执行其内容,当传入的段落渲染完成并被添加到页面时。通常,仅对需要操作传入段落元素的代码真正有用,因为必须等待它们被添加到页面。

Tip: If you need to run the same code on multiple passages, consider using the PassageDone special passage or, for a JavaScript/TwineScript solution, a :passagedisplay event instead. They serve the same basic purpose as the <<done>> macro, but are run each time passage navigation occurs.
提示:如果您需要在多个段落上运行相同的代码,请考虑使用 PassageDone 特殊段落,或者对于 JavaScript/TwineScript 解决方案,使用 :passagedisplay 事件。它们与 <<done>> 宏具有相同的基本功能,但在每次段落导航时都会运行。

History:  历史:

Arguments: none  无参数

Examples:  示例:

@@#spy;@@

<<done>> <<replace "#spy">>I spy with my little eye, a crab rangoon.<</replace>> <</done>>

 <<goto passageName>>
<<goto linkMarkup>>

Immediately forwards the player to the passage with the given name. May be called either with the passage name or with a link markup.
立即将玩家转发到具有给定名称的段落。可以使用段落名称或链接标记来调用。

Note: In most cases, you will not need to use <<goto>> as there are often better and easier ways to forward the player. For example, a common use of <<link>> is to perform various actions before forwarding the player to another passage. In that case, unless you need to dynamically determine the destination passage within the <<link>> body, <<goto>> is unnecessary as <<link>> already includes the ability to forward the player.
注意:在大多数情况下,您不需要使用 <<goto>> ,因为通常有更好、更简单的方法来转发玩家。例如, <<link>> 的一个常见用途是在将玩家转发到另一个段落之前执行各种操作。在这种情况下,除非您需要在 <<link>> 体中动态确定目标段落,否则 <<goto>> 是不必要的,因为 <<link>> 已经包括了转发玩家的能力。

Warning: Using <<goto>> to automatically forward players from one passage to another with no input from them will both create junk moments within the story history and make it extremely difficult for players to navigate the history. It is strongly recommended that you look into other methods to achieve your goals instead—e.g., Config.navigation.override.
警告:使用 <<goto>> 自动将玩家从一条段落转发到另一条段落而无需他们输入,这将在故事历史中创建垃圾时刻,并使玩家难以导航历史。强烈建议您考虑其他方法来实现您的目标——例如, Config.navigation.override

Warning: <<goto>> does not terminate passage rendering in the passage where it was encountered, so care must be taken to ensure that no unwanted state modifications occur after its call.
警告: <<goto>> 在遇到它的段落中未终止段落渲染,因此在使用后必须小心,以确保不会发生不希望的状态更改。

History:  历史:

Arguments:  参数:

Passage name form  段落名称表单
Link markup form  链接标记形式

Examples:  示例:

→ Passage name form
<<goto "Somewhere over yonder">>
<<goto $selectedPassage>>

→ Link markup form <<goto [[Somewhere over yonder]]>> <<goto [[$selectedPassage]]>>

 <<repeat delay [transition|t8n]>> … <</repeat>>

Repeatedly executes its contents after the given delay, inserting any output into the passage in its place. May be terminated by a <<stop>> macro.
在给定延迟后重复执行其内容,并将任何输出插入到其位置。可以通过 <<stop>> 宏终止。

Note: Passage navigation terminates all pending timed executions.
注意:段落导航将终止所有挂起的定时执行。

History:  历史:

Arguments:  参数:

Examples:  示例:

→ A countdown timer
<<set $seconds to 10>>\
Countdown: <span id="countdown">$seconds seconds remaining</span>!\
<<silent>>
	<<repeat 1s>>
		<<set $seconds to $seconds - 1>>
		<<if $seconds gt 0>>
			<<replace "#countdown">>$seconds seconds remaining<</replace>>
		<<else>>
			<<replace "#countdown">>Too Late<</replace>>
			/* do something useful here */
			<<stop>>
		<</if>>
	<</repeat>>
<</silent>>

 <<stop>>

Used within <<repeat>> macros. Terminates the execution of the current <<repeat>>.
<<repeat>> 宏中使用。终止当前 <<repeat>> 的执行。

History:  历史:

Arguments: none  无参数

 <<timed delay [transition|t8n]>> …
[<<next [delay]>> …]
<</timed>>

Executes its contents after the given delay, inserting any output into the passage in its place. Additional timed executions may be chained via <<next>>.
在给定延迟后执行其内容,将任何输出插入到段落中。可以通过 <<next>> . 连接多个定时执行。

Note: Passage navigation terminates all pending timed executions.
注意:段落导航将终止所有挂起的定时执行。

History:  历史:

Arguments:  参数:

<<timed>>
<<next>>

Examples:  示例:

→ Insert some text after 5 seconds with a transition
I want to go to…<<timed 5s t8n>> WONDERLAND!<</timed>>

→ Replace some text after 10 seconds I like green <span id="eggs">eggs</span> and ham!\ <<timed 10s>><<replace "#eggs">>pancakes<</replace>><</timed>>

→ A execute <<goto>> after 10 seconds <<timed 10s>><<goto "To the Moon, Alice">><</timed>>

→ Insert some text in 2 second intervals three times (at: 2s, 4s, 6s) <<timed 2s>>Hi! Ho! <<next>>Hi! Ho! <<next>>It's off to work we go! <</timed>>

→ Set a $variable after 4 seconds, 3 seconds, 2 seconds, and 1 second <<silent>> <<set $choice to 0>> <<timed 4s>> <<set $choice to 1>> <<next 3s>> <<set $choice to 2>> <<next 2s>> <<set $choice to 3>> <<next 1s>> <<set $choice to 4>> <</timed>> <</silent>>

→ Replace some text with a variable interval → Given: _delay is "2s" the interval will be 2 seconds I'll have <span id="drink">some water</span>, please.\ <<timed _delay>><<replace "#drink">>a glass of milk<</replace>>\ <<next>><<replace "#drink">>a can of soda<</replace>>\ <<next>><<replace "#drink">>a cup of coffee<</replace>>\ <<next>><<replace "#drink">>tea, southern style, sweet<</replace>>\ <<next>><<replace "#drink">>a scotch, neat<</replace>>\ <<next>><<replace "#drink">>a bottle of your finest absinthe<</replace>>\ <</timed>>

 <<widget widgetName [container]>> … <</widget>>

Creates a new widget macro (henceforth, widget) with the given name. Widgets allow you to create macros by using the standard macros and markup that you use normally within your story. All widgets may access arguments passed to them via the _args special variable. Block widgets may access the contents they enclose via the _contents special variable.
创建一个新的具有给定名称的小部件宏(以下简称小部件)。小部件允许您通过使用您通常在故事中使用的标准宏和标记来创建宏。所有小部件都可以通过 _args 特殊变量访问传递给它们的参数。块小部件可以通过 _contents 特殊变量访问它们包含的内容。

Warning: Widgets should always be defined within a widget-tagged passage—any widgets that are not may be lost on page reload—and you may use as few or as many such passages as you desire. Do not add a widget tag to any of the specially named passages and attempt to define your widgets there.
警告:小部件应始终在标记为 widget 的段落内定义——否则在页面重新加载时可能会丢失——并且您可以按需使用尽可能少或尽可能多的此类段落。不要在任何特别命名的段落中添加 widget 标签并尝试在那里定义小部件。

Warning: The array-like object stored in the _args variable should be treated as though it were immutable—i.e., unable to be modified—because in the future it will be made thus, so any attempt to modify it will cause an error.
警告:存储在 _args 变量中的类似数组的对象应被视为不可变——即无法修改——因为将来它将被这样处理,所以任何修改它的尝试都将导致错误。

History:  历史:

Arguments:  参数:

Special variables, _args & _contents:
特殊变量, _args & _contents

The _args special variable is used internally to store arguments passed to the widget—as zero-based indices; i.e., _args[0] is the first parsed argument, _args[1] is the second, etc—the full argument string in raw and parsed forms—accessed via the _args.raw and _args.full properties—and the widgets' name via the _args.name property.
_args 特殊变量用于内部存储传递给部件的参数——即零基于索引;即 _args[0] 是第一个解析参数, _args[1] 是第二个,等等——完整参数字符串的原始和解析形式——通过 _args.raw_args.full 属性访问——以及部件的名称通过 _args.name 属性访问。

The _contents special variable is used internally, by container widgets, to store the contents they enclose.
_contents 特殊变量由容器小部件内部使用,用于存储它们包含的内容。

When a widget is called, any existing _args variable, and for container widgets _contents, is stored for the duration of the call and restored after. This means that non-widget uses of these special variable are completely safe, though this does have the effect that uses external to widgets are inaccessible within them unless passed in as arguments.
当一个组件被调用时,任何现有的 _args 变量,以及对于容器组件 _contents ,都会在调用期间被存储并在调用后恢复。这意味着这些特殊变量的非组件使用是完全安全的,但这确实有这样一个效果,即除非作为参数传递,否则外部组件的使用在组件内部是不可访问的。

Warning:

When calling one container widget directly from within another container widget, the _contents special variable of the outer widget must not be included within the body of the call of the inner widget. Doing so will cause uncontrolled recursion. E.g.,


警告:当直接从另一个容器小部件中调用一个容器小部件时,外部小部件的 _contents 特殊变量不得包含在内部小部件调用体中。这样做将导致不受控制的递归。例如:
<<widget "inner" container>>
_contents
<</widget>>
<<widget "outer" container>> <<inner>>_contents<</inner>> <</widget>>
<<outer>>ford<</outer>>

Warning: Unless localized by use of the <<capture>> macro, any story or other temporary variables used within widgets are part of a story's normal variable store, so care must be taken not to accidentally either overwrite or pick up an existing value.
警告:除非使用 <<capture>> 宏进行本地化,否则在部件中使用的任何故事或其他临时变量都是故事正常变量存储的一部分,因此必须小心,以免意外覆盖或获取现有值。

Examples:  示例:

Note: No line-break control mechanisms are used in the following examples for readability. In practice, you'll probably want to use either line continuations or one of the no-break methods: Config.passages.nobr setting, nobr special tag, <<nobr>> macro.
注意:以下示例中未使用换行控制机制以提高可读性。在实际应用中,您可能希望使用行续或以下无换行方法之一: Config.passages.nobr 设置, nobr 特殊标签, <<nobr>> 宏。

Basic usage (non-container)
基本用法(非容器)
→ Creating a gender pronoun widget
<<widget "he">>
	<<if $pcSex eq "male">>
		he
	<<elseif $pcSex eq "female">>
		she
	<<else>>
		it
	<</if>>
<</widget>>

→ Using it "Are you sure that <<he>> can be trusted?"
→ Creating a silly print widget
<<widget "pm">>
	<<if _args[0]>>
		<<print _args[0]>>
	<<else>>
		Mum's the word!
	<</if>>
<</widget>>

→ Using it <<pm>> → Outputs: Mum's the word! <<pm "Hi!">> → Outputs: Hi!
Basic usage (container)  基本用法(容器)
→ Creating a simple dialog box widget
<<widget "say" container>>
	<div class="say-box">
		<img class="say-image" @src="'images/' + _args[0].toLowerCase() + '.png'">
		<p class="say-text">_contents</p>
	</div>
<</widget>>

→ Using it <<say "Chapel">>Tweego is a pathway to many abilities some consider to be… unnatural.<</say>>

 Functions   函数

 clone(original)any

Returns a deep copy of the given value.
返回给定值的深拷贝。

Only primitives, generic objects, Array, Date, Map, RegExp, and Set are supported by default. Unsupported object types, either native or custom, will need to implement a .clone() method to be properly supported by the clone() function—when called on such an object, it will defer to the local method; see the Non-generic object types (classes) guide for more information.
仅支持原语、通用对象、 ArrayDateMapRegExpSet 。不支持的对象类型(无论是原生还是自定义的)需要实现一个 .clone() 方法,以便由 clone() 函数正确支持——当调用此类对象时,它将委托给本地方法;有关更多信息,请参阅非通用对象类型(类)指南。

Warning: Referential relationships between objects are not maintained—i.e., after cloning multiple references to an object will refer to seperate yet equivalent objects, as each reference receives its own clone of the original.
警告:对象之间的引用关系不被维护——即,在克隆多个对象引用后,每个引用将指向单独但等效的对象,因为每个引用都接收了原始对象的克隆副本。

Warning: Generic objects have only their own enumerable properties copied. Non-enumerable properties and property descriptors are not duplicated. In particular, this means that getters/setters are not properly duplicated. If you need getters/setters, then you'll need to use a non-generic object/class.
警告:通用对象仅复制其自身的可枚举属性。不可枚举属性和属性描述符不会被复制。特别是,这意味着获取器/设置器不会被正确复制。如果您需要获取器/设置器,那么您需要使用非通用对象/类。

History:  历史:

Parameters:  参数:

Returns:  返回值:

A deep copy (any) of the original value.
原值的深度副本( any )。

Throws: none  无抛出异常

Examples:  示例:

// Without clone(); given the generic object: $foo = { id : 1 }
<<set $bar to $foo>>
<<set $bar.id to 5>>
$foo.id  → Returns: 5
$bar.id  → Returns: 5

// With clone(); given the generic object: $foo = { id : 1 } <<set $bar to clone($foo)>> <<set $bar.id to 5>> $foo.id → Returns: 1 $bar.id → Returns: 5

 either(list…)any

Returns a random value from its given arguments.
返回其给定参数的随机值。

History:  历史:

Parameters:  参数:

Returns:  返回:

A random value from its given arguments.
从其给定的参数中返回一个随机值。

Throws: none  不抛出异常

Examples:  示例:

// Using singular values
either("Blueberry", "Cherry", "Pecan")  → Returns a random pie from the whole list

// Using arrays; given: $pies = ["Blueberry", "Cherry", "Pecan"] either($pies) → Returns a random pie from the whole array

// Using singular values and arrays; given: $letters = ["A", "B"] either($letters, "C", "D") → Returns a random value from the whole list—i.e., "A", "B", "C", "D"

// Using multiple arrays; given: $letters = ["A", "B"] & $numerals = ["1", "2"] either($letters, $numerals) → Returns a random value from the whole list—i.e., "A", "B", "1", "2"

 forget(key)

Removes the specified key, and its associated value, from the story metadata store.
从故事元数据存储中删除指定的键及其关联的值。

See Also: memorize(), recall().
参见: memorize()recall()

History:  历史:

Parameters:  参数:

Returns: none  返回值:无

Throws:  抛出异常:

An Error or TypeError instance.
一个 ErrorTypeError 实例。

Examples:  示例:

<<run forget('achievements')>>

 hasVisited(passages…)boolean

Returns whether the passage with the given name occurred within the story history. If multiple passage names are given, returns the logical-AND aggregate of the set—i.e., true if all were found, false if any were not found.
返回给定名称的段落是否在故事历史中出现过。如果给出了多个段落名称,则返回该集合的逻辑-AND 聚合——即,如果所有名称都找到,则返回 true ,如果任何名称未找到,则返回 false

History:  历史:

Parameters:  参数:

Returns:  返回:

Boolean true if all were found, elsewise false.
布尔值 true 如果全部找到,否则 false

Throws:  抛出:

An Error instance.  一个 Error 实例。

Examples:  示例:

<<if hasVisited("Bar")>>…has been to the Bar…<</if>>
<<if not hasVisited("Bar")>>…has never been to the Bar…<</if>>
<<if hasVisited("Bar", "Café")>>…has been to both the Bar and Café<</if>>
<<if not hasVisited("Bar", "Café")>>…has never been to either the Bar, Café, or both…<</if>>

 lastVisited(passages…)integer

Returns the number of turns that have passed since the last instance of the passage with the given name occurred within the story history or -1 if it does not exist. If multiple passage names are given, returns the lowest count (which can be -1).
返回自上次在故事历史中发生给定名称的段落实例以来经过的回合数,如果不存在则返回 -1 。如果给出了多个段落名称,则返回最低计数(可以是 -1 )。

History:  历史:

Parameters:  参数:

Returns:  返回:

The lowest count (integer), elsewise -1.
最小计数( integer ),否则 -1

Throws:  抛出:

An Error instance.  一个 Error 实例。

Examples:  示例:

<<if lastVisited("Bar") is -1>>…has never been to the Bar…<</if>>
<<if lastVisited("Bar") is 0>>…is currently in the Bar…<</if>>
<<if lastVisited("Bar") is 1>>…was in the Bar one turn ago…<</if>>
<<if lastVisited("Bar", "Café") is -1>>…has never been to the Bar, Café, or both…<</if>>
<<if lastVisited("Bar", "Café") is 2>>…has been to both the Bar and Café, most recently two turns ago…<</if>>

 importScripts(urls…)Promise

Load and integrate external JavaScript scripts.
加载并集成外部 JavaScript 脚本。

Note: Loading is done asynchronously at run time, so if the script must be available within a tight time frame, then you should use the Promise returned by the function to ensure that the script is loaded before it is needed.
注意:加载是在运行时异步进行的,因此如果脚本必须在很紧的时间内可用,那么您应该使用函数返回的 Promise 来确保在需要之前脚本已加载。

Note: Your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) is normally the best place to call importScripts().
注意:您的项目中的 JavaScript 部分(Twine 2:故事 JavaScript;Twine 1/Twee:带有 script -标签的段落)通常是调用 importScripts() 的最佳位置。

History:  历史:

Parameters:  参数:

Note: URLs ending in .mjs are imported as modules.
注意:以 .mjs 结尾的 URL 作为模块导入。

Returns:  返回:

A Promise that simply resolves, or rejects with an error if the script could not be loaded.
一个简单的解析器,如果脚本无法加载,则拒绝并报错。

Throws:  抛出异常:

An Error instance.  一个 Error 实例。

Examples:  示例:

Basic usage  基本用法
// Import scripts a.js as normal, b.mjs as a module, and c.js as a
// module
importScripts(
	"https://somesite/a/path/a.js",
	"https://somesite/a/path/b.mjs",
	{
		type : "module",
		src  : "https://somesite/a/path/c.js"
	}
);

// Import all scripts concurrently importScripts( "https://somesite/a/path/a.js", "https://somesite/a/path/b.js", "https://somesite/a/path/c.js", "https://somesite/a/path/d.js" );

// Import all scripts sequentially importScripts([ "https://somesite/a/path/a.js", "https://somesite/a/path/b.js", "https://somesite/a/path/c.js", "https://somesite/a/path/d.js" ]);

// Import scripts a.js, b.js, and the c.js/d.js group concurrently, // while importing c.js and d.js sequentially relative to each other importScripts( "https://somesite/a/path/a.js", "https://somesite/a/path/b.js", [ "https://somesite/a/path/c.js", "https://somesite/a/path/d.js" ] );
Basic usage with the returned Promise object
基本用法与返回的 Promise 对象
// Import a script while using the returned Promise to ensure that
// the script has been fully loaded before executing dependent code
importScripts("https://somesite/a/path/a.js")
	.then(function () {
		// Code that depends on the script goes here
	})
	.catch(function (err) {
		// There was an error loading the script, log it to the console
		console.log(err);
	});
Saving the returned Promise object for later use
保存返回的 Promise 对象以供以后使用
// Import a script while saving the returned Promise so it may be used later
setup.aScriptImport = importScripts("https://somesite/a/path/aScript.js");

// Use the returned Promise later on to ensure that the script has been fully // loaded before executing dependent code setup.aScriptImport .then(function () { // Code that depends on the script goes here }) .catch(function (err) { // There was an error loading the script, log it to the console console.log(err); });

 importStyles(urls…)Promise

Load and integrate external CSS stylesheets.
加载并集成外部 CSS 样式表。

Note: Loading is done asynchronously at run time, so if the stylesheet must be available within a tight time frame, then you should use the Promise returned by the function to ensure that the stylesheet is loaded before it is needed.
注意:加载是在运行时异步进行的,因此如果样式表必须在很紧的时间内可用,那么您应该使用函数返回的 Promise 来确保在需要之前加载样式表。

Note: Your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) is normally the best place to call importStyles().
注意:您的项目中的 JavaScript 部分(Twine 2:故事 JavaScript;Twine 1/Twee:带有 script -标签的段落)通常是调用 importStyles() 的最佳位置。

History:  历史:

Parameters:  参数:

Returns:  返回:

A Promise that simply resolves, or rejects with an error if the style could not be loaded.
一个简单的解析器,如果样式无法加载,则拒绝并返回错误。

Throws:  抛出异常:

An Error instance.  一个 Error 实例。

Examples:  示例:

Basic usage  基本用法
// Import all stylesheets concurrently
importStyles(
	"https://somesite/a/path/a.css",
	"https://somesite/a/path/b.css",
	"https://somesite/a/path/c.css",
	"https://somesite/a/path/d.css"
);

// Import all stylesheets sequentially importStyles([ "https://somesite/a/path/a.css", "https://somesite/a/path/b.css", "https://somesite/a/path/c.css", "https://somesite/a/path/d.css" ]);

// Import stylesheets a.css, b.css, and the c.css/d.css group concurrently, // while importing c.css and d.css sequentially relative to each other importStyles( "https://somesite/a/path/a.css", "https://somesite/a/path/b.css", [ "https://somesite/a/path/c.css", "https://somesite/a/path/d.css" ] );
Basic usage with the returned Promise object
基本用法与返回的 Promise 对象
// Grab a loading screen lock
var lsLockId = LoadScreen.lock();

// Import a stylesheet while using the returned Promise to ensure that the // stylesheet has been fully loaded before unlocking the loading screen importStyles("https://somesite/a/path/a.css") .then(function () { // The stylesheet has been loaded, release the loading screen lock LoadScreen.unlock(lsLockId); }) .catch(function (err) { // There was an error loading the stylesheet, log it to the console console.log(err); });

 memorize(key, value)

Sets the specified key and value within the story metadata store, which causes them to persist over story and browser restarts. To update the value associated with a key, simply set it again.
设置故事元数据存储中的指定键和值,使它们在故事和浏览器重启后持续存在。要更新与键关联的值,只需再次设置它即可。

Note: The story metadata, like saves, is tied to the specific story it was generated with. It is not a mechanism for moving data between stories.
注意:故事元数据,如存档,与特定生成的故事相关联。它不是在故事之间移动数据的一种机制。

Warning: The story metadata store is not, and should not be used as, a replacement for saves. Examples of good uses: achievement tracking, new game+ data, playthrough statistics, etc.
警告:故事元数据存储不应,也不应作为保存的替代品使用。示例:成就跟踪、新游戏+数据、游玩统计等。

Warning: This feature is largely incompatible with private browsing modes, which cause all in-browser storage mechanisms to either persist only for the lifetime of the browsing session or fail outright.
警告:此功能与私密浏览模式大部分不兼容,可能导致所有浏览器存储机制只能持续浏览会话的生命周期,或者完全失败。

See Also: forget(), recall().
参见: forget()recall()

History:  历史:

Parameters:  参数:

Returns: none  返回值:无

Throws:  抛出异常:

An TypeError instance.  一个 TypeError 实例。

Examples:  示例:

// Sets 'achievements', with the given value, in the metadata store.
<<run memorize('achievements', { ateYellowSnow : true })>>

// Sets 'ngplus', with the given value, in the metadata store. <<run memorize('ngplus', true)>>

 passage()string

Returns the name of the active (present) passage.
返回活动(存在)的段落名称。

History:  历史:

Parameters: none  参数:无

Returns:  返回:

The name (string) of the passage.
过渡名称( string )。

Throws: none  无抛出异常

Examples:  示例:

<<if passage() is "Café">>…the active passage is the Café passage…<</if>>

 previous()string

Returns the name of the most recent previous passage whose name does not match that of the active passage or an empty string, if there is no such passage.
返回名称与当前过渡名称不匹配的最最近的前一个过渡的名称,如果没有这样的过渡则返回空字符串。

History:  历史:

Parameters: none  参数:无

Returns:  返回:

The name (string) of the passage, elsewise an empty string ('').
段落名称( string ),否则为空字符串( '' )。

Throws: none  无抛出异常

Examples:  示例:

<<if previous() is "Café">>…the most recent non-active passage is the Café passage…<</if>>

→ Commonly used as part of a link to return to the most recent non-active passage [[Return|previous()]]

 random([min ,] max)integer

Returns a pseudo-random whole number (integer) within the range of the given bounds (inclusive)—i.e., [min, max].
返回给定范围(包含)内的伪随机整数(整数)——即 [min, max]。

Note: By default, it uses Math.random() as its source of (non-deterministic) randomness, however, when the seedable PRNG has been enabled, via State.prng.init(), it uses that (deterministic) seeded PRNG instead.
注意:默认情况下,它使用 Math.random() 作为其(非确定性)随机性的来源,然而,当通过 State.prng.init() 启用了可播种的 PRNG 时,它将使用那个(确定性)播种的 PRNG 代替。

History:  历史:

Parameters:  参数:

Returns:  返回:

A random whole number (integer).
一个随机整数( integer )。

Throws:  抛出异常:

An Error or TypeError instance.
一个 ErrorTypeError 实例。

Examples:  示例:

random(5)     → Returns a number in the range 0–5
random(1, 6)  → Returns a number in the range 1–6

 randomFloat([min ,] max)decimal

Returns a pseudo-random decimal number (floating-point) within the range of the given bounds (inclusive for the minimum, exclusive for the maximum)—i.e., [min, max).
返回给定范围(最小值包含,最大值不包含)内的伪随机十进制数(浮点数)——即[min, max)。

Note: By default, it simply returns non-deterministic results from Math.random(), however, when the seedable PRNG has been enabled, via State.prng.init(), it returns deterministic results from the seeded PRNG instead.
注意:默认情况下,它仅从 Math.random() 返回非确定性结果,然而,当启用可播种的伪随机数生成器后,通过 State.prng.init() ,它将返回从播种的伪随机数生成器得到的确定性结果。

History:  历史:

Parameters:  参数:

Returns:  返回:

A random floating-point number (decimal).
一个随机的浮点数( decimal )。

Throws:  抛出异常:

An Error or TypeError instance.
一个 ErrorTypeError 实例。

Examples:  示例:

randomFloat(5.0)       → Returns a number in the range 0.0–4.9999999…
randomFloat(1.0, 6.0)  → Returns a number in the range 1.0–5.9999999…

 recall(key [, defaultValue])any

Returns the value associated with the specified key from the story metadata store or, if no such key exists, the specified default value, if any.
返回从故事元数据存储中与指定键关联的值,如果不存在这样的键,则返回指定的默认值(如果有的话)。

See Also: forget(), memorize().
参见: forget()memorize()

History:  历史:

Parameters:  参数:

Returns:  返回:

A value (any) from the specified key, elsewise the default value if specified.
来自指定键的值( any ),否则返回指定的默认值。

Throws:  抛出异常:

A TypeError instance.  一个 TypeError 实例。

Examples:  示例:

// Set setup.achievements to the 'achievements' metadata or an empty generic object.
<<set setup.achievements to recall('achievements', {})>>

// Set setup.ngplus to the 'ngplus' metadata, with no default. <<set setup.ngplus to recall('ngplus')>>

 setPageElement(idOrElement , passages [, defaultText])HTMLElement | null

Renders the selected passage into the target element, replacing any existing content, and returns the element. If no passages are found and default text is specified, it will be used instead.
将所选段落渲染到目标元素中,替换任何现有内容,并返回该元素。如果没有找到段落且指定了默认文本,则将使用该默认文本。

History:  历史:

Parameters:  参数:

Returns:  返回:

An HTMLElement, elsewise null.
一个 HTMLElement ,否则 null

Throws: none  无抛出异常

Examples:  示例:

Note: As it is highly unlikely that either an array of passage names or default text will be needed in the vast majority of cases, only a few basic examples will be given.
注意:由于在绝大多数情况下,不需要使用段落名称数组或默认文本,因此这里只给出几个基本示例。

// Using an ID; given an existing element on the page: <div id="my-display"></div>
setPageElement("my-display", "MyPassage");

// Using an element; given a reference to an existing element: myElement setPageElement(myElement, "MyPassage");

 tags([passages…])Array<string>

Returns a new array consisting of all of the tags of the given passages.
返回一个包含给定段落所有标签的新数组。

History:  历史:

Parameters:  参数:

Returns:  返回:

The tags (Array<string>).
标签( Array<string> )。

Throws: none  无抛出异常

Examples:  示例:

<<if tags().includes("forest")>>…the active passage is part of the forest…<</if>>
<<if tags("Lonely Glade").includes("forest")>>…the Lonely Glade passage is part of the forest…<</if>>

 temporary()object

Returns a reference to the current temporary variables store (equivalent to: State.temporary). This is only really useful within pure JavaScript code, as within TwineScript you may simply access temporary variables natively.
返回当前临时变量存储的引用(相当于: State.temporary )。这仅在纯 JavaScript 代码中非常有用,因为在 TwineScript 中,您可以直接访问临时变量。

History:  历史:

Parameters: none  参数:无

Returns:  返回:

A reference to the temporary variable store (object).
暂时变量存储的引用( object )。

Throws: none  无抛出异常

Examples:  示例:

// Given: _selection is 'Zagnut Bar'
if (temporary().selection === 'Zagnut Bar') {
	/* Do something… */
}

 time()integer

Returns the number of milliseconds that have passed since the current passage was rendered to the page.
返回自当前段落渲染到页面以来经过的毫秒数。

History:  历史:

Parameters: none  参数:无

Returns:  返回:

The number of milliseconds (integer) since the passage was rendered.
段落渲染以来的毫秒数( integer )。

Throws: none  无抛出异常

Examples:  示例:

→ Links that vary based on the time
In the darkness, something wicked this way comes.  Quickly!  Do you \
<<link "try to run back into the light">>
	<<if time() lt 5000>>
		/% The player clicked the link in under 5s, so they escape %/
		<<goto "Well lit passageway">>
	<<else>>
		/% Else, they're eaten by a grue %/
		<<goto "Eaten by a grue">>
	<</if>>
<</link>> \
or [[stand your ground|Eaten by a grue]]?

 triggerEvent(name [, targets [, options]])

Dispatches a synthetic event with the given name, optionally on the given targets and with the given options.
发送具有给定名称的合成事件,可选地在给定的目标上,并带有给定的选项。

History:  历史:

Parameters:  参数:

Tip: If dispatching custom events, it is recommended that you limit your custom event names to the following characters: letters, digits, periods (.), hyphens (-), underscores (_), and colons (:).
提示:如果发送自定义事件,建议将自定义事件名称限制为以下字符:字母、数字、点( . )、连字符( - )、下划线( _ )和冒号( : )。

Options object:  选项对象:

Warning: Adding additional properties directly to event options objects is not recommended. Instead, use the detail property.
警告:不建议直接向事件选项对象添加额外属性。相反,请使用 detail 属性。

An event options object should have some of the following properties:
事件选项对象应包含以下一些属性:

Returns: none  返回值:无

Throws: none  无抛出异常

Examples:  示例:

Dispatch a custom fnord event on document
document 上派发自定义的 fnord 事件
triggerEvent('fnord');
Dispatch a click event on the element bearing the ID some-menu
在具有 ID some-menu 的元素上派发 click 事件
triggerEvent('click', document.getElementById('some-menu'));
Dispatch a custom update-meter event on document while specifying some options
document 上派发自定义 update-meter 事件,并指定一些选项。
triggerEvent('update-meter', document, {
	detail : {
		tags : ['health', 'magick']
	}
});
Various ways to dispatch a mouseover event on all elements bearing the class flippable
在所有具有类 flippable 的元素上派发 mouseover 事件的多种方式。
triggerEvent('mouseover', document.getElementsByClassName('flippable'));

triggerEvent('mouseover', document.querySelectorAll('.flippable'));

triggerEvent('mouseover', jQuery('.flippable'));

 turns()integer

Returns the total number (count) of played turns currently in effect—i.e., the number of played moments up to the present moment; future (rewound/undone) moments are not included within the total.
返回当前生效的已播放回合总数(计数)——即,截至当前时刻已播放的瞬间数量;未来的(回放/撤销)瞬间不包括在总数内。

History:  历史:

Parameters: none  参数:无

Returns:  返回:

The turn count (integer).
轮数( integer )。

Throws: none  无抛出异常

Examples:  示例:

<<print "This is turn #" + turns()>>

 variables()object

Returns a reference to the active (present) story variables store (equivalent to: State.variables). This is only really useful within pure JavaScript code, as within TwineScript you may simply access story variables natively.
返回对活动(当前)故事变量存储的引用(相当于: State.variables )。这仅在纯 JavaScript 代码中非常有用,因为在 TwineScript 中,您可以直接访问故事变量。

History:  历史:

Parameters: none  参数:无

Returns:  返回:

A reference to the story variable store (object).
参考故事变量存储( object )的引用。

Throws: none  无抛出异常

Examples:  示例:

// Given: $hasGoldenKey is true
if (variables().hasGoldenKey) {
	/* Do something… */
}

 visited([passages…])integer

Returns the number of times that the passage with the given title occurred within the story history. If multiple passage titles are given, returns the lowest count.
返回给定标题的篇章在故事历史中出现的次数。如果给出多个篇章标题,则返回最低计数。

History:  历史:

Parameters:  参数:

Returns:  返回:

The passage count (integer).
段落数量( integer )。

Throws: none  无抛出异常

Examples:  示例:

<<if visited() is 3>>…this is the third visit to the current passage…<</if>>
<<if visited("Bar")>>…has been to the Bar at least once…<</if>>
<<if visited("Café") is 1>>…has been to the Café exactly once…<</if>>
<<if visited("Bar", "Café") is 4>>…has been to both the Bar and Café at least four times…<</if>>

 visitedTags(tags…)integer

Returns the number of passages within the story history that are tagged with all of the given tags.
返回故事历史中所有给定标签的段落数量。

History:  历史:

Parameters:  参数:

Returns:  返回:

The number (integer) of passages that are tagged with the given tags.
标记给定标签的段落数量( integer )。

Throws:  抛出异常:

An Error instance.  一个 Error 实例。

Examples:  示例:

<<if visitedTags("forest")>>…has been to some part of the forest at least once…<</if>>
<<if visitedTags("forest", "haunted") is 1>>…has been to the haunted part of the forest exactly once…<</if>>
<<if visitedTags("forest", "burned") is 3>>…has been to the burned part of the forest three times…<</if>>

 Methods   方法

Most of the methods listed below are SugarCube extensions, with the rest being either JavaScript natives or bundled library methods that are listed here for their utility—though, this is not an exhaustive list.
下面的大多数方法都是 SugarCube 扩展,其余的是 JavaScript 原生方法或捆绑的库方法,这里列出它们是为了它们的实用性——尽管这不是一个详尽的列表。

For more information see:
更多信息请参阅:

Additionally. SugarCube includes polyfills for virtually all JavaScript (ECMAScript) 5 & 6 native object methods—via the es5-shim and es6-shim polyfill libraries (shims only, no shams)—so they may be safely used even if your project will be played in ancient browsers that do not natively support them.
此外,SugarCube 包括几乎所有 JavaScript(ECMAScript)5 & 6 原生对象方法的 polyfills——通过 es5-shim 和 es6-shim polyfill 库(仅提供 shims,不提供 shams)——因此即使在那些不支持它们的古老浏览器中运行项目,也可以安全地使用它们。

 Array Methods   数组方法

 <Array>.concat(members…)Array<any>

Concatenates one or more members to the end of the base array and returns the result as a new array. Does not modify the original.
连接一个或多个成员到数组的末尾,并返回一个新数组。不会修改原始数组。

History: native JavaScript method
历史:原生 JavaScript 方法

Parameters:  参数:

Examples:  示例:

// Given: $fruits1 = ["Apples", "Oranges"], $fruits2 = ["Pears", "Plums"]
$fruits1.concat($fruits2)            → Returns ["Apples", "Oranges", "Pears", "Plums"]
$fruits1.concat($fruits2, $fruits2)  → Returns ["Apples", "Oranges", "Pears", "Plums", "Pears", "Plums"]
$fruits1.concat("Pears")             → Returns ["Apples", "Oranges", "Pears"]
$fruits1.concat("Pears", "Pears")    → Returns ["Apples", "Oranges", "Pears", "Pears"]
$fruits1.concat($fruits2, "Pears")   → Returns ["Apples", "Oranges", "Pears", "Plums", "Pears"]

 <Array>.concatUnique(members…)Array<any>

Concatenates one or more unique members to the end of the base array and returns the result as a new array. Does not modify the original.
将一个或多个唯一的成员连接到基本数组的末尾,并返回一个新数组。不会修改原始数组。

History:  历史:

Parameters:

Examples:

// Given: $fruits1 = ["Apples", "Oranges"], $fruits2 = ["Pears", "Plums"]
$fruits1.concatUnique($fruits2)            → Returns ["Apples", "Oranges", "Pears", "Plums"]
$fruits1.concatUnique($fruits2, $fruits2)  → Returns ["Apples", "Oranges", "Pears", "Plums"]
$fruits1.concatUnique("Pears")             → Returns ["Apples", "Oranges", "Pears"]
$fruits1.concatUnique("Pears", "Pears")    → Returns ["Apples", "Oranges", "Pears"]
$fruits1.concatUnique($fruits2, "Pears")   → Returns ["Apples", "Oranges", "Pears", "Plums"]

 <Array>.count(needle [, position])integer

Returns the number of times that the given member was found within the array, starting the search at position.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.count("Oranges")     → Returns 2
$fruits.count("Oranges", 2)  → Returns 1

 <Array>.countWith(predicate [, thisArg])integer

Returns the number of times that members within the array pass the test implemented by the given predicate function.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.countWith(function (fruit) { return fruit === "Oranges"; })  → Returns 2
// Given: $numbers = [1, 2.3, 4, 76, 3.1]
$numbers.countWith(Number.isInteger)  → Returns 3
// Given: $items = [
// 	{ name : 'Healing potion', kind : 'potion' },
// 	{ name : 'Longsword', kind : 'weapon' },
// 	{ name : 'Mana potion', kind : 'potion' },
// 	{ name : 'Dead rat', kind : 'junk' },
// 	{ name : 'Endurance potion', kind : 'potion' },
// 	{ name : 'Shortbow', kind : 'weapon' }
// ]
$items.countWith(function (item) { return item.kind === 'junk'; })  → Returns 1

 <Array>.deleteAll(needles…)Array<any>

Removes all instances of the given members from the array and returns a new array containing the removed members.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.deleteAll("Oranges")          → Returns ["Oranges", "Oranges"]; $fruits ["Apples", "Plums"]
$fruits.deleteAll("Apples", "Plums")  → Returns ["Apples", "Plums"]; $fruits ["Oranges", "Oranges"]

 <Array>.deleteAt(indices…)Array<any>

Removes all of the members at the given indices from the array and returns a new array containing the removed members.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.deleteAt(2)     → Returns ["Plums"]; $fruits ["Apples", "Oranges", "Oranges"]
$fruits.deleteAt(1, 3)  → Returns ["Oranges", "Oranges"]; $fruits ["Apples", "Plums"]
$fruits.deleteAt(0, 2)  → Returns ["Apples", "Plums"]; $fruits ["Oranges", "Oranges"]

 <Array>.deleteFirst(needles…)Array<any>

Removes the first instance of the given members from the array and returns a new array containing the removed members.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.deleteFirst("Oranges")          → Returns ["Oranges"]; $fruits ["Apples", "Plums", "Oranges"]
$fruits.deleteFirst("Apples", "Plums")  → Returns ["Apples", "Plums"]; $fruits ["Oranges", "Oranges"]

 <Array>.deleteLast(needles…)Array<any>

Removes the last instance of the given members from the array and returns a new array containing the removed members.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.deleteLast("Oranges")          → Returns ["Oranges"]; $fruits ["Apples", "Oranges", "Plums"]
$fruits.deleteLast("Apples", "Plums")  → Returns ["Apples", "Plums"]; $fruits ["Oranges", "Oranges"]

 <Array>.deleteWith(predicate [, thisArg])Array<any>

Removes all of the members from the array that pass the test implemented by the given predicate function and returns a new array containing the removed members.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Apricots", "Oranges"]

→ Returns ["Apricots"]; $fruits ["Apples", "Oranges"]
$fruits.deleteWith(function (val) {
	return val === "Apricots";
})

→ Returns ["Apples", "Apricots"]; $fruits ["Oranges"]
$fruits.deleteWith(function (val) {
	return val.startsWith("Ap");
})

// Given: $fruits = [{ name : "Apples" }, { name : "Apricots" }, { name : "Oranges" }]

→ Returns [{ name : "Apricots" }]; $fruits [{ name : "Apples" }, { name : "Oranges" }]
$fruits.deleteWith(function (val) {
	return val.name === "Apricots";
})

→ Returns [{ name : "Apples" }, { name : "Apricots" }]; $fruits [{ name : "Oranges" }]
$fruits.deleteWith(function (val) {
	return val.name.startsWith("Ap");
})

 <Array>.first()any

Returns the first member from the array. Does not modify the original.

History:

Parameters: none

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.first()  → Returns "Blueberry"

 <Array>.flat(depth)Array<any>

Returns a new array consisting of the source array with all sub-array elements concatenated into it recursively up to the given depth. Does not modify the original.

History: native JavaScript method

Parameters:

Examples:

// Given: $npa = [["Alfa", "Bravo"], [["Charlie", "Delta"], ["Echo"]], "Foxtrot"]

$npa.flat()   → Returns ["Alfa", "Bravo", ["Charlie", "Delta"], ["Echo"], "Foxtrot"]
$npa.flat(1)  → Returns ["Alfa", "Bravo", ["Charlie", "Delta"], ["Echo"], "Foxtrot"]
$npa.flat(2)  → Returns ["Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"]

 <Array>.flatMap(callback [, thisArg])Array<any>

Returns a new array consisting of the result of calling the given mapping function on every element in the source array and then concatenating all sub-array elements into it recursively up to a depth of 1. Does not modify the original.

Note: Identical to calling <Array>.map(…).flat().

History: native JavaScript method

Parameters:

Examples:

// Given: $npa = ["Alfa", "Bravo Charlie", "Delta Echo Foxtrot"]

→ Returns ["Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"]
$npa.flatMap(function (val) {
	return val.split(" ");
})

 <Array>.includes(needle [, position])boolean

Returns whether the given member was found within the array, starting the search at position.

History: native JavaScript method

Parameters:

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
<<if $pies.includes("Cherry")>>…found Cherry pie…<</if>>
<<if $pies.includes("Pecan", 3)>>…found Pecan pie within ["Pecan", "Pumpkin"]…<</if>>

 <Array>.includesAll(needles…)boolean

Returns whether all of the given members were found within the array.

History:

Parameters:

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
<<if $pies.includesAll("Cherry", "Pecan")>>…found Cherry and Pecan pies…<</if>>

// Given: $search = ["Blueberry", "Pumpkin"]
<<if $pies.includesAll($search)>>…found Blueberry and Pumpkin pies…<</if>>

 <Array>.includesAny(needles…)boolean

Returns whether any of the given members were found within the array.

History:

Parameters:

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
<<if $pies.includesAny("Cherry", "Pecan")>>…found Cherry or Pecan pie…<</if>>

// Given: $search = ["Blueberry", "Pumpkin"]
<<if $pies.includesAny($search)>>…found Blueberry or Pumpkin pie…<</if>>

 <Array>.last()any

Returns the last member from the array. Does not modify the original.

History:

Parameters: none

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.last()  → Returns "Pumpkin"

 <Array>.pluck()any

Removes and returns a random member from the base array.

History:

Parameters: none

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.pluck()  → Removes and returns a random pie from the array

 <Array>.pluckMany(want)Array<any>

Randomly removes the given number of members from the base array and returns the removed members as a new array.

History:

Parameters:

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.pluckMany(3)  → Removes three random pies from the array and returns them as a new array

 <Array>.pop()any

Removes and returns the last member from the array, or undefined if the array is empty.

History: native JavaScript method

Parameters: none

Examples:

// Given: $fruits = ["Apples", "Oranges", "Pears"]
$fruits.pop()  → Returns "Pears"; $fruits ["Apples", "Oranges"]

 <Array>.push(members…)number

Appends one or more members to the end of the base array and returns its new length.

History: native JavaScript method

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges"]
$fruits.push("Apples")  → Returns 3; $fruits ["Apples", "Oranges", "Apples"]

// Given: $fruits = ["Apples", "Oranges"]
$fruits.push("Plums", "Plums")  → Returns 4; $fruits ["Apples", "Oranges", "Plums", "Plums"]

 <Array>.pushUnique(members…)number

Appends one or more unique members to the end of the base array and returns its new length.

History:

Parameters:

Examples:

// Given: $fruits = ["Apples", "Oranges"]
$fruits.pushUnique("Apples")  → Returns 2; $fruits ["Apples", "Oranges"]

// Given: $fruits = ["Apples", "Oranges"]
$fruits.pushUnique("Plums", "Plums")  → Returns 3; $fruits ["Apples", "Oranges", "Plums"]

 <Array>.random()any

Returns a random member from the base array. Does not modify the original.

History:

Parameters: none

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.random()  → Returns a random pie from the array

 <Array>.randomMany(want)Array<any>

Randomly selects the given number of unique members from the base array and returns the selected members as a new array. Does not modify the original.

History:

Parameters:

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.randomMany(3)  → Returns a new array containing three unique random pies from the array

 <Array>.shift()any

Removes and returns the first member from the array, or undefined if the array is empty.

History: native JavaScript method

Parameters: none

Examples:

// Given: $fruits = ["Apples", "Oranges", "Pears"]
$fruits.shift()  → Returns "Apples"; $fruits ["Oranges", "Pears"]

 <Array>.shuffle()Array<any>

Randomly shuffles the array.

History:

Parameters: none

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.shuffle()  → Randomizes the order of the pies in the array

 <Array>.toShuffled()Array<any>

Returns a new copy of the base array created by shuffling the array. Does not modify the original.

History:

Parameters: none

Examples:

// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.toShuffled()  → Randomizes the order of the pies in the array w/o modifying the original

 <Array>.toUnique()Array<any>

Returns a new copy of the base array created by removing all duplicate members. Does not modify the original.

History:

Parameters: none

Examples:

// Given: $fruits = ["Apples", "Oranges", "Plums", "Plums", "Apples"]
$fruits.toUnique()  → Returns ["Apples", "Oranges", "Plums"]

 <Array>.unshift(members…)number

Prepends one or more members to the beginning of the base array and returns its new length.

History: native JavaScript method

Parameters:

Examples:

// Given: $fruits = ["Oranges", "Plums"]
$fruits.unshift("Oranges")  → Returns 3; $fruits ["Oranges", "Oranges", "Plums"]

// Given: $fruits = ["Oranges", "Plums"]
$fruits.unshift("Apples", "Apples")  → Returns 4; $fruits ["Apples", "Apples", "Oranges", "Plums"]

 <Array>.unshiftUnique(members…)number

Prepends one or more unique members to the beginning of the base array and returns its new length.

History:

Parameters:

Examples:

// Given: $fruits = ["Oranges", "Plums"]
$fruits.unshiftUnique("Oranges")  → Returns 2; $fruits ["Oranges", "Plums"]

// Given: $fruits = ["Oranges", "Plums"]
$fruits.unshiftUnique("Apples", "Apples")  → Returns 3; $fruits ["Apples", "Oranges", "Plums"]

 <Array>.delete(needles…)Array<any>

Deprecated: This instance method has been deprecated and should no longer be used. See the <Array>.deleteAll() instance method.

History:

 jQuery Methods

 <jQuery>.ariaClick([options ,] handler)jQuery object

Makes the target element(s) WAI-ARIA-compatible clickables—meaning that various accessibility attributes are set and, in addition to mouse clicks, enter/return and spacebar key presses also activate them. Returns a reference to the current jQuery object for chaining.

History:

Parameters:

Options object:

An options object should have some of the following properties:

Examples:

// Given an existing element: <a id="so-clicky">Click me</a>
$('#so-clicky').ariaClick(function (event) {
	/* do stuff */
});

// Creates a basic link and appends it to the `output` element
$('<a>Click me</a>')
	.ariaClick(function (event) {
		/* do stuff */
	})
	.appendTo(output);

// Creates a basic button and appends it to the `output` element
$('<button>Click me</button>')
	.ariaClick(function (event) {
		/* do stuff */
	})
	.appendTo(output);

// Creates a link with options and appends it to the `output` element
$('<a>Click me</a>')
	.ariaClick({
		one   : true,
		label : 'This single-use link does stuff.'
	}, function (event) {
		/* do stuff */
	})
	.appendTo(output);

 <jQuery>.ariaDisabled(state)jQuery object

Changes the disabled state of the target WAI-ARIA-compatible clickable element(s). Returns a reference to the current jQuery object for chaining.

Note: This method is meant to work with clickables created via <jQuery>.ariaClick() and may not work with clickables from other sources. SugarCube uses <jQuery>.ariaClick() internally to handle all of its various link markup and macros.

History:

Parameters:

Examples:

// Given an existing WAI-ARIA-compatible clickable element with the ID "so-clicky"
$('#so-clicky').ariaDisabled(true)   → Disables the target element
$('#so-clicky').ariaDisabled(false)  → Enables the target element

 <jQuery>.ariaIsDisabled()boolean

Returns whether any of the target WAI-ARIA-compatible clickable element(s) are disabled.

Note: This method is meant to work with clickables created via <jQuery>.ariaClick() and may not work with clickables from other sources. SugarCube uses <jQuery>.ariaClick() internally to handle all of its various link markup and macros.

History:

Parameters: none

Examples:

// Given an existing WAI-ARIA-compatible clickable element with the ID "so-clicky"

// If "#so-clicky" is disabled:
$('#so-clicky').ariaIsDisabled()  → Returns true

// If "#so-clicky" is enabled:
$('#so-clicky').ariaIsDisabled()  → Returns false

 jQuery.wiki(sources…)

Wikifies the given content source(s) and discards the result. If there were errors, an exception is thrown. This is only really useful when you want to invoke a macro for its side-effects and aren't interested in its output.

History:

Parameters:

Examples:

$.wiki('<<somemacro>>');  → Invokes the <<somemacro>> macro, discarding any output

 jQuery.wikiPassage(name)

Wikifies the passage by the given name and discards the result. If there were errors, an exception is thrown. This is only really useful when you want to invoke a macro for its side-effects and aren't interested in its output.

History:

Parameters:

Examples:

$.wikiPassage('Fight Init');  → Renders the passage, discarding any output

 <jQuery>.wiki(sources…)jQuery object

Wikifies the given content source(s) and appends the result to the target element(s). Returns a reference to the current jQuery object for chaining.

History:

Parameters:

Examples:

// Given an element: <div id="the-box"></div>
$('#the-box').wiki('Who //are// you?');  → Appends "Who <em>are</em> you?" to the target element

 <jQuery>.wikiPassage(name)jQuery object

Wikifies the passage by the given name and appends the result to the target element(s). Returns a reference to the current jQuery object for chaining.

History:

Parameters:

Examples:

// Given an element: <div id="notebook"></div>
$('#notebook').wikiPassage('Notes');  → Appends the rendered passage to the target element

 JSON Methods

 JSON.reviveWrapper(code [, data])array

Deprecated: This static method has been deprecated and should no longer be used. See the Serial.createReviver() static method.

History:

 Math Methods

 Math.clamp(num , min , max)number

Returns the given number clamped to the specified bounds. Does not modify the original.

History:

Parameters:

Examples:

Math.clamp($stat, 0, 200)  → Clamps $stat to the bounds 0–200 and returns the new value
Math.clamp($stat, 1, 6.6)  → Clamps $stat to the bounds 1–6.6 and returns the new value

 Math.trunc(num)integer

Returns the whole (integer) part of the given number by removing its fractional part, if any. Does not modify the original.

History: native JavaScript method

Parameters:

Examples:

Math.trunc(12.7)   → Returns 12
Math.trunc(-12.7)  → Returns -12

 Number Methods

 <Number>.clamp(min , max)number

Deprecated: This static method has been deprecated and should no longer be used. See the Math.clamp() static method.

History:

 RegExp Methods

 RegExp.escape(text)string

Returns the given string with all regular expression metacharacters escaped. Does not modify the original.

History:

Parameters:

Examples:

RegExp.escape('That will be $5 (cash only)')   → Returns 'That will be \$5 \(cash only\)'

 Serial Methods

 Serial.createReviver(code [, data])Array

Returns the given code string, and optional data, wrapped within the deserialization reviver. Intended to allow authors to easily create the reviver required to revive their custom object types (classes). The reviver should be returned from an object instance's .toJSON() method, so that the instance may be properly revived upon deserialization.

See: The Non-generic object types (classes) guide for more detailed information.

History:

Parameters:

Examples:

Serial.createReviver( /* valid JavaScript code string */ );             → Without data chunk
Serial.createReviver( /* valid JavaScript code string */ , myOwnData);  → With data chunk

// E.g., Assume that you're attempting to revive an instance of a custom class named
//       `Character`, which is assigned to a story variable named `$pc`.  The call
//       to `Serial.createReviver()` might look something like the following.
var ownData = {};
Object.keys(this).forEach(function (pn) { ownData[pn] = clone(this[pn]); }, this);
return Serial.createReviver('new Character($ReviveData$)', ownData);

 String Methods

Note: Strings in TwineScript/JavaScript are Unicode, however, due to historic reasons they are comprised of, and indexed by, individual UTF-16 code units rather than code points. This means that some code points may span multiple code units—e.g., the emoji 💩 is one code point, but two code units.

 <String>.count(needle [, position])integer

Returns the number of times that the given substring was found within the string, starting the search at position.

History:

Parameters:

Examples:

// Given: $text = "How now, brown cow."
$text.count("ow")     → Returns 4
$text.count("ow", 8)  → Returns 2

 <String>.first()string

Returns the first Unicode code point within the string. Does not modify the original.

See: String methods note.

History:

Parameters: none

Examples:

// Given: $text = "abc"
$text.first()  → Returns "a"

// Given: $text = "🙈🙉🙊"
$text.first()  → Returns "🙈"

 String.format(format , arguments…)string

Returns a formatted string, after replacing each format item in the given format string with the text equivalent of the corresponding argument's value.

History:

Parameters:

Format items:

A format item has the syntax {index[,alignment]}, square-brackets denoting optional elements.

Examples:

String.format("{0}, {1}!", "Hello", "World")      → List of arguments; Returns "Hello, World!"
String.format("{0}, {1}!", [ "Hello", "World" ])  → Array argument; Returns "Hello, World!"
String.format("{0,6}", "foo")                     → Returns "   foo"
String.format("{0,-6}", "foo")                    → Returns "foo   "

 <String>.includes(needle [, position])boolean

Returns whether the given substring was found within the string, starting the search at position.

History: native JavaScript method

Parameters:

Examples:

// Given: $text = "How now, brown cow."
$text.includes("row")      → Returns true
$text.includes("row", 14)  → Returns false
$text.includes("cow", 14)  → Returns true
$text.includes("pow")      → Returns false

 <String>.last()string

Returns the last Unicode code point within the string. Does not modify the original.

See: String methods note.

History:

Parameters: none

Examples:

// Given: $text = "abc"
$text.last()  → Returns "c"

// Given: $text = "🙈🙉🙊"
$text.last()  → Returns "🙊"

 <String>.toLocaleUpperFirst()string

Returns the string with its first Unicode code point converted to upper case, according to any locale-specific rules. Does not modify the original.

See: String methods note.

History:

Parameters: none

Examples:

// Using the Turkish (Türkçe) locale and given: $text = "ışık"
$text.toLocaleUpperFirst()  → Returns "Işık"

// Using the Turkish (Türkçe) locale and given: $text = "iki"
$text.toLocaleUpperFirst()  → Returns "İki"

 <String>.toUpperFirst()string

Returns the string with its first Unicode code point converted to upper case. Does not modify the original.

See: String methods note.

History:

Parameters: none

Examples:

// Given: $text = "hello."
$text.toUpperFirst()  → Returns "Hello."

// Given: $text = "χαίρετε."
$text.toUpperFirst()  → Returns "Χαίρετε."

 Special Names

Passage, tag, and variable names that have special meaning to SugarCube.

 Warning

  1. All special names listed herein are case sensitive, so their spelling and capitalization must be exactly as shown.
  2. Never combine special or code passages with code tags. By doing so, you will probably break things in subtle and hard to detect ways.

 Code Passages

Passages that are used only as code and should not be navigated to. They exist simply to fill in parts of the UI—e.g., StoryCaption—or execute code at specific times—e.g., PassageReady—or both—e.g., PassageHeader.

 PassageDone

Used for post-passage-display tasks, like redoing dynamic changes (happens after the rendering and display of each passage). Generates no output.

Roughly equivalent to the :passagedisplay event.

History:

 PassageFooter

Appended to each rendered passage.

Roughly equivalent to the :passagerender event.

History:

 PassageHeader

Prepended to each rendered passage.

Roughly equivalent to the :passagestart event.

History:

 PassageReady

Used for pre-passage-display tasks, like redoing dynamic changes (happens before the rendering of each passage). Generates no output.

Roughly equivalent to the :passagestart event.

History:

 StoryAuthor

Used to populate the authorial byline area in the UI bar (element ID: story-author).

History:

 StoryBanner

Used to populate the story's banner area in the UI bar (element ID: story-banner).

History:

 StoryCaption

Used to populate the story's caption area in the UI bar (element ID: story-caption). May also be, and often is, used to add additional story UI elements and content to the UI bar.

History:

 StoryDisplayTitle

Sets the story's display title in the browser's titlebar and the UI bar (element ID: story-title). If omitted, the story title will be used instead.

History:

 StoryInit

Used for pre-story-start initialization tasks, like variable initialization (happens at the beginning of story initialization). Generates no output.

History:

 StoryInterface

Used to replace SugarCube's default UI. Its contents are treated as raw HTML markup—i.e., none of SugarCube's special HTML processing is performed. The markup is contained within a <div id="story" role="main"> element and must itself contain, at least, an element with the ID passages that will be the main passage display area. For example:

<div id="story" role="main">
	<!-- StoryInterface elements added here -->
</div>

Additional elements, aside from the #passages element, may include either the data-init-passage or data-passage content attribute, whose value is the name of the passage used to populate the element—the passage will be processed as normal, meaning that markup and macros will work as expected. The data-init-passage attribute causes the element to be updated once at initialization, while the data-passage attribute causes the element to be updated upon each passage navigation.

Warning: Elements that include either a data-init-passage or data-passage content attribute should not themselves contain additional elements. This is because such elements' contents are replaced via their associated passage, so any child elements will be lost.

History:

Examples:

Minimal working example
<div id="passages"></div>

Combined with the built-in wrapper:

<div id="story" role="main">
	<div id="passages"></div>
</div>
With data-init-passage and data-passage content attributes
<div id="menu" data-init-passage="Menu"></div>
<div id="notifications" data-passage="Notifications"></div>
<div id="passages"></div>

Combined with the built-in wrapper:

<div id="story" role="main">
	<div id="menu" data-init-passage="Menu"></div>
	<div id="notifications" data-passage="Notifications"></div>
	<div id="passages"></div>
</div>

 StoryMenu

Used to populate the story's menu items in the UI bar (element ID: menu-story).

Note: The story menu only displays links—specifically, anything that creates an anchor element (<a>). While it renders content just as any other passage does, instead of displaying the rendered output as-is, it sifts through the output and builds its menu from the generated links contained therein.

History:

Examples:

[[Inventory]]
<<link "Schedule">>…<</link>>

 StorySettings

Warning: Twine 1.4 code passage unused by SugarCube. The Config API serves the same basic purpose.

 StorySubtitle

Sets the story's subtitle in the UI bar (element ID: story-subtitle).

History:

 StoryTitle

Warning: The story title is used to create the storage ID that is used to store all player data, both temporary and persistent. It should be plain text, containing no code, markup, or macros of any kind.

Tip: If you want to set a title for display that contains code, markup, or macros, see the StoryDisplayTitle code passage.

Twine 2: Unused, not a code passage. The story's title is part of the story project.

Twine 1/Twee: Required. Sets the story's title.

History:

 StoryShare

Deprecated: This special passage has been deprecated and should no longer be used.

History:

 Special Passages

Passages that receive some kind of special treatment from the engine.

Note: Some special passages are conditional and may not always be special passages. The conditions will be noted within each such passsge's entry.

 Start

Twine 2: Not a special passage. Any passage may be chosen as the starting passage by selecting it via the Start Story Here passage context-menu item—n.b. older versions of Twine 2 used a icon for the same purpose.

Twine 1/Twee: Required. The starting passage, the first passage displayed. Configurable, see Config.passages.start for more information.

History:

 Code Tags

Passages tagged with code tags are used only as code or data and cannot be navigated to.

Note: Some code tags are conditional and may not always act as code tags. The conditions will be noted within each such tag's entry.

 init

Registers the passage as an initialization passage. Used for pre-story-start initialization tasks, like variable initialization (happens at the beginning of story initialization). Generates no output.

Note: This is chiefly intended for use by add-ons/libraries. For normal projects, authors are strongly encouraged to continue to use the StoryInit special named passage.

History:

 script

Twine 2: Unused, not a code tag. Use the Edit Story JavaScript story editor menu item for scripts.

Twine 1/Twee: Registers the passage as JavaScript code, which is executed during startup.

History:

 stylesheet

Twine 2: Unused, not a code tag. Use the Edit Story Stylesheet story editor menu item for styles.

Twine 1/Twee: Registers the passage as a CSS stylesheet, which is loaded during startup. It is strongly recommended that you use only one stylesheet passage. Additionally, see the tagged stylesheet warning.

History:

 Twine.audio

Registers the passage as an audio passage. See Guide: Media Passages for more information.

History:

 Twine.image

Registers the passage as an image passage. See Guide: Media Passages for more information.

History:

 Twine.video

Registers the passage as a video passage. See Guide: Media Passages for more information.

History:

 Twine.vtt

Registers the passage as a VTT passage. See Guide: Media Passages for more information.

History:

 widget

Registers the passage as <<widget>> macro definitions, which are loaded during startup.

History:

 Special Tags

 nobr

Causes leading/trailing newlines to be removed and all remaining sequences of newlines to be replaced with single spaces before the passage is rendered. Equivalent to wrapping the entire passage in a <<nobr>> macro. See the Config.passages.nobr setting for a way to apply the same processing to all passages at once.

Note: Does not affect script or stylesheet tagged passages, for Twine 1/Twee.

History:

 bookmark

Deprecated: This special tag has been deprecated and should no longer be used.

History:

 Special Variables

 $

Alias for jQuery, by default. NOTE: This should not be confused with story variables, which start with a $—e.g., $foo.

History:

 _args

Widget arguments array (only inside widgets). See <<widget>> for more information.

History:

 _contents

Widget contents string (only inside block widgets). See <<widget>> for more information.

History:

 Config

Configuration API. See Config API for more information.

History:

 Dialog

Dialog API. See Dialog API for more information.

History:

 Engine

Engine API. See Engine API for more information.

History:

 Fullscreen

Fullscreen API. See Fullscreen API for more information.

History:

 jQuery

jQuery library function.

History:

 l10nStrings

Strings localization object. See Localization for more information.

History:

 LoadScreen

LoadScreen API. See LoadScreen API for more information.

History:

 Macro

Macro API. See Macro API for more information.

History:

 Passage

Passage API. See Passage API for more information.

History:

 Save

Save API. See Save API for more information.

History:

 Setting

Setting API. See Setting API for more information.

History:

 settings

Player settings object, set up by the author/developer. See Setting API for more information.

History:

 setup

Object that authors/developers may use to set up various bits of static data. Generally, you would use this for data that does not change and should not be stored within story variables, which would make it part of the history.

History:

 SimpleAudio

SimpleAudio API. See SimpleAudio API for more information.

History:

 State

State API. See State API for more information.

History:

 Story

Story API. See Story API for more information.

History:

 Template

Template API. See Template API for more information.

History:

 UI

UI API. See UI API for more information.

History:

 UIBar

UIBar API. See UIBar API for more information.

History:

 $args

Deprecated: The $args special variable has been deprecated and should no longer be used. See the _args special variable for its replacement.

History:

 postdisplay

Deprecated: postdisplay tasks have been deprecated and should no longer be used. See the :passagedisplay event for its replacement.

History:

 postrender

Deprecated: postrender tasks have been deprecated and should no longer be used. See the :passagerender event for its replacement.

History:

 predisplay

Deprecated: predisplay tasks have been deprecated and should no longer be used. See the :passagestart event for its replacement.

History:

 prehistory

Deprecated: prehistory tasks have been deprecated and should no longer be used. See the :passageinit event for its replacement.

History:

 prerender

Deprecated: prerender tasks have been deprecated and should no longer be used. See the :passagestart event for its replacement.

History:

 CSS

 Passage Conversions

IDs and classes automatically generated from passage names and tags are normalized to kebab case with all lowercase letters—which entails: removing characters that are not alphanumerics, underscores, hyphens, en-/em-dashes, or whitespace, then replacing any remaining non-alphanumeric characters with hyphens, one per group, and finally converting the result to lowercase.

Passage Names

Passage names have passage- prepended to their converted forms and are converted both into IDs and classes depending on how the passage is used—an ID for the active passage, classes for included (via <<include>>) passages.

For example, if the passage name was Gone fishin', then:

Passage Tags

When displaying a passage, its tags are:

  1. Added to the active passage's container element, <html> element, and <body> element as a space separated list within the data-tags attribute.
  2. Added to the active passage's container element and <body> element as classes. The following special tags are excluded from this mapping:
    Twine 2: debug, nobr, passage, widget, and any tag starting with twine.
    Twine 1/Twee: debug, nobr, passage, script, stylesheet, widget, and any tag starting with twine.

For example, if the tag name was Sector_42, then it would become both the data-tags attribute member Sector_42 (selector: [data-tags~="Sector_42"]) and the class sector-42 (selector: .sector-42).

 Example Selectors

Selector Description
html

The document element. The default font stack is set here.

The active passage's tags will be added to its data-tags attribute (see: Passage Conversions).

body

The body of the page. The default foreground and background colors are set here.

The active passage's tags will be added to its data-tags attribute and classes (see: Passage Conversions).

#story Selects the story element.
#passages Selects the element that contains passage elements. All created passage elements will be children of this element.
.passage

Selects the passage element. Normally, there will be only one such passage per turn, however, during passage navigation there may briefly be two—the incoming (a.k.a. active) and outgoing passages.

The active passage's name will be added as its ID (see: Passage Conversions).

The active passage's tags will be added to its data-tags attribute and classes (see: Passage Conversions).

.passage a Selects all <a> elements within the passage element.
.passage a:hover Selects <a> elements within the passage element that are being hovered over.
.passage a:active Selects <a> elements within the passage element that are being clicked on.
.passage .link-broken Selects all internal link elements within the passage element whose passages do not exist within the story.
.passage .link-disabled Selects all internal link elements within the passage element who have been disabled—e.g., already chosen <<choice>> macro links.
.passage .link-external Selects all external link elements within the passage element—e.g., links to other pages and websites.
.passage .link-internal Selects all internal link elements within the passage element—e.g., passage and macro links.
.passage .link-visited1 Selects all internal link elements within the passage element whose passages are within the in-play story history—i.e., passages the player has been to before.
.passage .link-internal:not(.link-visited)1 Selects all internal link elements within the passage element whose passages are not within the in-play story history—i.e., passages the player has never been to before.
  1. The .link-visited class is not enabled by default, see the Config API's Config.addVisitedLinkClass property for more information.

 Warnings

Multiple Stylesheets (for Twine 1/Twee only)

When using Twine 1/Twee, it is strongly recommended that you use only a single stylesheet tagged passage. CSS styles cascade in order of load, so if you use multiple stylesheet tagged passages, then it is all too easy for your styles to be loaded in the wrong order, since Twine 1/Twee gives you no control over the order that multiple stylesheet tagged passages load.

Tagged Stylesheets

SugarCube does not support the Twine 1.4+ vanilla story formats' tagged stylesheets. In SugarCube, you would instead simply prefix the selectors of your styles with the appropriate tag-based selectors—e.g., either [data-tags~="…"] attribute selectors or class selectors.

For example, if some story passages were tagged with forest, then styles for those forest passages might look like this:

/* Using [data-tags~="…"] attribute selectors on <html> */
html[data-tags~="forest"] { background-image: url(forest-bg.jpg); }
html[data-tags~="forest"] .passage { color: darkgreen; }
html[data-tags~="forest"] a { color: green; }
html[data-tags~="forest"] a:hover { color: lime; }

/* Using [data-tags~="…"] attribute selectors on <body> */
body[data-tags~="forest"] { background-image: url(forest-bg.jpg); }
body[data-tags~="forest"] .passage { color: darkgreen; }
body[data-tags~="forest"] a { color: green; }
body[data-tags~="forest"] a:hover { color: lime; }

/* Using class selectors on <body> */
body.forest { background-image: url(forest-bg.jpg); }
body.forest .passage { color: darkgreen; }
body.forest a { color: green; }
body.forest a:hover { color: lime; }

 Built-in Stylesheets

These are SugarCube's built-in stylesheets, in order of load/cascade. The most interesting of which, from an end-user's standpoint, are 5–13. The links go to the most recent release versions of each in SugarCube's source code repository.

  1. normalize.css
  2. init-screen.css
  3. font-icons.css
  4. font-emoji.css
  5. core.css
  6. core-display.css
  7. core-passage.css
  8. core-macro.css
  9. ui-dialog.css
  10. ui-dialog-saves.css
  11. ui-dialog-settings.css
  12. ui-dialog-legacy.css
  13. ui-bar.css
  14. ui-debug-bar.css
  15. ui-debug-views.css

 HTML

The hierarchy of the document body, including associated HTML IDs and class names is as follows.

Notes:

<body class="…">
	<div id="init-screen"></div>
	<div id="ui-overlay" class="ui-close"></div>
	<div id="ui-dialog" tabindex="0" role="dialog" aria-labelledby="ui-dialog-title">
		<div id="ui-dialog-titlebar">
			<h1 id="ui-dialog-title"></h1>
			<button id="ui-dialog-close" class="ui-close" tabindex="0" aria-label="…"></button>
		</div>
		<div id="ui-dialog-body"></div>
	</div>
	<div id="ui-bar">
		<div id="ui-bar-tray">
			<button id="ui-bar-toggle" tabindex="0" title="…" aria-label="…"></button>
			<div id="ui-bar-history">
				<button id="history-backward" tabindex="0" title="…" aria-label="…">…</button>
				<button id="history-jumpto" tabindex="0" title="…" aria-label="…">…</button>
				<button id="history-forward" tabindex="0" title="…" aria-label="…">…</button>
			</div>
		</div>
		<div id="ui-bar-body">
			<header id="title" role="banner">
				<div id="story-banner"></div>
				<h1 id="story-title"></h1>
				<div id="story-subtitle"></div>
				<div id="story-title-separator"></div>
				<p id="story-author"></p>
			</header>
			<div id="story-caption"></div>
			<nav id="menu" role="navigation">
				<ul id="menu-story">…<ul>
				<ul id="menu-core">
					<li id="menu-item-continue"><a tabindex="0">…</a></li>
					<li id="menu-item-saves"><a tabindex="0">…</a></li>
					<li id="menu-item-settings"><a tabindex="0">…</a></li>
					<li id="menu-item-restart"><a tabindex="0">…</a></li>
				</ul>
			</nav>
		</div>
	</div>
	<div id="story" role="main">
		<div id="passages">
			<div class="passage …" id="…" data-passage="…">
				<!-- The active (present) passage content -->
			</div>
		</div>
	</div>
	<!-- The story data chunk, which depends on the compiler release (see below) -->
	<script id="script-sugarcube" type="text/javascript"><!-- The main SugarCube module --></script>
</body>

Story data chunks:

Periods of ellipsis () signify data that is generated at compile time.

Twine 2 style data chunk
<tw-storydata name="…" startnode="…" creator="…" creator-version="…"
	ifid="…" zoom="…" format="…" format-version="…" options="…" hidden>
	<!-- Passage data nodes… -->
</tw-storydata>
Twine 1 style data chunk
<div id="store-area" data-size="…" hidden>
	<!-- Passage data nodes… -->
</div>

 Events

Events are messages that are sent (a.k.a.: fired, triggered) to notify code that something has taken place, from player interactions to automated happenings. Each event is represented by an object that has properties that may be used to get additional information about what happened.

This section offers a list of SugarCube-specific events, triggered at various points during story operation.

See Also: For standard browser/DOM events, see the Event reference @MDN.

 Dialog Events

Dialog events allow the execution of JavaScript code at specific points during the opening and closing of dialogs.

See: Dialog API.

 :dialogclosed event

Global event triggered as the last step in closing the dialog when Dialog.close() is called.

Warning: You cannot obtain data about the closing dialog from the dialog itself—e.g., title or classes—when using the :dialogclosed event, as the dialog has already closed and been reset by the time the event is fired. If you need that kind of information from the dialog itself, then you may use the :dialogclosing event instead.

History:

Event object properties: none

Note: While there are no custom properties, the event is fired from the dialog's body, thus the target property will refer to its body element—i.e., #ui-dialog-body.

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':dialogclosed', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function exactly once. */
$(document).one(':dialogclosed', function (ev) {
	/* JavaScript code */
});

 :dialogclosing event

Global event triggered as the first step in closing the dialog when Dialog.close() is called.

History:

Event object properties: none

Note: While there are no custom properties, the event is fired from the dialog's body, thus the target property will refer to its body element—i.e., #ui-dialog-body.

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':dialogclosing', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function exactly once. */
$(document).one(':dialogclosing', function (ev) {
	/* JavaScript code */
});

 :dialogopened event

Global event triggered as the last step in opening the dialog when Dialog.open() is called.

History:

Event object properties: none

Note: While there are no custom properties, the event is fired from the dialog's body, thus the target property will refer to its body element—i.e., #ui-dialog-body.

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':dialogopened', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function exactly once. */
$(document).one(':dialogopened', function (ev) {
	/* JavaScript code */
});

 :dialogopening event

Global event triggered as the first step in opening the dialog when Dialog.open() is called.

History:

Event object properties: none

Note: While there are no custom properties, the event is fired from the dialog's body, thus the target property will refer to its body element—i.e., #ui-dialog-body.

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':dialogopening', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function exactly once. */
$(document).one(':dialogopening', function (ev) {
	/* JavaScript code */
});

 Navigation Events

Navigation events allow the execution of JavaScript code at specific points during passage navigation.

In order of processing: (for reference, this also shows tasks and various special passages)

  1. Passage init. Happens before the modification of the state history.
    1. :passageinit event.
  2. Passage start. Happens before the rendering of the incoming passage.
    1. PassageReady special passage.
    2. :passagestart event.
    3. PassageHeader special passage.
  3. Passage render. Happens after the rendering of the incoming passage.
    1. PassageFooter special passage.
    2. :passagerender event.
  4. Passage display. Happens after the display—i.e., output—of the incoming passage.
    1. PassageDone special passage.
    2. :passagedisplay event.
  5. UI bar special passages update. Happens before the end of passage navigation.
    1. StoryBanner special passage.
    2. StoryDisplayTitle special passage.
    3. StorySubtitle special passage.
    4. StoryAuthor special passage.
    5. StoryCaption special passage.
    6. StoryMenu special passage.
  6. Passage end. Happens at the end of passage navigation.
    1. :passageend event.

 :passageinit event

Triggered before the modification of the state history.

History:

Event detail object properties:

:passageinit events have a detail property whose value is an object with the following properties:

Examples:

/* Execute the handler function each time the event triggers. */
$(document).on(':passageinit', function (ev) {
	/* Log details about the current moment. */
	console.group('Details about the current moment');
	console.log('passage name:', ev.detail.passage.name);
	console.log('passage tags:', ev.detail.passage.tags);
	console.groupEnd();

	/* Do something useful here. */
});

/* Execute the handler function exactly once. */
$(document).one(':passageinit', function (ev) {
	/* Do something useful here. */
});

 :passagestart event

Triggered before the rendering of the incoming passage.

History:

Event detail object properties:

:passagestart events have a detail property whose value is an object with the following properties:

Examples:

Basic usage
/* Execute the handler function each time the event triggers. */
$(document).on(':passagestart', function (ev) {
	/* Log details about the current moment. */
	console.group('Details about the current moment');
	console.log('buffer:', ev.detail.content);
	console.log('passage name:', ev.detail.passage.name);
	console.log('passage tags:', ev.detail.passage.tags);
	console.groupEnd();

	/* Do something useful here. */
});

/* Execute the handler function exactly once. */
$(document).one(':passagestart', function (ev) {
	/* Do something useful here. */
});
Modifying the content buffer
/*
	Process the given markup and append the result to the incoming
	passage's element.
*/
$(document).on(':passagestart', function (ev) {
	$(ev.detail.content).wiki("In the //beginning//.");
});

 :passagerender event

Triggered after the rendering of the incoming passage.

History:

Event detail object properties:

:passagerender events have a detail property whose value is an object with the following properties:

Examples:

Basic usage
/* Execute the handler function each time the event triggers. */
$(document).on(':passagerender', function (ev) {
	/* Log details about the current moment. */
	console.group('Details about the current moment');
	console.log('buffer:', ev.detail.content);
	console.log('passage name:', ev.detail.passage.name);
	console.log('passage tags:', ev.detail.passage.tags);
	console.groupEnd();

	/* Do something useful here. */
});

/* Execute the handler function exactly once. */
$(document).one(':passagerender', function (ev) {
	/* Do something useful here. */
});
Modifying the content buffer
/*
	Process the given markup and append the result to the incoming
	passage's element.
*/
$(document).on(':passagerender', function (ev) {
	$(ev.detail.content).wiki("At the //end// of some renderings.");
});

 :passagedisplay event

Triggered after the display—i.e., output—of the incoming passage.

History:

Event detail object properties:

:passagedisplay events have a detail property whose value is an object with the following properties:

Examples:

Basic usage
/* Execute the handler function each time the event triggers. */
$(document).on(':passagedisplay', function (ev) {
	/* Log details about the current moment. */
	console.group('Details about the current moment');
	console.log('buffer:', ev.detail.content);
	console.log('passage name:', ev.detail.passage.name);
	console.log('passage tags:', ev.detail.passage.tags);
	console.groupEnd();

	/* Do something useful here. */
});

/* Execute the handler function exactly once. */
$(document).one(':passagedisplay', function (ev) {
	/* Do something useful here. */
});
Modifying the content buffer
/*
	Process the given markup and append the result to the incoming
	passage's element.
*/
$(document).on(':passagedisplay', function (ev) {
	$(ev.detail.content).wiki("It's //showtime//!");
});

 :passageend event

Triggered at the end of passage navigation.

History:

Event detail object properties:

:passageend events have a detail property whose value is an object with the following properties:

Examples:

Basic usage
/* Execute the handler function each time the event triggers. */
$(document).on(':passageend', function (ev) {
	/* Log details about the current moment. */
	console.group('Details about the current moment');
	console.log('buffer:', ev.detail.content);
	console.log('passage name:', ev.detail.passage.name);
	console.log('passage tags:', ev.detail.passage.tags);
	console.groupEnd();

	/* Do something useful here. */
});

/* Execute the handler function exactly once. */
$(document).one(':passageend', function (ev) {
	/* Do something useful here. */
});
Modifying the content buffer
/*
	Process the given markup and append the result to the incoming
	passage's element.
*/
$(document).on(':passageend', function (ev) {
	$(ev.detail.content).wiki("So long and //thanks for all the fish//!");
});

 prehistory tasks

Deprecated: prehistory tasks have been deprecated and should no longer be used. See the :passageinit event for its replacement.

History:

 predisplay tasks

Deprecated: predisplay tasks have been deprecated and should no longer be used. See the :passagestart event for its replacement.

History:

 prerender tasks

Deprecated: prerender tasks have been deprecated and should no longer be used. See the :passagestart event for its replacement.

History:

 postrender tasks

Deprecated: postrender tasks have been deprecated and should no longer be used. See the :passagerender event for its replacement.

History:

 postdisplay tasks

Deprecated: postdisplay tasks have been deprecated and should no longer be used. See the :passagedisplay event for its replacement.

History:

 SimpleAudio Events

SimpleAudio events allow the execution of JavaScript code at specific points during audio playback.

See: To add or remove event listeners to audio tracks managed by the SimpleAudio API see:

 :faded event

Track event triggered when a fade completes normally.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers for one track via <AudioTrack>. */
aTrack.on(':faded', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function when the event triggers for multiple tracks via <AudioRunner>. */
someTracks.on(':faded', function (ev) {
	/* do something */
});

 :fading event

Track event triggered when a fade starts.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers for one track via <AudioTrack>. */
aTrack.on(':fading', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function when the event triggers for multiple tracks via <AudioRunner>. */
someTracks.on(':fading', function (ev) {
	/* do something */
});

 :stopped event

Track event triggered when playback is stopped after <AudioTrack>.stop() or <AudioRunner>.stop() is called—either manually or as part of another process.

See Also: ended and pause for information on somewhat similar native events.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers for one track via <AudioTrack>. */
aTrack.on(':stopped', function (ev) {
	/* JavaScript code */
});

/* Execute the handler function when the event triggers for multiple tracks via <AudioRunner>. */
someTracks.on(':stopped', function (ev) {
	/* do something */
});

 System Events

System events allow the execution of JavaScript code at specific points during story startup and teardown.

 :enginerestart event

Global event triggered once just before the page is reloaded when Engine.restart() is called.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers. */
$(document).one(':enginerestart', function (ev) {
	/* JavaScript code */
});

 :storyready event

Global event triggered once just before the dismissal of the loading screen at startup.

History:

Event object properties: none

Examples:

/* Execute the handler function exactly once, since it's only fired once. */
$(document).one(':storyready', function (ev) {
	/* JavaScript code */
});

 :uiupdate event

Global event triggered when the built-in user interface is being updated.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':uiupdate', function (ev) {
	/* JavaScript code */
});

 <<type>> Events

<<type>> macro events allow the execution of JavaScript code at specific points during typing.

 :typingcomplete event

Global event triggered when all <<type>> macros within a passage have completed.

Note: Injecting additional <<type>> macro invocations after a :typingcomplete event has been fired will cause another event to eventually be generated, since you're creating a new sequence of typing.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':typingcomplete', function (ev) {
	/* JavaScript code */
});

 :typingstart event

Local event triggered on the typing wrapper when the typing of a section starts.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':typingstart', function (ev) {
	/* JavaScript code */
});

 :typingstop event

Local event triggered on the typing wrapper when the typing of a section stops.

History:

Event object properties: none

Examples:

/* Execute the handler function when the event triggers. */
$(document).on(':typingstop', function (ev) {
	/* JavaScript code */
});

 Config API

The Config object controls various aspects of SugarCube's behavior.

Note: Config object settings should be placed within your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage).

 General Settings

 Config.addVisitedLinkClassboolean (default: false)

Determines whether the link-visited class is added to internal passage links that go to previously visited passages—i.e., the passage already exists within the story history.

Note: You must provide your own styling for the link-visited class as none is provided by default.

History:

Examples:

Config.addVisitedLinkClass = true;

CSS styles:

You will also need to specify a .link-visited style that defines the properties visited links should have. For example:

.link-visited {
	color: purple;
}

 Config.cleanupWikifierOutputboolean (default: false)

Determines whether the output of the Wikifier is post-processed into more sane markup—i.e., where appropriate, it tries to transition the plethora of <br> elements into <p> elements.

History:

Examples:

Config.cleanupWikifierOutput = true;

 Config.debugboolean (default: false)

Indicates whether SugarCube is running in test mode, which enables debug views and various optional debugging errors and warnings. See the Test Mode guide for more information.

Note: This setting is automatically set based on whether you're using a testing mode in a Twine compiler—i.e., Test mode in Twine 2, Test Play From Here in Twine 1, or the test mode option (-t, --test) in Tweego. You may, however, forcibly enable it if you need to for some reason—e.g., if you're using another compiler, which doesn't offer a way to enable test mode.

See Also: Config.enableOptionalDebugging setting.

History:

Examples:

Forcibly enabling test mode
// Forcibly enable test mode
Config.debug = true;
Check if test mode is enabled via JavaScript
if (Config.debug) {
	/* do something debug related */
}
Check if test mode is enabled via macros
<<if Config.debug>>
	/* do something debug related */
<</if>>

 Config.enableOptionalDebuggingboolean (default: false)

Determines whether various optional debugging errors and warnings are enabled outside of test mode.

See Also: Config.debug setting.

List of optional errors and warnings: (not exhaustive)

History:

Examples:

Config.enableOptionalDebugging = true;

 Config.loadDelayinteger (default: 0)

Sets the integer delay (in milliseconds) before the loading screen is dismissed, once the document has signaled its readiness. Not generally necessary, however, some browsers render slower than others and may need a little extra time to get a media-heavy page done. This allows you to fine tune for those cases.

History:

Examples:

// Delay the dismissal of the loading screen by 2000ms (2s)
Config.loadDelay = 2000;

 Audio Settings

 Config.audio.pauseOnFadeToZeroboolean (default: true)

Determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Examples:

Config.audio.pauseOnFadeToZero = false;

 Config.audio.preloadMetadataboolean (default: true)

Determines whether the audio subsystem attempts to preload track metadata—meaning information about the track (e.g., duration), not its audio frames.

Note: It is unlikely that you will ever want to disable this setting.

History:

Examples:

Config.audio.preloadMetadata = false;

 History Settings

 Config.history.controlsboolean (default: true)

Determines whether the story's history controls (Backward, Jump To, & Forward buttons) are enabled within the UI bar.

History:

Examples:

Config.history.controls = false;

 Config.history.maxStatesinteger (default: 40)

Sets the maximum number of states (moments) to which the history is allowed to grow. Should the history exceed the limit, states will be dropped from the past (oldest first).

Tip: For game-oriented projects, as opposed to more story-oriented interactive fiction, a setting of 1 is strongly recommended.

History:

Examples:

// Limit the history to a single state (recommended for games)
Config.history.maxStates = 1;

// Limit the history to 25 states
Config.history.maxStates = 25;

 Macros Settings

 Config.macros.maxLoopIterationsinteger (default: 1000)

Sets the maximum number of iterations allowed before the <<for>> macro conditional forms are terminated with an error.

Note: This setting exists to prevent a misconfigured loop from making the browser unresponsive.

History:

Examples:

// Allow only 5000 iterations
Config.macros.maxLoopIterations = 5000;

 Config.macros.typeSkipKeystring (default: " ", space)

Sets the default KeyboardEvent.key value that causes the currently running <<type>> macro instance to finish typing its content immediately.

History:

Examples:

// Change the default skip key to Control (CTRL)
Config.macros.typeSkipKey = "Control";

 Config.macros.typeVisitedPassagesboolean (default: true)

Determines whether the <<type>> macro types out content on previously visited passages or simply outputs it immediately.

History:

Examples:

// Do not type on previously visited passages
Config.macros.typeVisitedPassages = false;

 Config.macros.ifAssignmentErrorboolean (default: true)

Deprecated: This setting has been deprecated and should no longer be used. See the Config.enableOptionalDebugging setting for its replacement.

History:

 Navigation Settings

 Config.navigation.overridefunction (default: none)

Allows the destination of passage navigation to be overridden. The callback is passed one parameter, the original destination passage title. If its return value is falsy, the override is cancelled and navigation to the original destination continues unperturbed. If its return value is truthy, the override succeeds and that value is used as the new destination of the navigation.

History:

Examples:

Config.navigation.override = function (destinationPassage) {
	/* code that returns a passage name or a falsy value */
};
Based upon a story variable
// Force the player to the "You Died" passage if they let $health get too low.
Config.navigation.override = function (dest) {
	var sv = State.variables;

	// If $health is less-than-or-equal to 0, go to the "You Died" passage instead.
	if (sv.health <= 0) {
		return "You Died";
	}
};

 Passages Settings

 Config.passages.displayTitlesboolean (default: false)

Determines whether passage titles are combined with the story title, within the browser's/tab's titlebar, when passages are displayed.

History:

Examples:

Config.passages.displayTitles = true;

 Config.passages.nobrboolean (default: false)

Determines whether rendering passages have their leading/trailing newlines removed and all remaining sequences of newlines replaced with single spaces before they're rendered. Equivalent to including the nobr special tag on every passage.

Note: Does not affect script or stylesheet tagged passages, for Twine 1/Twee, or the Story JavaScript or Story Stylesheet sections, for Twine 2.

History:

Examples:

Config.passages.nobr = true;

 Config.passages.onProcessfunction (default: none)

Allows custom processing of passage text. The function is invoked each time the <Passage>.processText() method is called. It is passed an abbreviated version of the associated passage's Passage instance—containing only the tags, text, and title properties. Its return value should be the post-processed text.

Note: Does not affect script or stylesheet tagged passages, for Twine 1/Twee, or the Story JavaScript or Story Stylesheet sections, for Twine 2.

Note: The function will be called just before the built-in no-break passage processing if you're also using that—see the Config.passages.nobr setting and nobr special tag.

History:

Examples:

// Change instancess of "cat" to "dog"
Config.passages.onProcess = function (p) {
	return p.text.replace(/\bcat(s?)\b/g, "dog$1");
};

 Config.passages.startstring (Twine 2 default: user-selected; Twine 1/Twee default: "Start")

Sets the starting passage, the very first passage that will be displayed.

History:

Examples:

Config.passages.start = "That Other Starting Passage";

 Config.passages.transitionOutstring | integer (default: none)

Determines whether outgoing passage transitions are enabled. Valid values are the name of the property being animated, which causes the outgoing passage element to be removed once that transition animation is complete, or an integer delay (in milliseconds), which causes the outgoing passage element to be removed once the delay has expired. You will also need some CSS styles to make this work—examples given below.

Note: If using an integer delay, ideally, it should probably be slightly longer than the outgoing transition delay that you intend to use—e.g., an additional 10ms or so should be sufficient.

History:

Examples:

// Remove outgoing elements when their opacity animation ends
Config.passages.transitionOut = "opacity";

// Remove outgoing elements after 1010ms (1.01s)
Config.passages.transitionOut = 1010;

CSS styles:

At the very least you will need to specify a .passage-out style that defines the transition's end state. For example:

.passage-out {
	opacity: 0;
}

That probably won't be very pleasing to the eye, however, so you will likely need several styles to make something that looks half-decent. For example, the following will give you a basic crossfade:

#passages {
	position: relative;
}
.passage {
	left: 0;
	position: absolute;
	top: 0;
	transition: opacity 1s ease;
}
.passage-out {
	opacity: 0;
}

 Config.passages.descriptionsboolean | object | function (default: none)

Deprecated: This setting has been deprecated and should no longer be used. See the Config.saves.descriptions setting for its replacement.

History:

 Saves Settings

 Config.saves.descriptionsfunction (default: none)

Sets browser saves descriptions. If unset, a brief description of the current turn is used. If a callback function is assigned, it is passed one parameter, the type of save being attempted. If its return value is truthy, the returned description is used, elsewise the default description is used.

See: Save.Type pseudo-enumeration for more information on save types.

History:

Examples:

Using passages' names
Config.saves.descriptions = function (saveType) {
	return passage();
};
Using descriptions mapped by passages' names
var saveDescriptions = {
	"passage_title_a" : "description text a…",
	"passage_title_b" : "description text b…",
	"passage_title_c" : "description text c…"
};
Config.saves.descriptions = function (saveType) {
	return saveDescriptions[passage()];
};
Using the provided save type
Config.saves.descriptions = function (saveType) {
	const base = `(${L10n.get("turn")} ${State.turns})`;

	switch (saveType) {
		case Save.Type.Auto:
			return `${base} A browser auto save…`;
		case Save.Type.Base64:
			return `${base} A base64 save…`;
		case Save.Type.Disk:
			return `${base} A local disk save…`;
		case Save.Type.Slot:
			return `${base} A browser slot save…`;
	}
};

 Config.saves.idstring (default: slugified story title)

Sets the story ID associated with saves.

History:

Examples:

Config.saves.id = "a-big-huge-story-part-1";

 Config.saves.isAllowedfunction (default: none)

Determines whether saving is allowed within the current context. If unset, saves are always allowed. If a callback function is assigned, it is passed one parameter, the type of save being attempted. If its return value is truthy, the save is allowed, elsewise it is disallowed.

See: Save.Type pseudo-enumeration for more information on save types.

History:

Examples:

Basic usage

Allows saves on passages if it returns a truthy value.

Config.saves.isAllowed = function (saveType) {
	/* code returning a boolean value */
};

Disallow saving on passages tagged with menu.

Config.saves.isAllowed = function (saveType) {
	return !tags().includes("menu");
};
Using the save type parameter

Attempt a new auto save only on passages tagged with autosave. Other save types are not limited.

Config.saves.isAllowed = function (saveType) {
	if (saveType === Save.Type.Auto) {
		return tags().includes("autosave");
	}

	return true;
};

Attempt a new auto save only on every eighth turn and limit all other save types to passages tagged with cansave.

// Using an `if` statement
Config.saves.isAllowed = function (saveType) {
	if (saveType === Save.Type.Auto) {
		return turns() % 8 === 0;
	}

	return tags().includes("cansave");
};

Different logic for most save types.

Note: For example purposes only, not really recommended.

Config.saves.isAllowed = function (saveType) {
	switch (saveType) {
		case Save.Type.Auto:
			// Only every tenth turn
			return turns() % 10 === 0;

		case Save.Type.Disk:
		case Save.Type.Slot:
			// Only on passages tagged `cansave`
			return tags().includes("cansave");

		case Save.Type.Base64:
			// Always
			return true;
	}
};

 Config.saves.maxAutoSaves integer (default: 0)

Sets the maximum number of available auto saves. Using a value of 0 disables auto saves.

Note: When enabled, an auto save is attempted each turn by default. Thus, it is recommended that the Config.saves.isAllowed setting be used to limit the frequency.

Warning: As available browser-based storage is very limited, it is strongly recommended that the number of available saves not be set too high. A range of 110 is suggested.

History:

Examples:

Config.saves.maxAutoSaves = 3;

 Config.saves.maxSlotSaves integer (default: 8)

Sets the maximum number of available slot saves. Using a value of 0 disables slot saves.

Warning: As available browser-based storage is very limited, it is strongly recommended that the number of available saves not be set too high. A range of 110 is suggested.

History:

Examples:

Config.saves.maxSlotSaves = 4;

 Config.saves.versionany (default: none)

Sets the version property of saves.

Note: This setting is only used to set the version property of saves. Thus, it is only truly useful if you plan to upgrade out-of-date saves via the Save Events API—specifically the Save.onLoad.add() static method.

History:

Examples:

// As an integer (recommended)
Config.saves.version = 3;

// As a string (strongly not recommended)
Config.saves.version = "v3";

 Config.saves.autoloadboolean | string | function (default: none)

Deprecated: This setting has been deprecated and should no longer be used. The default UI now includes a Continue button, which loads the latest save. If disabling or replacing the default UI, see the Save.browser.continue() method to replicate the functionality.

History:

 Config.saves.autosaveboolean | Array<string> | function (default: none)

Deprecated: This setting has been deprecated and should no longer be used. See the Config.saves.maxAutoSaves setting to set the number of available auto saves and the Config.saves.isAllowed setting to control when new auto saves are created.

History:

 Config.saves.onLoadfunction (default: none)

Deprecated: This setting has been deprecated and should no longer be used. See the Save.onLoad.add() method for its replacement.

History:

 Config.saves.onSavefunction (default: none)

Deprecated: This setting has been deprecated and should no longer be used. See the Save.onSave.add() method for its replacement.

History:

 Config.saves.slots integer (default: 8)

Deprecated: This setting has been deprecated and should no longer be used. See the Config.saves.maxSlotSaves setting for its replacement.

History:

 Config.saves.tryDiskOnMobileboolean (default: true)

Deprecated: This setting has been deprecated and should no longer be used. Saving to disk on mobile devices is now unconditionally enabled.

History:

 UI Settings

 Config.ui.stowBarInitiallyboolean | integer (default: 800)

Determines whether the UI bar (sidebar) starts in the stowed (shut) state initially. Valid values are boolean true/false, which causes the UI bar to always/never start in the stowed state, or an integer, which causes the UI bar to start in the stowed state if the viewport width is less-than-or-equal-to the specified number of pixels.

History:

Examples:

// As a boolean; always start stowed
Config.ui.stowBarInitially = true;

// As a boolean; never start stowed
Config.ui.stowBarInitially = false;

// As an integer; start stowed if the viewport is 800px or less
Config.ui.stowBarInitially = 800;

 Config.ui.updateStoryElementsboolean (default: true)

Determines whether certain elements within the UI bar are updated when passages are displayed. The affected elements are the story: banner, subtitle, author, caption, and menu.

Note: The story title is not included in updates because SugarCube uses it as the basis for the key used to store and load data used when playing the story and for saves.

History:

Examples:

// If you don't need those elements to update
Config.ui.updateStoryElements = false;

 Dialog API

 Dialog.append(content)Dialog object

Appends the given content to the dialog's content area. Returns a reference to the Dialog object for chaining.

Note: If your content contains any SugarCube markup, you'll need to use the Dialog.wiki() method instead.

History:

Parameters:

Examples:

Dialog.append("Cry 'Havoc!', and let slip the <em>ponies</em> of <strong>friendship</strong>.");

Dialog.append( /* DOM nodes */ );

 Dialog.body()HTMLElement object

Returns a reference to the dialog's content area.

History:

Parameters: none

Examples:

jQuery(Dialog.body())
	.append("Cry 'Havoc!', and let slip the <em>ponies</em> of <strong>friendship</strong>.");

jQuery(Dialog.body())
	.wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");

 Dialog.close()Dialog object

Closes the dialog. Returns a reference to the Dialog object for chaining.

History:

Parameters: none

Examples:

Dialog.close();

 Dialog.create([title [, classNames]])Dialog object

Prepares the dialog for use. Returns a reference to the Dialog object for chaining.

History:

Parameters:

Examples:

Basic usage
Dialog.create();
With a title
Dialog.create("Character Sheet");
With a title and class
Dialog.create("Character Sheet", "charsheet");
Making use of chaining
Dialog
	.create("Character Sheet", "charsheet")
	.wikiPassage("Player Character")
	.open();

 Dialog.empty()HTMLElement object

Empties the dialog's content area. Returns a reference to the Dialog object for chaining.

History:

Parameters: none

Examples:

Basic usage
Dialog.empty();
Replacing the open dialog's content
Dialog
	.empty()
	.wikiPassage("Quests");

 Dialog.isOpen([classNames])boolean

Returns whether the dialog is currently open.

History:

Parameters:

Examples:

Basic usage
if (Dialog.isOpen()) {
	/* code to execute if the dialog is open… */
}
While also checking if the saves class exists
if (Dialog.isOpen("saves")) {
	/* code to execute if the Saves dialog is open… */
}

 Dialog.open([options [, closeFn]])Dialog object

Opens the dialog. Returns a reference to the Dialog object for chaining.

Note: Call this only after populating the dialog with content.

History:

Parameters:

Options object:

An options object should have some of the following properties:

Examples:

Dialog.open();

 Dialog.wiki(wikiMarkup)Dialog object

Renders the given markup and appends it to the dialog's content area. Returns a reference to the Dialog object for chaining.

Note: If you simply want to render a passage, see the Dialog.wikiPassage() method instead.

Warning: If your content consists of DOM nodes, you'll need to use the Dialog.append() method instead.

History:

Parameters:

Examples:

Dialog.wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");

 Dialog.wikiPassage(name)Dialog object

Renders the passage by the given name and appends it to the dialog's content area. Returns a reference to the Dialog object for chaining.

History:

Parameters:

Examples:

Dialog.wikiPassage("Inventory");

 Dialog.setup([title [, classNames]])HTMLElement object

Deprecated: This method has been deprecated and should no longer be used.

History:

 Engine API

 Engine.lastPlaynumber

Returns a timestamp representing the last time Engine.play() was called.

History:

Examples:

Engine.lastPlay  → The timestamp at which Engine.play() was last called

 Engine.statestring

Returns the current state of the engine ("idle", "playing", "rendering").

History:

States:

Examples:

Engine.state  → Returns the current state of the engine

 Engine.backward()boolean

Moves backward one moment within the full history (past + future), if possible, activating and showing the moment moved to. Returns whether the history navigation was successful (should only fail if already at the beginning of the full history).

History:

Parameters: none

Examples:

Engine.backward()  → Rewinds the full history by one moment—i.e., undoes the moment

 Engine.forward()boolean

Moves forward one moment within the full history (past + future), if possible, activating and showing the moment moved to. Returns whether the history navigation was successful (should only fail if already at the end of the full history).

History:

Parameters: none

Examples:

Engine.forward()  → Fast forwards the full history by one moment—i.e., redoes the moment

 Engine.go(offset)boolean

Activates the moment at the given offset from the active (present) moment within the full state history and show it. Returns whether the history navigation was successful (should only fail if the offset from the active (present) moment is not within the bounds of the full history).

History:

Parameters:

Examples:

Engine.go(2)   → Fast forwards the full history by two moments—i.e., redoes the moments
Engine.go(-4)  → Rewinds the full history by four moments—i.e., undoes the moments

 Engine.goTo(index)boolean

Activates the moment at the given index within the full state history and show it. Returns whether the history navigation was successful (should only fail if the index is not within the bounds of the full history).

History:

Parameters:

Examples:

Engine.goTo(0)  → Goes to the first moment
Engine.goTo(9)  → Goes to the tenth moment

 Engine.isIdle()boolean

Returns whether the engine is idle.

History:

Parameters: none

Examples:

Engine.isIdle()  → Returns whether the engine is idle

 Engine.isPlaying()boolean

Returns whether the engine is processing a turn—i.e., passage navigation has been triggered.

History:

Parameters: none

Examples:

Engine.isPlaying()  → Returns whether the engine is playing

 Engine.isRendering()boolean

Returns whether the engine is rendering the incoming passage.

History:

Parameters: none

Examples:

Engine.isRendering()  → Returns whether the engine is rendering

 Engine.play(passageTitle [, noHistory])HTMLElement object

Renders and displays the passage referenced by the given title, optionally without adding a new moment to the history.

History:

Parameters:

Examples:

Engine.play("Foo")        → Renders, displays, and adds a moment for the passage "Foo" to the history
Engine.play("Foo", true)  → Renders and displays the passage "Foo", but does not add new history

 Engine.restart()

Causes the browser to immediately attempt to reload the window, thus restarting the story.

Warning: The player will not be prompted and all unsaved state will be lost.

Note: In general, you should not call this method directly. Instead, call the UI.restart() static method, which prompts the player with an OK/Cancel dialog before itself calling Engine.restart(), if they accept.

History:

Parameters: none

Examples:

Engine.restart()  → Restarts the story

 Engine.show()HTMLElement object

Renders and displays the active (present) moment's associated passage without adding a new moment to the history.

History:

Parameters: none

Examples:

Engine.show()  → Renders and displays the present passage without adding new history

 Fullscreen API

Provides access to browsers' fullscreen functionality.

 Backgrounds in fullscreen

If you wish to use custom backgrounds, either simply colors or with images, then you should place them on the body element. For example:

body {
	background: #111 fixed url("images/background.png") center / contain no-repeat;
}

Warning: It is strongly recommended that you do not place background properties on the html element in addition to the body element as this can cause background jitter in Internet Explorer when scrolling outside of fullscreen mode.

Warning: If setting a background image via the background shorthand property, then you should also specify a background-color value with it or include a separate background-color property after the background property. The reason being is that the background property resets the background color, so if you do not set one either as one of its values or via a following background-color property, then the browser's default background color could show through if the background image does not cover the entire viewport or includes transparency.

 Fullscreen limitations

The Fullscreen API comes with some built-in limitations:

  1. Fullscreen requests must be initiated by the player, generally via click/touch—i.e., the request must be made as a result of player interaction; e.g., activating a button/link/etc whose code makes the request.

 Fullscreen.elementHTMLElement object | null

Returns the current fullscreen element or, if fullscreen mode is not active, null.

History:

Examples:

Fullscreen.element  → The current fullscreen element

 Fullscreen.isEnabled()boolean

Returns whether fullscreen is both supported and enabled.

History:

Parameters: none

Examples:

Fullscreen.isEnabled()  → Whether fullscreen mode is available

 Fullscreen.isFullscreen()boolean

Returns whether fullscreen mode is currently active.

History:

Parameters: none

Examples:

Fullscreen.isFullscreen()  → Whether fullscreen mode is active

 Fullscreen.request([options [, requestedEl]])Promise object

Request that the browser enter fullscreen mode.

See: Backgrounds and limitations.

History:

Parameters:

Options object:

A fullscreen options object should have some of the following properties:

Note: Browsers are not currently required to honor the navigationUI setting.

Examples:

Basic usage (recommended)
/* Request to enter fullscreen mode. */
Fullscreen.request();
With options and a specified element
/* Request to enter fullscreen mode while showing its navigation UI and with the given element. */
Fullscreen.request({ navigationUI : "show" }, myElement);

 Fullscreen.exit()Promise object

Request that the browser exit fullscreen mode.

History:

Parameters: none

Examples:

/* Request to exit fullscreen mode. */
Fullscreen.exit();

 Fullscreen.toggle([options [, requestedEl]])Promise object

Request that the browser toggle fullscreen mode—i.e., enter or exit as appropriate.

History:

Parameters:

Examples:

Basic usage (recommended)
/* Request to toggle fullscreen mode. */
Fullscreen.toggle();
With options and a specified element
/* Request to toggle fullscreen mode while showing its navigation UI and with the given element. */
Fullscreen.toggle({ navigationUI : "show" }, myElement);

 Fullscreen.onChange(handlerFn [, requestedEl])

Attaches fullscreen change event handlers.

History:

Parameters:

Examples:

Basic usage (recommended)
/* Attach a hander to listen for fullscreen change events. */
Fullscreen.onChange(function (ev) {
	/* Fullscreen mode changed, so do something. */
});
With a specified element
/* Attach a hander to the given element to listen for fullscreen change events. */
Fullscreen.onChange(function (ev) {
	/* Fullscreen mode changed on myElement, so do something. */
}, myElement);

 Fullscreen.offChange([handlerFn [, requestedEl]])

Removes fullscreen change event handlers.

History:

Parameters:

Examples:

Basic usage (recommended)
/* Remove all fullscreen change event handers. */
Fullscreen.offChange();
/* Remove the given fullscreen change event hander. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offChange(originalHandlerFn);
With a specified element
/* Remove all fullscreen change event handers from myElement. */
Fullscreen.offChange(null, myElement);
/* Remove the given fullscreen change event hander from myElement. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offChange(originalHandlerFn, myElement);

 Fullscreen.onError(handlerFn [, requestedEl])

Attaches fullscreen error event handlers.

History:

Parameters:

Examples:

Basic usage (recommended)
/* Attach a hander to listen for fullscreen error events. */
Fullscreen.onError(function (ev) {
	/* Fullscreen mode changed, so do something. */
});
With a specified element
/* Attach a hander to the given element to listen for fullscreen error events. */
Fullscreen.onError(function (ev) {
	/* Fullscreen mode changed on myElement, so do something. */
}, myElement);

 Fullscreen.offError([handlerFn [, requestedEl]])

Removes fullscreen error event handlers.

History:

Parameters:

Examples:

Basic usage (recommended)
/* Remove all fullscreen error event handers. */
Fullscreen.offError();
/* Remove the given fullscreen error event hander. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offError(originalHandlerFn);
With a specified element
/* Remove all fullscreen error event handers from myElement. */
Fullscreen.offError(null, myElement);
/* Remove the given fullscreen error event hander from myElement. */
/* NOTE: Requires that the original handler function was saved. */
Fullscreen.offError(originalHandlerFn, myElement);

 LoadScreen API

Note: To simply add a delay to the dismissal of the loading screen to hide initial flashes of unstyled content (FOUC)—e.g., style changes and page reflows—you do not need to use this API. See the Config.loadDelay configuration setting.

 LoadScreen.lock()number

Acquire a loading screen lock and, if necessary, display the loading screen.

History:

Parameters: none

Returns:

The (integer) lock ID.

Examples:

See the LoadScreen.unlock() static method for additional examples.

// Lock the loading screen and get the lock ID.
var lockId = LoadScreen.lock();

 LoadScreen.unlock(lockId)

Release the loading screen lock with the given ID and, if no other locks exist, hide the loading screen.

History:

Parameters:

Returns: none

Examples:

// Lock the loading screen and get the lock ID.
var lockId = LoadScreen.lock();

// Do something whose timing is unpredictable that should be hidden by the loading screen.

// Release the given lock ID.
LoadScreen.unlock(lockId);

 Macro API

See Also: MacroContext API.

 Macro.add(name , definition)

Add new macro(s).

History:

Parameters:

Definition object:

A macro definition object should have some of the following properties (only handler is absolutely required):

Additional properties may be added for internal use.

Examples:

/*
	Example of a very simple/naive <<if>>/<<elseif>>/<<else>> macro implementation.
*/
Macro.add('if', {
	skipArgs : true,
	tags     : ['elseif', 'else'],
	handler  : function () {
		try {
			for (var i = 0, len = this.payload.length; i < len; ++i) {
				if (
					this.payload[i].name === 'else' ||
					!!Scripting.evalJavaScript(this.payload[i].args.full)
				) {
					jQuery(this.output).wiki(this.payload[i].contents);
					break;
				}
			}
		}
		catch (ex) {
			return this.error('bad conditional expression: ' + ex.message);
		}
	}
});

 Macro.delete(name)

Remove existing macro(s).

History:

Parameters:

Examples:

Macro.delete("amacro")
Macro.delete(["amacro", "bmacro"])

 Macro.get(name)object

Return the named macro definition, or null on failure.

History:

Parameters:

Examples:

Macro.get("print")

 Macro.has(name)boolean

Returns whether the named macro exists.

History:

Parameters:

Examples:

Macro.has("print")

 Macro.tags.get(name)Array<string>

Return the named macro tag's parents array (includes the names of all macros who have registered the tag as a child), or null on failure.

History:

Parameters:

Examples:

Macro.tags.get("else")  → For the standard library, returns: ["if"]

 Macro.tags.has(name)boolean

Returns whether the named macro tag exists.

History:

Parameters:

Examples:

Macro.tags.has("else")

 MacroContext API

See Also: Macro API.

Macro handlers are called with no arguments, but with their this set to a macro (execution) context object. Macro context objects contain the following data and method properties.

 <MacroContext>.argsArray<any>

An array of discrete arguments parsed from the argument string.

History:

Examples:

// Given: <<someMacro "a" "b" "c">>
this.args.length  // Returns 3
this.args[0]      // Returns 'a'
this.args[1]      // Returns 'b'
this.args[2]      // Returns 'c'

 <MacroContext>.args.fullstring

The argument string after converting all TwineScript syntax elements into their native JavaScript counterparts.

History:

Examples:

// Given: <<if $a is "b">>
this.args.full  // Returns 'State.variables.a === "b"'

 <MacroContext>.args.rawstring

The unprocessed argument string.

History:

Examples:

// Given: <<if "a" is "b">>
this.args.raw  // Returns '"a" is "b"'

 <MacroContext>.namestring

The name of the macro.

History:

Examples:

// Given: <<someMacro …>>
this.name  // Returns 'someMacro'

 <MacroContext>.outputHTMLElement object

The current output element.

History:

 <MacroContext>.parentnull | object

The (execution) context object of the macro's parent, or null if the macro has no parent.

History:

 <MacroContext>.parserobject

The parser instance that generated the macro call.

History:

 <MacroContext>.payloadnull | Array<object>

The text of a container macro parsed into discrete payload objects by tag. Payload objects have the following properties:

History:

 <MacroContext>.selfobject

The macro's definition—created via Macro.add().

History:

 <MacroContext>.contextFilter(predicate)Array<object>

Returns a new array containing all of the macro's ancestors that passed the test implemented by the given predicate function or an empty array, if no members pass.

History:

Parameters:

Examples:

var isInclude = function (ctx) { return ctx.name === "include"; };
this.contextFilter(isInclude); // Returns an array of all <<include>> macro ancestors

 <MacroContext>.contextFind(predicate)object | undefined

Returns the first of the macro's ancestors that passed the test implemented by the given predicate function or undefined, if no members pass.

History:

Parameters:

Examples:

var isInclude = function (ctx) { return ctx.name === "include"; };
this.contextFind(isInclude); // Returns the first <<include>> macro ancestor

 <MacroContext>.contextSome(predicate)boolean

Returns whether any of the macro's ancestors passed the test implemented by the given predicate function.

History:

Parameters:

Examples:

var isInclude = function (ctx) { return ctx.name === "include"; };
this.contextSome(isInclude);  // Returns true if any ancestor was an <<include>> macro

 <MacroContext>.error(message)boolean:false

Renders the message prefixed with the name of the macro and returns false.

History:

Parameters:

Examples:

// Given: <<someMacro …>>
return this.error("oops"); // Outputs '<<someMacro>>: oops'

 <MacroContext>.shadowHandler(callback [, doneCallback [, startCallback]])function

Returns a callback function that wraps the specified callback functions to provide access to the variable shadowing system used by the <<capture>> macro.

Note: All of the specified callbacks are invoked as the wrapper is invoked—meaning, with their this set to the this of the wrapper and with whatever parameters were passed to the wrapper.

Warning: Only useful when you have an asynchronous callback that invokes code/content that needs to access story and/or temporary variables shadowed by <<capture>>. If you don't know what that means, then this API is likely not for you.

History:

Parameters:

Examples:

Basic usage
$someElement.on('some_event', this.shadowHandler(function (ev) {
	/* main asynchronous code */
}));
With an optional doneCallback
$someElement.on('some_event', this.shadowHandler(
	function (ev) {
		/* main asynchronous code */
	},
	function (ev) {
		/* asynchronous code invoked after the main code */
	}
));
With an optional doneCallback and startCallback
$someElement.on('some_event', this.shadowHandler(
	function (ev) {
		/* main asynchronous code */
	},
	function (ev) {
		/* asynchronous code invoked after the main code */
	},
	function (ev) {
		/* asynchronous code invoked before the main code */
	}
));

 <MacroContext>.contextHas(filter)boolean

Deprecated: This method has been deprecated and should no longer be used. See the <MacroContext>.contextSome() method for its replacement.

History:

 <MacroContext>.contextSelect(filter)null | object

Deprecated: This method has been deprecated and should no longer be used. See the <MacroContext>.contextFind() method for its replacement.

History:

 <MacroContext>.contextSelectAll(filter)Array<object>

Deprecated: This method has been deprecated and should no longer be used. See the <MacroContext>.contextFilter() method for its replacement.

History:

 <MacroContext>.createShadowWrapper(callback [, doneCallback [, startCallback]])function

Deprecated: This method has been deprecated and should no longer be used. See the <MacroContext>.shadowHandler() method for its replacement.

History:

 Passage API

Instances of the Passage object are returned by the Story.get() static method.

All properties of Passage objects should be treated as if they were read-only, as modifying them could result in unexpected behavior.

 <Passage>.idstring

The DOM-compatible ID of the passage, created from the slugified passage title.

History:

 <Passage>.namestring

The name of the passage.

History:

 <Passage>.tagsArray<string>

The tags of the passage.

History:

 <Passage>.textstring

The raw text of the passage.

History:

 <Passage>.processText()string

Returns the processed text of the passage, created from applying nobr tag and image passage processing to its raw text.

History:

Parameters: none

Examples:

var passage = Story.get("The Ducky");

passage.processText()  → Returns the fully processed text of "The Ducky" passage

 <Passage>.domIdstring

Deprecated: This property has been deprecated and should no longer be used. See the <Passage>.id property for its replacement.

History:

 <Passage>.titlestring

Deprecated: This property has been deprecated and should no longer be used. See the <Passage>.name property for its replacement.

History:

 <Passage>.description()string

Deprecated: This method has been deprecated and should no longer be used.

History:

 Save API

Note: There are several configuration settings for saves that it would be wise for you to familiarize yourself with.

Warning: Browser saves—i.e., auto and slot saves—are largely incompatible with private browsing modes, which cause all in-browser storage mechanisms to either persist only for the lifetime of the browsing session or fail outright.

 Save Objects

 Save Data

Note: Adding additional properties directly to save data objects is not recommended. Instead, use the metadata property.

Save Data Object

A save data object has the following properties:

Save State Object

The marshaled story state object, from the state property, has the following properties:

Save History Moment Objects

Each moment object, from the history property's array, has the following properties:

 Save Descriptor

Save descriptor objects are only provided for browser saves and are identical to save data objects save that they do not include a state property

 Constants

 Save.Type

Save types pseudo-enumeration. Used to denote the type of save.

History:

Values:

Type Description
Save.Type.Auto Browser auto saves.
Save.Type.Base64 Base64 string saves.
Save.Type.Disk Disk saves.
Save.Type.Slot Browser slot saves.

 Browser Saves: General

 Save.browser.sizeinteger

The total number of existing browser saves, both auto and slot.

History:

Value:

The integer count of existing browser saves.

Examples:

console.log(`There are currently ${Save.browser.size} browser saves`);
if (Save.browser.size > 0) {
	/* Browser saves exist. */
}
if (Save.browser.size === 0) {
	/* No browser saves exist. */
}

 Save.browser.clear()

Deletes all existing browser saves, both auto and slot.

History:

Parameters: none

Returns: none

Throws: none

Examples:

Save.browser.clear();

 Save.browser.continue()Promise

Loads the most recent browser save, either auto or slot.

Note: The default UI includes a Continue button that makes use of this API. Thus, unless you disable or replace the default UI, players already have access to this functionality.

Warning: Saves cannot be loaded during startup and any attempt to do so will cause an error.

History:

Parameters: none

Returns:

A Promise that simply resolves, or rejects with an error if the save could not be loaded.

Throws: none

Examples:

Basic usage

Load the most recent browser save, only handling failure. This should be sufficient in the majority of cases.

if (Save.browser.size > 0) {
	Save.browser.continue()
		.catch(error => {
			/* Failure.  Handle the error. */
			console.error(error);
			UI.alert(error);
		});
}
else {
	/* No browser saves exist. */
}

Load the most recent browser save, handling both success and failure.

if (Save.browser.size > 0) {
	Save.browser.continue()
		.then(() => {
			/* Success.  Do something special. */
		})
		.catch(error => {
			/* Failure.  Handle the error. */
			console.error(error);
			UI.alert(error);
		});
}
else {
	/* No browser saves exist. */
}

 Save.browser.isEnabled()boolean

Determines whether any browser saves are enabled, either auto, slot, or both.

History:

Parameters: none

Returns:

Boolean true if any browser saves are enabled, elsewise false.

Throws: none

Examples:

if (Save.browser.isEnabled()) {
	/* Some browser saves are enabled. */
}

 Browser Saves: Auto

 Save.browser.auto.sizeinteger

The total number of existing browser auto saves.

History:

Value:

The integer count of existing browser auto saves.

Examples:

console.log(`There are currently ${Save.browser.auto.size} browser auto saves`);
if (Save.browser.auto.size > 0) {
	/* Browser auto saves exist. */
}
if (Save.browser.auto.size === 0) {
	/* No browser auto saves exist. */
}

 Save.browser.auto.clear()

Deletes all existing auto saves.

History:

Parameters: none

Returns: none

Throws: none

Examples:

Save.browser.auto.clear();

 Save.browser.auto.delete(index)

Deletes the auto save at the given index.

History:

Parameters:

Returns: none

Throws:

An Error instance.

Examples:

Delete the auto save at the given index, handling failure.

try {
	Save.browser.auto.delete(index);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Save.browser.auto.entries()Array<object>

Provides an array of details about all auto saves.

History:

Parameters: none

Returns:

An Array of { index, info } generic objects, or an empty Array if no auto saves exist.

Throws:

An Error instance.

Examples:

Save.browser.auto.entries().forEach(function (entry) {
	console.log(`Descriptor of the auto save at index ${entry.index}:`, entry.info);
});

 Save.browser.auto.get(index)object

Details the auto save at the given index.

History:

Parameters:

Returns:

The descriptor object for the auto save if it exists, elsewise null.

Throws:

An Error instance.

Examples:

console.log(`Descriptor of the auto save at index ${index}:`, Save.browser.auto.get(index));

 Save.browser.auto.has(index)boolean

Determines whether the auto save at the given index exists.

History:

Parameters:

Returns:

Boolean true if the auto save exists, elsewise false.

Throws:

An Error instance.

Examples:

if (Save.browser.auto.has(index)) {
	/* The auto save at the given index exists. */
}

 Save.browser.auto.isEnabled()boolean

Determines whether auto saves are enabled.

History:

Parameters: none

Returns:

Boolean true if auto saves are anabled, elsewise false.

Throws: none

Examples:

if (Save.browser.auto.isEnabled()) {
	/* Auto saves are enabled. */
}

 Save.browser.auto.load(index)Promise

Loads the auto save at the given index.

Warning: Saves cannot be loaded during startup and any attempt to do so will cause an error.

History:

Parameters:

Returns: none

A Promise that simply resolves, or rejects with an error if the save could not be loaded.

Throws: none

Examples:

Basic usage

Load the auto save at the given index. This should be sufficient in the majority of cases.

Save.browser.auto.load(index)
	.then(() => {
		Engine.show();
	})
	.catch(error => {
		/* Failure.  Handle the error. */
		console.error(error);
		UI.alert(error);
	});

 Save.browser.auto.save([desc [, metadata]])

Saves an auto save. If all auto save positions are full, replaces the oldest auto save.

History:

Parameters:

Returns: none

Throws:

An Error instance.

Examples:

Basic usage

Save an auto save with the default description and no metadata, handling failure.

try {
	Save.browser.auto.save();
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save an auto save with a description and no metadata, handling failure.

try {
	Save.browser.auto.save("In the wilds");
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save an auto save with the default description and metadata, handling failure.

try {
	const saveMetadata = {
		chars : ['Celes', 'Locke', 'Edward'],
		gold  : 2345
	};
	Save.browser.auto.save(null, saveMetadata);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save an auto save with a description and metadata, handling failure.

try {
	const saveMetadata = {
		chars : ['Celes', 'Locke', 'Edward'],
		gold  : 2345
	};
	Save.browser.auto.save("In the wilds", saveMetadata);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Browser Saves: Slot

 Save.browser.slot.sizeinteger

The total number of existing browser slot saves.

History:

Value:

The integer count of existing browser slot saves.

Examples:

console.log(`There are currently ${Save.browser.slot.size} browser slot saves`);
if (Save.browser.slot.size > 0) {
	/* Browser slot saves exist. */
}
if (Save.browser.slot.size === 0) {
	/* No browser slot saves exist. */
}

 Save.browser.slot.clear()

Deletes all existing slot saves.

History:

Parameters: none

Returns: none

Throws: none

Examples:

Save.browser.slot.clear();

 Save.browser.slot.delete(index)

Deletes the slot save at the given index.

History:

Parameters:

Returns: none

Throws:

An Error instance.

Examples:

Delete the slot save at the given index, handling failure.

try {
	Save.browser.slot.delete(index);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Save.browser.slot.entries()Array<object>

Provides an array of details about all slot saves.

History:

Parameters: none

Returns:

An Array of { index, info } generic objects, or an empty Array if no slot saves exist.

Throws:

An Error instance.

Examples:

Save.browser.slot.entries().forEach(function (entry) {
	console.log(`Descriptor of the slot save at index ${entry.index}:`, entry.info);
});

 Save.browser.slot.get(index)object

Details the slot save at the given index.

History:

Parameters:

Returns:

The descriptor object for the slot save if it exists, elsewise null.

Throws:

An Error instance.

Examples:

console.log(`Descriptor of the slot save at index ${index}:`, Save.browser.slot.get(index));

 Save.browser.slot.has(index)boolean

Determines whether the slot save at the given index exists.

History:

Parameters:

Returns:

Boolean true if the slot save exists, elsewise false.

Throws:

An Error instance.

Examples:

if (Save.browser.slot.has(index)) {
	/* The slot save at the given index exists. */
}

 Save.browser.slot.isEnabled()boolean

Determines whether slot saves are enabled.

History:

Parameters: none

Returns:

Boolean true if slot saves are anabled, elsewise false.

Throws: none

Examples:

if (Save.browser.slot.isEnabled()) {
	/* Slot saves are enabled. */
}

 Save.browser.slot.load(index)Promise

Loads the slot save at the given index.

Warning: Saves cannot be loaded during startup and any attempt to do so will cause an error.

History:

Parameters:

Returns: none

A Promise that simply resolves, or rejects with an error if the save could not be loaded.

Throws: none

Examples:

Basic usage

Load the slot save at the given index. This should be sufficient in the majority of cases.

Save.browser.slot.load(index)
	.then(() => {
		Engine.show();
	})
	.catch(error => {
		/* Failure.  Handle the error. */
		console.error(error);
		UI.alert(error);
	});

 Save.browser.slot.save(index, [desc [, metadata]])

Saves a slot save to the given index.

History:

Parameters:

Returns: none

Throws:

An Error instance.

Examples:

Basic usage

Save to slot save index 0 with the default description and no metadata, handling failure.

try {
	Save.browser.slot.save(0);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save to slot save index 0 with a description and no metadata, handling failure.

try {
	Save.browser.slot.save(0, "In the wilds");
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save to slot save index 0 with the default description and metadata, handling failure.

try {
	const saveMetadata = {
		chars : ['Celes', 'Locke', 'Edward'],
		gold  : 2345
	};
	Save.browser.slot.save(0, null, saveMetadata);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save to slot save index 0 with a description and metadata, handling failure.

try {
	const saveMetadata = {
		chars : ['Celes', 'Locke', 'Edward'],
		gold  : 2345
	};
	Save.browser.slot.save(0, "In the wilds", saveMetadata);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Disk Saves

 Save.disk.export(filename)

Exports all existing browser saves as a bundle to disk, which may be restored via Save.disk.import().

History:

Parameters:

Returns: none

Throws:

An Error instance.

Examples:

Export all saves as a bundle with a base filename, handling failure.

try {
	Save.disk.export('The 6th Fantasy');
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Save.disk.import(event)Promise

Imports a saves bundle from disk, created via Save.disk.export().

Note: This method must be used as, or be called by, the change event handler of an <input type="file"> element.

Warning: All existing browser saves will be deleted as part of restoring the exported save bundle.

History:

Parameters:

Returns:

A Promise that simply resolves, or rejects with an error if the browser saves bundle could not be imported.

Throws: none

Examples:

Basic usage

Import the saves bundle from disk, only handling failure. This should be sufficient in the majority of cases.

jQuery(document.createElement('input'))
	.prop({
		id   : 'saves-import-file',
		name : 'saves-import-file',
		type : 'file'
	})
	.on('change', ev => {
		// You must provide the event to Save.disk.import()
		Save.disk.import(ev)
			.catch(error => {
				/* Failure.  Handle the error. */
				console.error(error);
				UI.alert(error);
			});
	});

Import the saves bundle from disk, handling both success and failure.

jQuery(document.createElement('input'))
	.prop({
		id   : 'saves-import-file',
		name : 'saves-import-file',
		type : 'file'
	})
	.on('change', function (ev) {
		// You must provide the event to Save.browser.import()
		Save.disk.import(ev)
			.then(() => {
				/* Success.  Do something special. */
			})
			.catch(error => {
				/* Failure.  Handle the error. */
				console.error(error);
				UI.alert(error);
			});
	});

 Save.disk.load(event)Promise

Loads the given save from disk, created via Save.disk.save().

Note: This method must be used as, or be called by, the change event handler of an <input type="file"> element.

Warning: Saves cannot be loaded during startup and any attempt to do so will cause an error.

History:

Parameters:

Returns:

A Promise that resolves with the save's metadata (any), or rejects with an error if the save could not be loaded.

Throws: none

Examples:

Basic usage

Load the disk save. This should be sufficient in the majority of cases.

jQuery(document.createElement('input'))
	.prop({
		id   : 'saves-disk-load-file',
		name : 'saves-disk-load-file',
		type : 'file'
	})
	.on('change', ev => {
		// You must provide the event to Save.disk.load()
		Save.disk.load(ev)
			.then(metadata => {
				Engine.show();
			})
			.catch(error => {
				/* Failure.  Handle the error. */
				console.error(error);
				UI.alert(error);
			});
	});
As a self-contained button using macros
<<button "Load From Disk">>
	<<script>>
	jQuery(document.createElement('input'))
		.prop('type', 'file')
		.on('change', ev => {
			// You must provide the event to Save.disk.load()
			Save.disk.load(ev)
				.then(metadata => {
					Engine.show();
				})
				.catch(error => {
					/* Failure.  Handle the error. */
					console.error(error);
					UI.alert(error);
				});
		})
		.trigger('click');
	<</script>>
<</button>>

 Save.disk.save(filename [, metadata])

Saves the current story state to disk, which may be restored via Save.disk.load().

History:

Parameters:

Returns: none

Throws:

An Error instance.

Examples:

Save with a base filename and no metadata, handling failure.

try {
	Save.disk.save("The 6th Fantasy");
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save with a base filename and metadata, handling failure.

try {
	const saveMetadata = {
		chars : ['Celes', 'Locke', 'Edward'],
		gold  : 2345
	};
	Save.disk.save("The 6th Fantasy", saveMetadata);
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Base64 Saves

 Save.base64.export()string

Exports all existing browser saves as a bundle to a Base64 string, which may be restored via Save.base64.import().

History:

Parameters: none

Returns: none

Throws:

An Error instance.

Examples:

Export all saves as a bundle, handling failure.

try {
	const base64Save = Save.base64.export();
	/* Do something with the saves bundle. */
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Save.base64.import(bundle)Promise

Imports the given Base64 saves bundle string, created via Save.base64.export().

Warning: All existing browser saves will be deleted as part of restoring the exported save bundle.

History:

Parameters:

Returns:

A Promise that simply resolves, or rejects with an error if the browser saves bundle could not be imported.

Throws: none

Examples:

Basic usage

Import the saves bundle, only handling failure. This should be sufficient in the majority of cases.

Save.base64.import(base64Bundle)
	.catch(error => {
		/* Failure.  Handle the error. */
		console.error(error);
		UI.alert(error);
	});

Import the saves bundle, handling both success and failure.

Save.base64.import(base64Bundle)
	.then(() => {
		/* Success.  Do something special. */
	})
	.catch(error => {
		/* Failure.  Handle the error. */
		console.error(error);
		UI.alert(error);
	});

 Save.base64.load(save)Promise

Loads the given Base64 save string, created via Save.base64.save().

Warning: Saves cannot be loaded during startup and any attempt to do so will cause an error.

History:

Parameters:

Returns:

A Promise that resolves with the save's metadata (any), or rejects with an error if the save could not be loaded.

Throws: none

Examples:

Basic usage

Load the save string. This should be sufficient in the majority of cases.

Save.base64.load(base64Save)
	.then(metadata => {
		Engine.show();
	})
	.catch(error => {
		/* Failure.  Handle the error. */
		console.error(error);
		UI.alert(error);
	});

 Save.base64.save([metadata])string

Saves the current story state as a Base64 string.

History:

Parameters:

Returns:

A Base64 save string.

Throws:

An Error instance.

Examples:

Basic usage

Save without metadata, handling failure.

try {
	const base64Save = Save.base64.save();
	/* Do something with the save. */
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

Save with metadata, handling failure.

try {
	const saveMetadata = {
		chars : ['Celes', 'Locke', 'Edward'],
		gold  : 2345
	};
	const base64Save = Save.base64.save(saveMetadata);
	/* Do something with the save. */
}
catch (error) {
	/* Failure.  Handle the error. */
	console.error(error);
	UI.alert(error);
}

 Save Events

 Save.onLoad.sizeinteger

The total number of currently registered on-load handlers.

History:

Value:

The integer count of currently registered on-load handlers.

Examples:

console.log('There are %d onLoad handlers registered.', Save.onLoad.size);

 Save.onLoad.add(handler)

Performs any required processing before the save data is loaded—e.g., upgrading out-of-date save data. The handler is passed one parameter, the save object to be processed. If it encounters an unrecoverable problem during its processing, it may throw an exception containing an error message; the message will be displayed to the player and loading of the save will be terminated.

History:

Parameters:

Returns: none

Throws:

An Error instance.

Handler parameters:

Examples:

Basic usage
Save.onLoad.add(function (save) {
	/* code to process the save object before it's loaded */
});
Using save objects' type property and the Save.Type pseudo-enumeration
Save.onLoad.add(function (save) {
	switch (save.type) {
		case Save.Type.Auto: {
			/* code to process an auto save object before it's loaded */
			break;
		}

		case Save.Type.Base64: {
			/* code to process a base64 save object before it's loaded */
			break;
		}

		case Save.Type.Disk: {
			/* code to process a disk save object before it's loaded */
			break;
		}

		case Save.Type.Slot: {
			/* code to process a slot save object before it's loaded */
			break;
		}
	}
});

 Save.onLoad.clear()

Deletes all currently registered on-load handlers.

History:

Parameters: none

Returns: none

Throws: none

Examples:

Save.onLoad.clear();

 Save.onLoad.delete(handler)boolean

Deletes the specified on-load handler.

History:

Parameters:

Returns:

Boolean true if the handler existed, elsewise false.

Throws: none

Examples:

// Given:
// 	let myOnLoadHandler = function (save) {
// 		/* code to process the save object before it's loaded */
// 	};
// 	Save.onLoad.add(myOnLoadHandler);

Save.onLoad.delete(myOnLoadHandler);

 Save.onSave.sizeinteger

The total number of currently registered on-save handlers.

History:

Value:

The integer count of currently registered on-save handlers.

Examples:

console.log('There are %d onSave handlers registered.', Save.onSave.size);

 Save.onSave.add(handler)

Performs any required processing before the save data is saved. The handler is passed one parameter, the save object to be processed.

History:

Parameters:

Returns: none

Throws:

An Error instance.

Handler parameters:

Examples:

Basic usage
Save.onSave.add(function (save) {
	/* code to process the save object before it's saved */
});
Using save objects' type property and the Save.Type pseudo-enumeration
Save.onSave.add(function (save) {
	switch (save.type) {
		case Save.Type.Auto: {
			/* code to process an auto save object before it's saved */
			break;
		}

		case Save.Type.Base64: {
			/* code to process a base64 save object before it's saved */
			break;
		}

		case Save.Type.Disk: {
			/* code to process a disk save object before it's saved */
			break;
		}

		case Save.Type.Slot: {
			/* code to process a slot save object before it's saved */
			break;
		}
	}
});

 Save.onSave.clear()

Deletes all currently registered on-save handlers.

History:

Parameters: none

Returns: none

Throws: none

Examples:

Save.onSave.clear();

 Save.onSave.delete(handler)boolean

Deletes the specified on-save handler.

History:

Parameters:

Returns:

Boolean true if the handler existed, elsewise false.

Throws: none

Examples:

// Given:
// 	let myOnSaveHandler = function (save) {
// 		/* code to process the save object before it's saved */
// 	};
// 	Save.onSave.add(myOnSaveHandler);

Save.onSave.delete(myOnSaveHandler);

 Deprecated APIs

 Save.clear()

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.clear() method for its replacement.

History:

 Save.get()

Deprecated: This method has been deprecated and should no longer be used.

History:

 Save.ok()boolean

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.isEnabled() method for its replacement.

History:

 Save.autosave.delete()

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.auto.delete() method for its replacement.

History:

 Save.autosave.get()object

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.auto.get() method for its replacement.

History:

 Save.autosave.has()boolean

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.auto.has() method for its replacement.

History:

 Save.autosave.load()

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.auto.load() method for its replacement.

History:

 Save.autosave.ok()boolean

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.auto.isEnabled() method for its replacement.

History:

 Save.autosave.save([title [, metadata]])

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.auto.save() method for its replacement.

History:

 Save.slots.lengthinteger

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.size property for its replacement.

History:

 Save.slots.count()integer

Deprecated: This method has been deprecated and should no longer be used.

History:

 Save.slots.delete(slot)

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.delete() method for its replacement.

History:

 Save.slots.get(slot)object

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.get() method for its replacement.

History:

 Save.slots.has(slot)boolean

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.has() method for its replacement.

History:

 Save.slots.isEmpty()boolean

Deprecated: This method has been deprecated and should no longer be used.

History:

 Save.slots.load(slot)

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.load() method for its replacement.

History:

 Save.slots.ok()boolean

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.isEnabled() method for its replacement.

History:

 Save.slots.save(slot [, title [, metadata]])

Deprecated: This method has been deprecated and should no longer be used. See the Save.browser.slot.save() method for its replacement.

History:

 Save.import(event)

Deprecated: This method has been deprecated and should no longer be used. See the Save.disk.load() method for its replacement.

History:

 Save.export([filename [, metadata]])

Deprecated: This method has been deprecated and should no longer be used. See the Save.disk.save() method for its replacement.

History:

 Save.deserialize(saveStr)any | null

Deprecated: This method has been deprecated and should no longer be used. See the Save.base64.load() method for its replacement.

History:

 Save.serialize([metadata])string | null

Deprecated: This method has been deprecated and should no longer be used. See the Save.base64.save() method for its replacement.

History:

 Setting API

Manages the Settings dialog and settings object.

Warning: Setting API method calls must be placed within your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) or settings will not function correctly.

 Setting.addHeader(name [, desc])

Adds a header to the Settings dialog.

History:

Parameters:

Examples:

// Setting up a basic header
Setting.addHeader("Content Settings");

// Setting up a header w/ a description
Setting.addHeader("Content Settings", "Settings controlling what content is made available in the game.");

 Setting.addList(name, definition)

Adds the named property to the settings object and a list control for it to the Settings dialog.

History:

Parameters:

Definition object:

A list-type definition object should have some of the following properties:

Result object:

Examples:

// Setting up a basic list control for the settings property 'difficulty'
Setting.addList("difficulty", {
	label   : "Choose a difficulty level.",
	list    : ["Easy", "Normal", "Hard", "INSANE"],
	default : "Normal"
});

// Setting up a list control for the settings property 'theme' w/ callbacks
var settingThemeNames = ["(none)", "Bright Lights", "Charcoal", "Midnight", "Tinsel City"];
var settingThemeHandler = function () {
	// cache the jQuery-wrapped <html> element
	var $html = $("html");

	// remove any existing theme class
	$html.removeClass("theme-bright-lights theme-charcoal theme-midnight theme-tinsel-city");

	// switch on the theme name to add the requested theme class
	switch (settings.theme) {
	case "Bright Lights":
		$html.addClass("theme-bright-lights");
		break;
	case "Charcoal":
		$html.addClass("theme-charcoal");
		break;
	case "Midnight":
		$html.addClass("theme-midnight");
		break;
	case "Tinsel City":
		$html.addClass("theme-tinsel-city");
		break;
	}
};
Setting.addList("theme", {
	label    : "Choose a theme.",
	list     : settingThemeNames,
	onInit   : settingThemeHandler,
	onChange : settingThemeHandler
}); // default value not defined, so the first array member "(none)" is used

 Setting.addRange(name, definition)

Adds the named property to the settings object and a range control for it to the Settings dialog.

History:

Parameters:

Definition object:

A range-type definition object should have some of the following properties:

Result object:

Examples:

// Setting up a volume control for the settings property 'masterVolume' w/ callback
var settingMasterVolumeHandler = function () {
	SimpleAudio.volume(settings.masterVolume / 10);
};
Setting.addRange("masterVolume", {
	label    : "Master volume.",
	min      : 0,
	max      : 10,
	step     : 1,
	onInit   : settingMasterVolumeHandler,
	onChange : settingMasterVolumeHandler
}); // default value not defined, so max value (10) is used

 Setting.addToggle(name, definition)

Adds the named property to the settings object and a toggle control for it to the Settings dialog.

History:

Parameters:

Definition object:

A toggle-type definition object should have some of the following properties:

Result object:

Examples:

Basic toggle setting
// Setting up a basic toggle control for the settings property 'mature'
Setting.addToggle("mature", {
	label : "Content for mature audiences?"
}); // default value not defined, so false is used
Toggle that adds/removes a CSS class
// Setting up a toggle control for the settings property 'widescreen' w/ callbacks
var settingWidescreenHandler = function () {
	if (settings.widescreen) { // is true
		$("html").addClass("widescreen");
	}
	else { // is false
		$("html").removeClass("widescreen");
	}
};
Setting.addToggle("widescreen", {
	label    : "Allow the story to use the full width of your browser window?",
	default  : false,
	onInit   : settingWidescreenHandler,
	onChange : settingWidescreenHandler
});

And the requisite CSS style rule:

html.widescreen #passages {
	max-width: none;
}

 Setting.addValue(name [, definition])

Adds the named property to the settings object.

Note: Does not add a control to the Settings dialog.

History:

Parameters:

Definition object:

A value-type definition object should have some of the following properties:

Result object:

Examples:

Basic usage
Setting.addValue("someSetting");
With a definition object
Setting.addValue("anotherSetting", {
	default  : 42,
	onInit   : function () {
		/* Do something when the setting is initialized. */
	},
	onChange : function () {
		/* Do something when the setting is changed. */
	}
});

 Setting.getValue(name)any

Returns the setting's current value.

Note: Calling this method is equivalent to using settings[name].

History:

Parameters: none

Examples:

// Assume `disableAudio` is a toggle-type setting.
if (Setting.getValue("disableAudio")) {
	/* Audio should be disabled. */
}

 Setting.load()

Loads the settings from storage.

Note: This method is automatically called during startup, so you should never need to call it manually.

History:

Parameters: none

Examples:

Setting.load();

 Setting.reset([name])

Resets the setting with the given name to its default value. If no name is given, resets all settings.

History:

Parameters:

Examples:

// Reset the setting 'difficulty'
Setting.reset("difficulty");
// Reset all settings
Setting.reset();

 Setting.save()

Saves the settings to storage.

Note: The controls of the Settings dialog and the Setting.setValue() method automatically call this method when settings are changed, so you should normally never need to call this method manually. Only when directly modifying the values of settings object properties, outside of the controls or Setting.setValue() method, would you need to call this method.

History:

Parameters: none

Examples:

Setting.save();

 Setting.setValue(name, value)

Sets the setting's value.

Note: This method automatically calls the Setting.save() method.

Warning: If manually changing a setting that has an associated control, be mindful that the value you set makes sense for the setting in question, elsewise shenanigans could occur—e.g., don't set a range-type setting to non-number or out-of-range values.

History:

Parameters:

Examples:

Setting.setValue("theme", "dark");

 settings object

A prototype-less generic object whose properties and values are defined by the Setting.addList(), Setting.addRange(), Setting.addToggle(), and Setting.addValue() methods.

For all types of setting types except value-types, the values of its properties are automatically managed by the Settings dialog controls. If necessary, you may manually change setting values via the Setting.setValue() method.

Note: You may also manually change setting values by assigning directly to the associated property—e.g., settings["mode"] = "day". Doing so, however, does not automatically save any values so updated, thus you must manually call the Setting.save() method afterwards.

Warning: If manually changing a setting that has an associated control, be mindful that the value you set makes sense for the setting in question, elsewise shenanigans could occur—e.g., don't set a range-type setting to non-number or out-of-range values.

History:

 SimpleAudio API

The core audio subsystem and backend for the audio macros.

See Also: AudioTrack API, AudioRunner API, and AudioList API.

 Audio limitations

The audio subsystem is based upon the HTML Media Elements APIs and comes with some built-in limitations:

  1. True gapless transitions between tracks is not supported.
  2. In mobile browsers, playback volume is controlled by the device hardware. Thus, all volume adjustments are ignored by the device, though muting should work normally.
  3. In mobile browsers and, more recently, most desktop browsers, playback must be initiated by the player—generally via click/touch. In these cases, audio will not automatically play on the starting passage, nor is it likely to play if initiated from within asynchronous code—e.g., via <<timed>>—though this ultimately depends on various factors. A simple solution for the former is to use some kind of click/touch-through screen—e.g., a splash screen, which the player goes through to the real starting passage. The latter is harder to resolve, so is best avoided.
  4. The load and playback states of tracks are not currently recorded within the active play session or saves. Thus, if you need either to be recoverable, then you'll have to handle that yourself.

 General

 SimpleAudio.load()

Pauses playback of all currently registered tracks and, if they're not already in the process of loading, force them to drop any existing data and begin loading.

Warning: This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

History:

Parameters: none

Examples:

SimpleAudio.load();

 SimpleAudio.loadWithScreen()

Displays the loading screen until all currently registered audio tracks have either loaded to a playable state or aborted loading due to errors. The loading process is as described in SimpleAudio.load().

Warning: This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

History:

Parameters: none

Examples:

SimpleAudio.loadWithScreen();

 SimpleAudio.mute([state])get: boolean | set: undefined

Gets or sets the mute state for the master volume (default: false).

History:

Parameters:

Examples:

// Get the current master volume mute state.
var isMuted = SimpleAudio.mute();

// Mute the master volume.
SimpleAudio.mute(true);

// Unmute the master volume.
SimpleAudio.mute(false);

 SimpleAudio.muteOnHidden([state])get: boolean | set: undefined

Gets or sets the mute-on-hidden state for the master volume (default: false). The mute-on-hidden state controls whether the master volume is automatically muted/unmuted when the story's browser tab loses/gains visibility. Loss of visibility is defined as when the browser window is either switched to another tab or minimized.

History:

Parameters:

Examples:

// Get the current master volume mute-on-hidden state.
var isMuteOnHidden = SimpleAudio.muteOnHidden();

// Enable automatic muting of the master volume when visibility is lost.
SimpleAudio.muteOnHidden(true);

// Disable automatic muting of the master volume when visibility is lost.
SimpleAudio.muteOnHidden(false);

 SimpleAudio.select(selector)AudioRunner object

Returns an AudioRunner instance for the tracks matching the given selector.

History:

Parameters:

Examples:

Basic usage
SimpleAudio.select(":ui")  → Returns the AudioRunner instance for the tracks matching ":ui"
Typical usage
// Return the AudioTrack instance matching "swamped" and do something with it
SimpleAudio.select("swamped").volume(1).play();

// Start playback of paused audio tracks
SimpleAudio.select(":paused").play();

// Pause playback of playing audio tracks
SimpleAudio.select(":playing").pause();

// Stop playback of playing audio tracks
SimpleAudio.select(":playing").stop();

// Stop playback of all audio tracks (not uniquely part of a playlist)
SimpleAudio.select(":all").stop();

// Stop playback of playing audio tracks except those in the ":ui" group
SimpleAudio.select(":playing:not(:ui)").stop();

// Change the volume of all audio tracks except those in the ":ui" group
// to 40%, without changing the current playback state
SimpleAudio.select(":all:not(:ui)").volume(0.40);

 SimpleAudio.stop()

Stops playback of all currently registered tracks.

History:

Parameters: none

Examples:

SimpleAudio.stop();

 SimpleAudio.unload()

Stops playback of all currently registered tracks and force them to drop any existing data.

Note: Once a track has been unloaded, playback cannot occur until it is reloaded.

History:

Parameters: none

Examples:

SimpleAudio.unload();

 SimpleAudio.volume([level])get: number | set: undefined

Gets or sets the master volume level (default: 1).

History:

Parameters:

Examples:

// Get the current master volume level.
var currentMasterVolume = SimpleAudio.volume();

// Set the master volume level to 75%.
SimpleAudio.volume(0.75);

 Tracks

 SimpleAudio.tracks.add(trackId, sources…)

Adds an audio track with the given track ID.

History:

Parameters:

Examples:

// Cache a track with the ID "boom" and one source via relative URL
SimpleAudio.tracks.add("boom", "media/audio/explosion.mp3");

// Cache a track with the ID "boom" and one source via audio passage
SimpleAudio.tracks.add("boom", "explosion");

// Cache a track with the ID "bgm_space" and two sources via relative URLs
SimpleAudio.tracks.add("bgm_space", "media/audio/space_quest.mp3", "media/audio/space_quest.ogg");

// Cache a track with the ID "what" and one source via URL with a format specifier
SimpleAudio.tracks.add("what", "mp3|http://an-audio-service.com/a-user/a-track-id");

 SimpleAudio.tracks.clear()

Deletes all audio tracks.

Note: Cannot delete tracks solely under the control of a playlist.

History:

Parameters: none

Examples:

SimpleAudio.tracks.clear();

 SimpleAudio.tracks.delete(trackId)

Deletes the audio track with the given track ID.

Note: Cannot delete tracks solely under the control of a playlist.

Warning: Does not currently remove the track from either groups or playlists. Thus, any groups or playlists containing the deleted track should be rebuilt.

History:

Parameters:

Examples:

SimpleAudio.tracks.delete("bgm_space");

 SimpleAudio.tracks.get(trackId)AudioTrack object | null

Returns the AudioTrack instance with the given track ID, or null on failure.

Note: To affect multiple tracks and/or groups at once, see the SimpleAudio.select() method.

History:

Parameters:

Examples:

Basic usage
SimpleAudio.tracks.get("swamped")  → Returns the AudioTrack instance matching "swamped"
Typical usage
// Return the AudioTrack instance matching "swamped" and do something with it
SimpleAudio.tracks.get("swamped").volume(1).play();

 SimpleAudio.tracks.has(trackId)boolean

Returns whether an audio track with the given track ID exists.

History:

Parameters:

Examples:

if (SimpleAudio.tracks.has("bgm_space")) {
	// Track "bgm_space" exists.
}

 Groups

 SimpleAudio.groups.add(groupId, trackIds…)

Adds an audio group with the given group ID. Groups are useful for applying actions to multiple tracks simultaneously and/or excluding the included tracks from a larger set when applying actions.

Note: If you want to play tracks in a sequence, then you want a playlist instead.

History:

Parameters:

Examples:

// Set up a group ":ui" with the tracks: "ui_beep", "ui_boop", and "ui_swish"
SimpleAudio.groups.add(":ui", "ui_beep", "ui_boop", "ui_swish");

 SimpleAudio.groups.clear()

Deletes all audio groups.

Note: Only deletes the groups themselves, does not affect their component tracks.

History:

Parameters: none

Examples:

SimpleAudio.groups.clear();

 SimpleAudio.groups.delete(groupId)

Deletes the audio group with the given group ID.

Note: Only deletes the group itself, does not affect its component tracks.

History:

Parameters:

Examples:

SimpleAudio.groups.delete(":ui");

 SimpleAudio.groups.get(groupId)Array<string> | null

Returns the array of track IDs with the given group ID, or null on failure.

Note: To actually affect multiple tracks and/or groups, see the SimpleAudio.select() method.

History:

Parameters:

Examples:

SimpleAudio.groups.get(":ui")  → Returns the array of track IDs matching ":ui"

 SimpleAudio.groups.has(groupId)boolean

Returns whether an audio group with the given group ID exists.

History:

Parameters:

Examples:

if (SimpleAudio.groups.has(":ui")) {
	// Group ":ui" exists.
}

 Lists

 SimpleAudio.lists.add(listId, sources…)

Adds a playlist with the given list ID. Playlists are useful for playing tracks in a sequence—i.e., one after another.

Note: If you simply want to apply actions to multiple tracks simultaneously, then you want a group instead.

History:

Parameters:

Descriptor objects:

Track descriptor objects come in two forms and should have some of the noted properties:

Examples:

Basic usage with track IDs
// Add existing tracks at their current volumes
SimpleAudio.lists.add("bgm_lacuna", "swamped", "heavens_a_lie", "closer", "to_the_edge");
Using a mix of track IDs and descriptors
SimpleAudio.lists.add("bgm_lacuna",
	// Add existing track "swamped" at its current volume
	"swamped",

	// Add existing track "heavens_a_lie" at 50% volume
	{
		id     : "heavens_a_lie",
		volume : 0.5
	},

	// Add an owned copy of existing track "closer" at its current volume
	{
		id  : "closer",
		own : true
	},

	// Add an owned copy of existing track "to_the_edge" at 100% volume
	{
		id     : "to_the_edge",
		own    : true,
		volume : 1
	}
);
Using descriptors with sources
SimpleAudio.lists.add("bgm_lacuna",
	// Add a track from the given sources at the default volume (100%)
	{
		sources : ["media/audio/Swamped.mp3"]
	}

	// Add a track from the given sources at 50% volume
	{
		sources : ["media/audio/Heaven's_A_Lie.mp3"],
		volume  : 0.5
	},

	// Add a track from the given sources at the default volume (100%)
	{
		sources : ["media/audio/Closer.mp3"]
	},

	// Add a track from the given sources at 100% volume
	{
		sources : ["media/audio/To_The_Edge.mp3"],
		volume  : 1
	}
);

 SimpleAudio.lists.clear()

Deletes all playlists.

History:

Parameters: none

Examples:

SimpleAudio.lists.clear();

 SimpleAudio.lists.delete(listId)

Deletes the playlist with the given list ID.

History:

Parameters:

Examples:

SimpleAudio.lists.delete("bgm_lacuna");

 SimpleAudio.lists.get(listId)AudioList object | null

Returns the AudioList instance with the given list ID, or null on failure.

History:

Parameters:

Examples:

Basic usage
SimpleAudio.lists.get("bgm_lacuna")  → Returns the AudioList instance matching "bgm_lacuna"
Typical usage
// Return the AudioList instance matching "bgm_lacuna" and do something with it
SimpleAudio.lists.get("bgm_lacuna").volume(1).loop(true).play();

 SimpleAudio.lists.has(listId)boolean

Returns whether a playlist with the given list ID exists.

History:

Parameters:

Examples:

if (SimpleAudio.lists.has("bgm_lacuna")) {
	// Playlist "bgm_lacuna" exists.
}

 AudioTrack API

Audio tracks encapsulate and provide a consistent interface to an audio resource.

See Also: SimpleAudio API, AudioRunner API, and AudioList API.

 <AudioTrack>.clone()AudioTrack object

Returns a new independent copy of the track.

History:

Parameters: none

Examples:

var trackCopy = aTrack.clone();

 <AudioTrack>.duration()number

Returns the track's total playtime in seconds, Infinity for a stream, or NaN if no metadata exists.

History:

Parameters: none

Examples:

var trackLength = aTrack.duration();

 <AudioTrack>.fade(duration , toVol [, fromVol])Promise object

Starts playback of the track and fades it between the specified starting and destination volume levels over the specified number of seconds.

Note: The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Parameters:

Examples:

// Fade the track from volume 0 to 1 over 6 seconds.
aTrack.fade(6, 1, 0);

 <AudioTrack>.fadeIn(duration [, fromVol])Promise object

Starts playback of the track and fades it from the specified volume level to 1 (loudest) over the specified number of seconds.

History:

Parameters:

Examples:

// Fade the track in from volume 0 over 5 seconds.
aTrack.fadeIn(5, 0);

 <AudioTrack>.fadeOut(duration [, fromVol])Promise object

Starts playback of the track and fades it from the specified volume level to 0 (silent) over the specified number of seconds.

Note: The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Parameters:

Examples:

// Fade the track out from volume 1 over 8 seconds.
aTrack.fadeOut(8, 1);

 <AudioTrack>.fadeStop()

Interrupts an in-progress fade of the track, or does nothing if no fade is progressing.

Note: This does not alter the volume level.

History:

Parameters: none

Examples:

aTrack.fadeStop();

 <AudioTrack>.hasData()boolean

Returns whether enough data has been loaded to play the track through to the end without interruption.

Note: This is an estimate calculated by the browser based upon the currently downloaded data and the download rate.

History:

Parameters: none

Examples:

if (aTrack.hasData()) {
	/* do something */
}

 <AudioTrack>.hasMetadata()boolean

Returns whether, at least, the track's metadata has been loaded.

History:

Parameters: none

Examples:

if (aTrack.hasMetadata()) {
	/* do something */
}

 <AudioTrack>.hasNoData()boolean

Returns whether none of the track's data has been loaded.

History:

Parameters: none

Examples:

if (aTrack.hasNoData()) {
	/* do something */
}

 <AudioTrack>.hasSomeData()boolean

Returns whether, at least, some of the track's data has been loaded.

Tip: The <AudioTrack>.hasData() method is generally more useful.

History:

Parameters: none

Examples:

if (aTrack.hasSomeData()) {
	/* do something */
}

 <AudioTrack>.hasSource()boolean

Returns whether any valid sources were registered.

History:

Parameters: none

Examples:

if (aTrack.hasSource()) {
	/* do something */
}

 <AudioTrack>.isEnded()boolean

Returns whether playback of the track has ended.

History:

Parameters: none

Examples:

if (aTrack.isEnded()) {
	/* do something */
}

 <AudioTrack>.isFading()boolean

Returns whether a fade is in-progress on the track.

History:

Parameters: none

Examples:

if (aTrack.isFading()) {
	/* do something */
}

 <AudioTrack>.isFailed()boolean

Returns whether an error has occurred.

History:

Parameters: none

Examples:

if (aTrack.isFailed()) {
	/* do something */
}

 <AudioTrack>.isLoading()boolean

Returns whether the track is loading data.

History:

Parameters: none

Examples:

if (aTrack.isLoading()) {
	/* do something */
}

 <AudioTrack>.isPaused()boolean

Returns whether playback of the track has been paused.

History:

Parameters: none

Examples:

if (aTrack.isPaused()) {
	/* do something */
}

 <AudioTrack>.isPlaying()boolean

Returns whether the track is playing.

History:

Parameters: none

Examples:

if (aTrack.isPlaying()) {
	/* do something */
}

 <AudioTrack>.isSeeking()boolean

Returns whether the track is seeking.

History:

Parameters: none

Examples:

if (aTrack.isSeeking()) {
	/* do something */
}

 <AudioTrack>.isStopped()boolean

Returns whether playback of the track has been stopped.

History:

Parameters: none

Examples:

if (aTrack.isStopped()) {
	/* do something */
}

 <AudioTrack>.isUnavailable()boolean

Returns whether the track is currently unavailable for playback. Possible reasons include: no valid sources are registered, no sources are currently loaded, an error has occurred.

History:

Parameters: none

Examples:

if (aTrack.isUnavailable()) {
	/* do something */
}

 <AudioTrack>.isUnloaded()boolean

Returns whether the track's sources are currently unloaded.

History:

Parameters: none

Examples:

if (aTrack.isUnloaded()) {
	/* do something */
}

 <AudioTrack>.load()

Pauses playback of the track and, if it's not already in the process of loading, forces it to drop any existing data and begin loading.

Warning: This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

History:

Parameters: none

Examples:

aTrack.load();

 <AudioTrack>.loop([state])get: boolean | set: AudioTrack object

Gets or sets the track's repeating playback state (default: false). When used to set the loop state, returns a reference to the current AudioTrack instance for chaining.

History:

Parameters:

Examples:

// Get the track's current loop state.
var isLooped = aTrack.loop();

// Loop the track.
aTrack.loop(true);

// Unloop the track.
aTrack.loop(false);

 <AudioTrack>.mute([state])get: boolean | set: AudioTrack object

Gets or sets the track's volume mute state (default: false). When used to set the mute state, returns a reference to the current AudioTrack instance for chaining.

History:

Parameters:

Examples:

// Get the track's current volume mute state.
var isMuted = aTrack.mute();

// Mute the track's volume.
aTrack.mute(true);

// Unmute the track's volume.
aTrack.mute(false);

 <AudioTrack>.off(...args)AudioTrack object

Removes event handlers from the track. Returns a reference to the current AudioTrack instance for chaining.

Note: Shorthand for jQuery's .off() method applied to the audio element.

Warning: The SimpleAudio APIs use events internally for various pieces of functionality. To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers. It is further strongly suggested that you provide that same custom user namespace when removing them.

History:

Parameters:

See: <jQuery>.off() in the jQuery API docs for more information.

Examples:

// Remove any handlers for the ended event.
aTrack.off('ended.myEvents');

 <AudioTrack>.on(...args)AudioTrack object

Attaches event handlers to the track. Returns a reference to the current AudioTrack instance for chaining.

Note: Shorthand for jQuery's .on() method applied to the audio element.

Warning: The SimpleAudio APIs use events internally for various pieces of functionality. To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers. It is further strongly suggested that you provide that same custom user namespace when removing them.

History:

Parameters:

See: <jQuery>.on() in the jQuery API docs for more information.

Examples:

// Add a handler for the ended event.
aTrack.on('ended.myEvents', function () {
	/* do something */
});

 <AudioTrack>.one(...args)AudioTrack object

Attaches single-use event handlers to the track. Returns a reference to the current AudioTrack instance for chaining.

Note: Shorthand for jQuery's .one() method applied to the audio element.

Warning: The SimpleAudio APIs use events internally for various pieces of functionality. To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers. It is further strongly suggested that you provide that same custom user namespace when removing them.

History:

Parameters:

See: <jQuery>.one() in the jQuery API docs for more information.

Examples:

// Add a single-use handler for the ended event.
aTrack.one('ended.myEvents', function () {
	/* do something */
});

 <AudioTrack>.pause()

Pauses playback of the track.

History:

Parameters: none

Examples:

aTrack.pause();

 <AudioTrack>.play()Promise object

Begins playback of the track.

History:

Parameters: none

Examples:

Basic usage
aTrack.play();
Using the returned Promise
aTrack.play()
	.then(function () {
		console.log('The track is playing.');
	})
	.catch(function (problem) {
		console.warn('There was a problem with playback: ' + problem);
	});

 <AudioTrack>.playWhenAllowed()

Begins playback of the track or, failing that, sets the track to begin playback as soon as the player has interacted with the document.

History:

Parameters: none

Examples:

aTrack.playWhenAllowed();

 <AudioTrack>.remaining()number

Returns how much remains of the track's total playtime in seconds, Infinity for a stream, or NaN if no metadata exists.

History:

Parameters: none

Examples:

var trackRemains = aTrack.remaining();

 <AudioTrack>.stop()

Stops playback of the track.

History:

Parameters: none

Examples:

someTrack.stop();

 <AudioTrack>.time([seconds])get: number | set: AudioTrack object

Gets or sets the track's current time in seconds. When used to set a value, returns a reference to the current AudioTrack instance for chaining.

History:

Parameters:

Examples:

// Get the track's current time.
var trackTime = aTrack.time();

// Set the track's current time to 30 seconds from its beginning.
aTrack.time(30);

// Set the track's current time to 30 seconds from its end.
aTrack.time(aTrack.duration() - 30);

 <AudioTrack>.unload()

Stops playback of the track and forces it to drop any existing data.

Note: Once unloaded, playback cannot occur until the track's data is loaded again.

History:

Parameters: none

Examples:

aTrack.unload();

 <AudioTrack>.volume([level])get: number | set: AudioTrack object

Gets or sets the track's volume level (default: 1). When used to set the volume, returns a reference to the current AudioTrack instance for chaining.

History:

Parameters:

Examples:

// Get the track's current volume level.
var trackVolume = aTrack.volume();

// Set the track's volume level to 75%.
aTrack.volume(0.75);

 AudioRunner API

Audio runners are useful for performing actions on multiple tracks at once.

See Also: SimpleAudio API, AudioTrack API, and AudioList API.

 <AudioRunner>.fade(duration , toVol [, fromVol])

Starts playback of the selected tracks and fades them between the specified starting and destination volume levels over the specified number of seconds.

Note: The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Parameters:

Examples:

// Fade the selected tracks from volume 0 to 1 over 6 seconds.
someTracks.fade(6, 1, 0);

 <AudioRunner>.fadeIn(duration [, fromVol])

Starts playback of the selected tracks and fades them from the specified volume level to 1 (loudest) over the specified number of seconds.

History:

Parameters:

Examples:

// Fade the selected tracks in from volume 0 over 5 seconds.
someTracks.fadeIn(5, 0);

 <AudioRunner>.fadeOut(duration [, fromVol])

Starts playback of the selected tracks and fades them from the specified volume level to 0 (silent) over the specified number of seconds.

Note: The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Parameters:

Examples:

// Fade the selected tracks out from volume 1 over 8 seconds.
someTracks.fadeOut(8, 1);

 <AudioRunner>.fadeStop()

Interrupts an in-progress fade of the selected tracks, or does nothing if no fade is progressing.

Note: This does not alter the volume level.

History:

Parameters: none

Examples:

someTracks.fadeStop();

 <AudioRunner>.load()

Pauses playback of the selected tracks and, if they're not already in the process of loading, forces them to drop any existing data and begin loading.

Warning: This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

History:

Parameters: none

Examples:

someTracks.load();

 <AudioRunner>.loop(state)AudioRunner object

Sets the selected tracks' repeating playback state (default: false). Returns a reference to the current AudioRunner instance for chaining.

History:

Parameters:

Examples:

// Loop the selected tracks.
someTracks.loop(true);

// Unloop the selected tracks.
someTracks.loop(false);

 <AudioRunner>.mute(state)AudioRunner object

Sets the selected tracks' volume mute state (default: false). Returns a reference to the current AudioRunner instance for chaining.

History:

Parameters:

Examples:

// Mute the selected tracks' volume.
someTracks.mute(true);

// Unmute the selected tracks' volume.
someTracks.mute(false);

 <AudioRunner>.off(...args)AudioRunner object

Removes event handlers from the selected tracks. Returns a reference to the current AudioRunner instance for chaining.

Note: Shorthand for jQuery's .off() method applied to each of the audio elements.

Warning: The SimpleAudio APIs use events internally for various pieces of functionality. To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers. It is further strongly suggested that you provide that same custom user namespace when removing them.

History:

Parameters:

See: <jQuery>.off() in the jQuery API docs for more information.

Examples:

// Remove any handlers for the ended event.
someTracks.off('ended.myEvents');

 <AudioRunner>.on(...args)AudioRunner object

Attaches event handlers to the selected tracks. Returns a reference to the current AudioRunner instance for chaining.

Note: Shorthand for jQuery's .on() method applied to each of the audio elements.

Warning: The SimpleAudio APIs use events internally for various pieces of functionality. To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers. It is further strongly suggested that you provide that same custom user namespace when removing them.

History:

Parameters:

See: <jQuery>.on() in the jQuery API docs for more information.

Examples:

// Add a handler for the ended event.
someTracks.on('ended.myEvents', function () {
	/* do something */
});

 <AudioRunner>.one(...args)AudioRunner object

Attaches single-use event handlers to the selected tracks. Returns a reference to the current AudioRunner instance for chaining.

Note: Shorthand for jQuery's .one() method applied to each of the audio elements.

Warning: The SimpleAudio APIs use events internally for various pieces of functionality. To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers. It is further strongly suggested that you provide that same custom user namespace when removing them.

History:

Parameters:

See: <jQuery>.one() in the jQuery API docs for more information.

Examples:

// Add a single-use handler for the ended event.
someTracks.one('ended.myEvents', function () {
	/* do something */
});

 <AudioRunner>.pause()

Pauses playback of the selected tracks.

History:

Parameters: none

Examples:

someTracks.pause();

 <AudioRunner>.play()

Begins playback of the selected tracks.

History:

Parameters: none

Examples:

someTracks.play();

 <AudioRunner>.playWhenAllowed()

Begins playback of the selected tracks or, failing that, sets the tracks to begin playback as soon as the player has interacted with the document.

History:

Parameters: none

Examples:

someTracks.playWhenAllowed();

 <AudioRunner>.stop()

Stops playback of the selected tracks.

History:

Parameters: none

Examples:

someTracks.stop();

 <AudioRunner>.time(seconds)AudioRunner object

Sets the selected tracks' current time in seconds. Returns a reference to the current AudioRunner instance for chaining.

History:

Parameters:

Examples:

// Set the selected tracks' current time to 30 seconds from their beginning.
someTracks.time(30);

 <AudioRunner>.unload()

Stops playback of the selected tracks and forces them to drop any existing data.

Note: Once unloaded, playback cannot occur until the selected tracks' data is loaded again.

History:

Parameters: none

Examples:

someTracks.unload();

 <AudioRunner>.volume(level)AudioRunner object

Sets the selected tracks' volume level (default: 1). Returns a reference to the current AudioRunner instance for chaining.

History:

Parameters:

Examples:

// Set the selected tracks' volume level to 75%.
someTracks.volume(0.75);

 AudioList API

Audio lists (playlists) are useful for playing tracks in a sequence—i.e., one after another.

See Also: SimpleAudio API, AudioTrack API, and AudioRunner API.

 <AudioList>.duration()number

Returns the playlist's total playtime in seconds, Infinity if it contains any streams, or NaN if no metadata exists.

History:

Parameters: none

Examples:

var listLength = aList.duration();

 <AudioList>.fade(duration , toVol [, fromVol])Promise object

Starts playback of the playlist and fades the currently playing track between the specified starting and destination volume levels over the specified number of seconds.

Note: The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Parameters:

Examples:

// Fade the playlist from volume 0 to 1 over 6 seconds.
aList.fade(6, 1, 0);

 <AudioList>.fadeIn(duration [, fromVol])Promise object

Starts playback of the playlist and fades the currently playing track from the specified volume level to 1 (loudest) over the specified number of seconds.

History:

Parameters:

Examples:

// Fade the playlist in from volume 0 over 5 seconds.
aList.fadeIn(5, 0);

 <AudioList>.fadeOut(duration [, fromVol])Promise object

Starts playback of the playlist and fades the currently playing track from the specified volume level to 0 (silent) over the specified number of seconds.

Note: The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

History:

Parameters:

Examples:

// Fade the playlist out from volume 1 over 8 seconds.
aList.fadeOut(8, 1);

 <AudioList>.fadeStop()

Interrupts an in-progress fade of the currently playing track, or does nothing if no fade is progressing.

Note: This does not alter the volume level.

History:

Parameters: none

Examples:

aList.fadeStop();

 <AudioList>.isEnded()boolean

Returns whether playback of the playlist has ended.

History:

Parameters: none

Examples:

if (aList.isEnded()) {
	/* do something */
}

 <AudioList>.isFading()boolean

Returns whether a fade is in-progress on the currently playing track.

History:

Parameters: none

Examples:

if (aList.isFading()) {
	/* do something */
}

 <AudioList>.isPaused()boolean

Returns whether playback of the playlist has been paused.

History:

Parameters: none

Examples:

if (aList.isPaused()) {
	/* do something */
}

 <AudioList>.isPlaying()boolean

Returns whether the playlist is playing.

History:

Parameters: none

Examples:

if (aList.isPlaying()) {
	/* do something */
}

 <AudioList>.isStopped()boolean

Returns whether playback of the playlist has been stopped.

History:

Parameters: none

Examples:

if (aList.isStopped()) {
	/* do something */
}

 <AudioList>.load()

Pauses playback of the playlist and, if they're not already in the process of loading, forces its tracks to drop any existing data and begin loading.

Warning: This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

History:

Parameters: none

Examples:

aList.load();

 <AudioList>.loop([state])get: boolean | set: AudioList object

Gets or sets the playlist's repeating playback state (default: false). When used to set the loop state, returns a reference to the current AudioList instance for chaining.

History:

Parameters:

Examples:

// Get the playlist's current loop state.
var isLooped = aList.loop();

// Loop the playlist.
aList.loop(true);

// Unloop the playlist.
aList.loop(false);

 <AudioList>.mute([state])get: boolean | set: AudioList object

Gets or sets the playlist's volume mute state (default: false). When used to set the mute state, returns a reference to the current AudioList instance for chaining.

History:

Parameters:

Examples:

// Get the playlist's current volume mute state.
var isMuted = aList.mute();

// Mute the playlist's volume.
aList.mute(true);

// Unmute the playlist's volume.
aList.mute(false);

 <AudioList>.pause()

Pauses playback of the playlist.

History:

Parameters: none

Examples:

aList.pause();

 <AudioList>.play()Promise object

Begins playback of the playlist.

History:

Parameters: none

Examples:

Basic usage
aList.play();
Using the returned Promise
aList.play()
	.then(function () {
		console.log('The playlist is playing.');
	})
	.catch(function (problem) {
		console.warn('There was a problem with playback: ' + problem);
	});

 <AudioList>.playWhenAllowed()

Begins playback of the playlist or, failing that, sets the playlist to begin playback as soon as the player has interacted with the document.

History:

Parameters: none

Examples:

aList.playWhenAllowed();

 <AudioList>.remaining()number

Returns how much remains of the playlist's total playtime in seconds, Infinity if it contains any streams, or NaN if no metadata exists.

History:

Parameters: none

Examples:

var listRemains = aList.remaining();

 <AudioList>.shuffle([state])get: boolean | set: AudioList object

Gets or sets the playlist's randomly shuffled playback state (default: false). When used to set the shuffle state, returns a reference to the current AudioList instance for chaining.

History:

Parameters:

Examples:

// Get the playlist's current shuffle state.
var isShuffled = aList.shuffle();

// Enable shuffling of the playlist.
aList.shuffle(true);

// Disable shuffling of the playlist.
aList.shuffle(false);

 <AudioList>.skip()

Skips ahead to the next track in the playlist, if any.

History:

Parameters: none

Examples:

someTrack.skip();

 <AudioList>.stop()

Stops playback of the playlist.

History:

Parameters: none

Examples:

someTrack.stop();

 <AudioList>.time()number

Returns the playlist's current time in seconds, or NaN if no metadata exists.

History:

Parameters: none

Examples:

var listTime = aList.time();

 <AudioList>.unload()

Stops playback of the playlist and forces its tracks to drop any existing data.

Note: Once unloaded, playback cannot occur until the track's data is loaded again.

History:

Parameters: none

Examples:

aList.unload();

 <AudioList>.volume([level])get: number | set: AudioList object

Gets or sets the playlist's volume level (default: 1). When used to set the volume, returns a reference to the current AudioList instance for chaining.

History:

Parameters:

Examples:

// Get the playlist's current volume level.
var trackVolume = aList.volume();

// Set the playlist's volume level to 75%.
aList.volume(0.75);

 State API

The story history contains moments (states) created during play. Since it is possible to navigate the history—i.e., move backward and forward though the moments within the history—it may contain both past moments—i.e., moments that have been played—and future moments—i.e., moments that had been played, but have been rewound/undone, yet are still available to be restored.

In addition to the history, there is also the active moment—i.e., present—and expired moments—i.e., moments that had been played, but have expired from the history, thus cannot be navigated to.

API members dealing with the history work upon either the active moment—i.e., present—or one of the history subsets: the full in-play history—i.e., past + future—the past in-play subset—i.e., past only—or the extended past subset—i.e., expired + past. These instances will be noted.

 State.activeobject

Returns the active (present) moment.

Note: Using State.active directly is generally unnecessary as there exist a number of shortcut properties, State.passage and State.variables, and story functions, passage() and variables(), which grant access to its normal properties.

History:

Examples:

State.active.title      → The title of the present moment
State.active.variables  → The variables of the present moment

 State.bottomobject

Returns the bottommost (least recent) moment from the full in-play history (past + future).

History:

Examples:

State.bottom.title      → The title of the least recent moment within the full in-play history
State.bottom.variables  → The variables of the least recent moment within the full in-play history

 State.currentobject

Returns the current moment from the full in-play history (past + future), which is the pre-play version of the active moment.

Warning: State.current is not a synonym for State.active. You will, very likely, never need to use State.current directly within your code.

History:

Examples:

State.current.title      → The title of the current moment within the full in-play history
State.current.variables  → The variables of the current moment within the full in-play history

 State.lengthinteger

Returns the number of moments within the past in-play history (past only).

History:

Examples:

if (State.length === 0) {
	/* No moments within the past in-play history. Egad! */
}

 State.passagestring

Returns the title of the passage associated with the active (present) moment.

History:

Examples:

State.passage  → The passage title of the present moment

 State.sizeinteger

Returns the number of moments within the full in-play history (past + future).

History:

Parameters: none

Examples:

if (State.size === 0) {
	/* No moments within the full in-play history. Egad! */
}

 State.temporaryobject

Returns the current temporary variables.

History:

Examples:

State.temporary  → The current temporary variables

 State.topobject

Returns the topmost (most recent) moment from the full in-play history (past + future).

Warning: State.top is not a synonym for State.active. You will, very likely, never need to use State.top directly within your code.

History:

Examples:

State.top.title      → The title of the most recent moment within the full in-play history
State.top.variables  → The variables of the most recent moment within the full in-play history

 State.turnsinteger

Returns the total number (count) of played moments within the extended past history (expired + past).

History:

Examples:

if (State.turns === 1) {
	/* Initial turn.  The starting passage is displayed. */
}

 State.variablesobject

Returns the variables from the active (present) moment.

History:

Examples:

State.variables  → The variables of the present moment

 State.getVar(varName)any

Returns the value of the story or temporary variable by the given name.

History:

Parameters:

Examples:

State.getVar("$charName")  → Returns the value of $charName

 State.has(passageTitle)boolean

Returns whether any moments with the given title exist within the past in-play history (past only).

Note: State.has() does not check expired moments. If you need to know if the player has ever been to a particular passage, then you must use the State.hasPlayed() method or the hasVisited() story function.

History:

Parameters:

Examples:

State.has("The Ducky")  → Returns whether a moment matching "The Ducky" exists

 State.hasPlayed(passageTitle)boolean

Returns whether any moments with the given title exist within the extended past history (expired + past).

Note: If you need to check for multiple passages, the hasVisited() story function will likely be more convenient to use.

History:

Parameters:

Examples:

State.hasPlayed("The Ducky")  → Returns whether a moment matching "The Ducky" ever existed

 State.index(index)object

Returns the moment, relative to the bottom of the past in-play history (past only), at the given index.

History:

Parameters:

Examples:

State.index(0)                 → Returns the least recent moment within the past in-play history
State.index(1)                 → Returns the second to least recent moment within the past in-play history
State.index(State.length - 1)  → Returns the most recent moment within the past in-play history

 State.isEmpty()boolean

Returns whether the full in-play history (past + future) is empty.

History:

Parameters: none

Examples:

if (State.isEmpty()) {
	/* No moments within the full in-play history. Egad! */
}

 State.peek([offset])object

Returns the moment, relative to the top of the past in-play history (past only), at the, optional, offset.

History:

Parameters:

Examples:

State.peek()                  → Returns the most recent moment within the past in-play history
State.peek(0)                 → Returns the most recent moment within the past in-play history
State.peek(1)                 → Returns the second most recent moment within the past in-play history
State.peek(State.length - 1)  → Returns the least recent moment within the past in-play history

 State.metadata.sizeinteger

Returns the size of the story metadata store—i.e., the number of stored pairs.

History:

Examples:

// Determines whether the metadata store has any members.
if (State.metadata.size > 0) {
	/* store is not empty */
}

 State.metadata.clear()

Empties the story metadata store.

History:

Parameters: none

Examples:

// Removes all values from the metadata store.
State.metadata.clear();

 State.metadata.delete(key)

Removes the specified key, and its associated value, from the story metadata store.

History:

Parameters:

Examples:

// Removes 'achievements' from the metadata store.
State.metadata.delete('achievements');

 State.metadata.entries()Array<Array<string, any>>

Returns an array of the story metadata store's key/value pairs as [key, value] arrays.

History:

Parameters: none

Examples:

// Iterate over the pairs with a `for` loop.
var metadata = State.metadata.entries();
for (var i = 0; i < metadata.length; ++i) {
	var key   = metadata[i][0];
	var value = metadata[i][1];

	/* do something */
}
// Iterate over the pairs with `<Array>.forEach()`.
State.metadata.entries().forEach(function (pair) {
	var key   = pair[0];
	var value = pair[1];

	/* do something */
});

 State.metadata.get(key)any

Returns the value associated with the specified key from the story metadata store.

History:

Parameters:

Examples:

// Returns the value of 'achievements' from the metadata store.
var playerAchievements = State.metadata.get('achievements');

 State.metadata.has(key)boolean

Returns whether the specified key exists within the story metadata store.

History:

Parameters:

Examples:

// Returns whether 'achievements' exists within the metadata store.
if (State.metadata.has('achievements')) {
	/* do something */
}

 State.metadata.keys()Array<string>

Returns an array of the story metadata store's keys.

History:

Parameters: none

Examples:

// Iterate over the keys with a `for` loop.
var metadataKeys = State.metadata.keys();
for (var i = 0; i < metadataKeys.length; ++i) {
	var key = metadataKeys[i];

	/* do something */
}
// Iterate over the keys with `<Array>.forEach()`.
State.metadata.keys().forEach(function (key) {
	/* do something */
});

 State.metadata.set(key, value)

Sets the specified key and value within the story metadata store, which causes them to persist over story and browser restarts—n.b. private browsing modes do interfere with this. To update the value associated with a key, simply set it again.

Note: The story metadata, like saves, is tied to the specific story it was generated with. It is not a mechanism for moving data between stories.

Warning: The story metadata store is not, and should not be used as, a replacement for saves. Examples of good uses: achievement tracking, new game+ data, playthrough statistics, etc.

Warning: This feature is largely incompatible with private browsing modes, which cause all in-browser storage mechanisms to either persist only for the lifetime of the browsing session or fail outright.

History:

Parameters:

Examples:

// Sets 'achievements', with the given value, in the metadata store.
State.metadata.set('achievements', { ateYellowSnow : true });

// Sets 'ngplus', with the given value, in the metadata store.
State.metadata.set('ngplus', true);

 State.prng.init([seed [, useEntropy]])

Initializes the seedable pseudo-random number generator (PRNG) and integrates it into the story state and saves. Once initialized, the State.random() method and story functions, random() and randomFloat(), return deterministic results from the seeded PRNG—by default, they return non-deterministic results from Math.random().

Note: State.prng.init() must be called during story initialization, within either your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) or the StoryInit special passage. Additionally, it is strongly recommended that you do not specify any arguments to State.prng.init() and allow it to automatically seed itself. If you should chose to use an explicit seed, however, it is strongly recommended that you also enable additional entropy, otherwise all playthroughs for all players will be exactly the same.

History:

Parameters:

Examples:

State.prng.init()                       → Automatically seed the PRNG (recommended)
State.prng.init("aVeryLongSeed")        → Seed the PRNG with "aVeryLongSeed" (not recommended)
State.prng.init("aVeryLongSeed", true)  → Seed the PRNG with "aVeryLongSeed" and pad it with extra entropy

 State.prng.isEnabled()boolean

Returns whether the seedable PRNG has been enabled.

History:

Examples:

State.prng.isEnabled()  → Returns whether the seedable PRNG is enabled

 State.prng.pullinteger | NaN

Returns the current pull count—i.e., how many requests have been made—from the seedable PRNG or, if the PRNG is not enabled, NaN.

Note: The pull count is automatically included within saves and sessions, so this is not especially useful outside of debugging purposes.

History:

Examples:

State.prng.pull  → Returns the current PRNG pull count

 State.prng.seedstring | null

Returns the seed from the seedable PRNG or, if the PRNG is not enabled, null.

Note: The seed is automatically included within saves and sessions, so this is not especially useful outside of debugging purposes.

History:

Examples:

State.prng.seed  → Returns the PRNG seed

 State.random()number

Returns a pseudo-random decimal number (floating-point) in the range 0 (inclusive) up to, but not including, 1 (exclusive).

Note: By default, it simply returns non-deterministic results from Math.random(), however, when the seedable PRNG has been enabled, via State.prng.init(), it returns deterministic results from the seeded PRNG instead.

History:

Parameters: none

Examples:

State.random()  → Returns a pseudo-random floating-point number in the range [0, 1)

 State.setVar(varName, value)boolean

Sets the value of the story or temporary variable by the given name. Returns whether the operation was successful.

History:

Parameters:

Examples:

State.setVar("$charName", "Jane Doe")  → Assigns the string "Jane Doe" to $charName

 Story API

 Story.idstring

The DOM-compatible ID of the story.

History:

Value:

The string DOM-compatible ID of the story, created from the slugified story name.

 Story.ifIdstring

The IFID (Interactive Fiction IDentifier) of the story.

History:

Value:

The string IFID of the story, or an empty string if no IFID exists. The Twine 2 ecosystem's IFIDs are v4 random UUIDs.

 Story.namestring

The name of the story.

History:

Value:

The string name of the story.

 Story.add(descriptor)boolean

Adds the passage to the passage store.

Note: This method cannot add code passages or passages tagged with code tags.

History:

Parameters:

Passage Descriptor:

A passage descriptor object should have the following properties:

Returns:

Boolean true if the passage was added, elsewise false.

Examples:

// Add a passage
const descriptor = {
	name : "Forest 4",
	tags : "forest heavy",
	text : "You can barely see farther than arm's length for all the trees.",
};

if (Story.add(descriptor)) {
	/* The "Forest 4" passage was added. */
}

 Story.delete(name)boolean

Deletes the Passage instance with the given name.

Note: This method cannot add code passages or passages tagged with code tags.

History:

Parameters:

Returns:

Boolean true if a Passage instance with the given name was deleted, elsewise false.

Examples:

// Delete the Passage instance with the name "The Ducky"
if (Story.delete("The Ducky")) {
	/* The "The Ducky" passage was deleted. */
}

 Story.filter(predicate [, thisArg])Array<Passage>

Searches all Passage instances for those that pass the test implemented by the given predicate function.

Note: This method cannot retrieve passages tagged with code tags.

History:

Parameters:

Returns:

A new Array<Passage> filled with all instances that pass the test implemented by the given predicate function, or an empty Array if no instances pass.

Examples:

// Returns all 'forest'-tagged Passage instances
Story.filter(function (p) {
	return p.tags.includes("forest");
});

// Returns all Passage instances whose names include whitespace
var hasWhitespaceRegExp = /\s/;
Story.filter(function (p) {
	return hasWhitespaceRegExp.test(p.name);
});

 Story.find(predicate [, thisArg])Passage

Searches all Passage instances for the first that passes the test implemented by the given predicate function.

Note: This method cannot retrieve passages tagged with code tags.

History:

Parameters:

Returns:

The first Passage instance that passed the test implemented by the given predicate function, or undefined if no instance passes.

Examples:

// Returns the first 'forest'-tagged Passage instance
Story.find(function (p) {
	return p.tags.includes("forest");
});

// Returns the first Passage instance whose name includes whitespace
var hasWhitespaceRegExp = /\s/;
Story.find(function (p) {
	return hasWhitespaceRegExp.test(p.name);
});

 Story.get(name)Passage

Gets the Passage instance with the given name.

Note: This method cannot retrieve passages tagged with code tags.

History:

Parameters:

Returns:

The Passage instance with the given name, or a new empty Passage instance if no such passage exists.

Examples:

// Get the Passage instance with the name "The Ducky"
const theDucky = Story.get("The Ducky");

 Story.has(name)boolean

Determines whether a Passage instance with the given name exists.

Note: This method does not check passages tagged with code tags.

History:

Parameters:

Returns:

Boolean true if a Passage instance with the given name exists, elsewise false.

Examples:

// Returns whether a "The Ducky" Passage instance exists
if (Story.has("The Ducky")) {
	/* The "The Ducky" passage exists. */
}

 Story.domIdstring

Deprecated: This setting has been deprecated and should no longer be used. See the Story.id setting for its replacement.

History:

 Story.titlestring

Deprecated: This setting has been deprecated and should no longer be used. See the Story.name setting for its replacement.

History:

 Story.lookup(propertyName , searchValue [, sortProperty])Array<Passage>

Deprecated: This static method has been deprecated and should no longer be used. See the Story.filter() static method for its replacement.

History:

 Story.lookupWith(predicate [, sortProperty])Array<Passage>

Deprecated: This static method has been deprecated and should no longer be used. See the Story.filter() static method for its replacement.

History:

 Template API

 Template.sizenumber

Returns the number of existing templates.

History:

Examples:

if (Template.size === 0) {
	/* No templates exist. */
}

 Template.add(name , definition)

Add new template(s).

History:

Parameters:

Function templates:

Function templates should return a string, which may itself contain markup. They are called with no arguments, but with their this set to a template (execution) context object that contains the following data properties:

String templates:

String templates consist solely of a string, which may itself contain markup.

Examples:

Basic usage
/* Define a function template named ?yolo. */
Template.add('yolo', function () {
	return either('YOLO', 'You Only Live Once');
});

/* Define a string template named ?nolf. */
Template.add('nolf', 'No One Lives Forever');

/* Define an array of string templates named ?alsoYolo. */
Template.add('alsoYolo', ['YOLO', 'You Only Live Once']);

/* Define an array of mixed string and function templates named ?cmyk. */
Template.add('cmyk', [
	'Cyan',
	function () {
		return either('Magenta', 'Yellow');
	},
	'Black'
]);
Using the context object (this)
/* Define a function template with two names, ?color and ?Color, whose output changes based on its name. */
Template.add(['color', 'Color'], function () {
	var color = either('red', 'green', 'blue');
	return this.name === 'Color' ? color.toUpperFirst() : color;
});

 Template.delete(name)

Remove existing template(s).

History:

Parameters:

Examples:

/* Deletes the template ?yolo. */
Template.delete('yolo');

/* Deletes the templates ?yolo and ?nolf. */
Template.delete(['yolo', 'nolf']);

 Template.get(name)function | string | Array<function | string>

Return the named template definition, or null on failure.

History:

Parameters:

Examples:

/* Returns the template ?yolo, or null if it doesn't exist. */
var yolo = Template.get('yolo');

 Template.has(name)boolean

Returns whether the named template exists.

History:

Parameters:

Examples:

if (Template.has('yolo')) {
	/* A ?yolo template exists. */
}

 UI API

 UI.alert(message [, options [, closeFn]])

Opens the built-in alert dialog, displaying the given message to the player.

History:

Parameters:

Returns: none

Examples:

UI.alert("You smell of elderberries!");

 UI.restart([options])

Opens the built-in restart dialog, prompting the player to restart the story.

History:

Parameters:

Returns: none

Examples:

UI.restart();

 UI.saves([options [, closeFn]])

Opens the built-in saves dialog.

History:

Parameters:

Returns: none

Examples:

UI.saves();

 UI.settings([options [, closeFn]])

Opens the built-in settings dialog, which is populated from the Setting API.

History:

Parameters:

Returns: none

Examples:

UI.settings();

 UI.update()

Triggers a :uiupdate event that causes the update of the dynamically updated sections built-in user interface—e.g., those populated by code passages, like StoryCaption and StoryMenu. Automatically invoked during passage navigation.

Warning:

As all dynamically updated sections of the built-in UI are updated, save for the main passage display, it is recommended that this method be used sparingly.

Ideally, if you need to update these sections of the built-in UI outside of the normal passage navigation update, then you should update only the specific areas you need to rather than the entire UI.

History:

Parameters: none

Returns: none

Examples:

UI.update();

 UI.jumpto([options [, closeFn]])

Deprecated: This method has been deprecated and should no longer be used.

History:

 UI.share([options [, closeFn]])

Deprecated: This method has been deprecated and should no longer be used.

History:

 UIBar API

 UIBar.destroy()

Completely removes the UI bar and all of its associated styles and event handlers.

History:

Parameters: none

Examples:

UIBar.destroy();

 UIBar.hide()UIBar object

Hides the UI bar. Returns a reference to the UIBar object for chaining.

Note: This does not reclaim the space reserved for the UI bar. Thus, a call to UIBar.stow() may also be necessary. Alternatively, if you simply want the UI bar gone completely and permanently, either using UIBar.destroy() or the StoryInterface special passage may be a better choice.

History:

Parameters: none

Examples:

Basic usage
UIBar.hide();
With stow
UIBar.hide().stow();

 UIBar.isHidden()boolean

Returns whether the UI bar is currently hidden.

History:

Parameters: none

Examples:

if (UIBar.isHidden()) {
	/* code to execute if the UI bar is hidden… */
}

if (!UIBar.isHidden()) {
	/* code to execute if the UI bar is not hidden… */
}

 UIBar.isStowed()boolean

Returns whether the UI bar is currently stowed.

History:

Parameters: none

Examples:

if (UIBar.isStowed()) {
	/* code to execute if the UI bar is stowed… */
}

if (!UIBar.isStowed()) {
	/* code to execute if the UI bar is not stowed… */
}

 UIBar.show()UIBar object

Shows the UI bar. Returns a reference to the UIBar object for chaining.

History:

Parameters: none

Examples:

Basic usage
UIBar.show();
With unstow
UIBar.unstow().show();

 UIBar.stow([noAnimation])UIBar object

Stows the UI bar, so that it takes up less space. Returns a reference to the UIBar object for chaining.

History:

Parameters:

Examples:

Basic usage
UIBar.stow();
With no animation
UIBar.stow(true);

 UIBar.unstow([noAnimation])UIBar object

Unstows the UI bar, so that it is fully accessible again. Returns a reference to the UIBar object for chaining.

History:

Parameters:

Examples:

Basic usage
UIBar.unstow();
With no animation
UIBar.unstow(true);

 UIBar.update()

Deprecated: This method has been deprecated and should no longer be used. See the UI.update() static method for its replacement.

History:

 Guide: State, Sessions, and Saving

SugarCube preserves the state of the story as it's being played in a number of ways to both prevent the loss of progress and allow players to save stories. This guide will detail how these features work.

 Story History

The story history is a collection of moments. A new moment is created whenever passage navigation occurs, and only when passage navigation occurs. Each moment contains data regarding the active passage and the state of all story variables—that is, the ones you use the $ sigil to interact with—as they exist when the moment is created. The history allows players to navigate through these moments.

The number of moments contained within the story history is, generally, limited, via the Config.history.maxStates setting. As new moments are added, older moments that exceed the maximum number are expired in order of age, oldest first. Expired moments are recorded in a separate expired collection and can no longer be navigated to. If you limit the moments within the history to 1, via setting Config.history.maxStates to 1, then there will only ever be one moment in the history, but passage navigation is still required for new moments to be created.

Note: All user functions and macros that check for the existence of moments within the history check both the story history and expired moments, so will work as expected even if the history is limited to a single moment as described above.

Saving the story records the story's state up until the last moment that was created. This is not necessarily the same as the current state of the story: because moment creation is tied to passage navigation, changes that occur between one passage navigation and the next are not part of the current moment and will not be preserved by a moment until the next navigation, when the next moment is created.

Consider the following:

:: one passage
<<set $var to 1>>

[[another passage]]

:: another passage
<<link "Click me!">>
	<<set $var to 2>>
<</link>>

In the above example, if you save the story after reaching the passage called another passage, the $var variable will be saved in the state as 1, as you would expect. If you click the link that sets the variable to 2, and then save the story, the $var variable will still be saved as 1, because a new moment has not yet been created.

 Playthrough Session

Note: Auto saves are occasionally confused with the playthrough session, but they are in fact distinct systems.

SugarCube automatically stores the current playthrough state to the browser's session storage whenever a new moment is created. This can be thought of as a special, temporary saved story, which is automatically deleted after the player's current browsing session ends. This temporary playthrough session is intended to prevent players from losing data. Some browsers, particularly mobile ones, will free up memory by unloading web pages that are running in the background. This functionally refreshes the webpage, and can cause users to lose their progress. When SugarCube is reloaded by the browser, it checks if a playthrough session exists and loads it to prevent any inadvertent loss of progress.

This feature also prevents players from losing progress if they try to use the browser back and forward buttons to navigate, or if they refresh their browser for any reason. The built-in Restart button, along with the methods UI.restart() and Engine.restart() are provided so that the story can be restarted without restoring a session.

To recap:

 Auto saves

Note: A playthrough session is occasionally confused with auto saves, but they are in fact distinct systems.

SugarCube features configurable auto saves. Auto saves are otherwise normal browser saves that automatically save either on every turn or only on certain turns, depending on how they're configured.

See: Config.saves.maxAutoSave setting, Config.saves.isAllowed setting, and Save.browser.auto API.

 What Happens When a Save is Loaded?

When a save is loaded, the state loaded from the save replaces the current state. This process is the same regardless of where the loaded state is coming from, be it a save or the playthrough session. The previous state is completely lost—the new state is not added to or combined with the current state, instead it replaces it in its entirety. The easiest way to understand this is to look at what happens when you make some changes to StoryInit and then load a save from before those changes were made. For example:

:: StoryInit
<<set $x to 0>>

:: Start
$$x is <<if def $x>> $x <<else>> undefined <</if>>

If you run the above, you'll see $x is 0. Create a save, then edit the code as follows:

:: StoryInit
<<set $x to 0>>
<<set $y to 1>>

:: Start
$$x is <<if def $x>> $x <<else>> undefined <</if>>
$$y is <<if def $y>> $y <<else>> undefined <</if>>

Running that, you'll see $x is 0 and $y is 1. Now, load the save from before the changes were made, and you'll see $y is undefined, since it doesn't exist at all in the loaded state.

 Refreshing and Restarting

Whenever your story is first started or, for any reason, restarted—e.g., the browser window/tab was refreshed/reloaded—it undergoes its startup sequence. Several things occur each and every time startup happens, regardless of whether or not a playthrough session will be restored or the starting passage run. First, the CSS, JavaScript, and Widget sections are processed. Next, any init-tagged passages and the StoryInit special passage are processed. Finally, one of two things happen (in order): the existing playthrough session is restored, if it exists, elsewise the starting passage is run.

Some users have the false impression that StoryInit is not run when the story is restarted when the playthrough session is restored or autosave is loaded. Code like <<set $y to 1>> seems to have no effect because the startup state is replaced by the of the incoming state, but they are still executed by the engine. You can see this effect by changing data outside the state. For example, let's return to the example above and change it again:

:: StoryInit
<<set $x to 0>>
<<set setup.y to 1>>

:: Start
$$x is <<if def $x>> $x <<else>> undefined <</if>>
setup.y is <<if def setup.y>> <<= setup.y>> <<else>> undefined <</if>>

You'll see that setup.y is being set to 1 and displayed properly regardless of whether you load a save or not, because it is not part of the state.

When the story is restarted by SugarCube rather than refreshed via the browser, the playthrough session, if any, is not loaded. The CSS, JavaScript, & Widget sections, any init-tagged passages, and the StoryInit special passage are processed, as usual, and then the starting passage is rendered.

 Guide: Non-generic object types (classes)

As a basic working definition, non-generic object types—i.e., classes—are instantiable objects whose own prototype is not Object—e.g., Array is a native non-generic object type.

Many of the commonly used native non-generic object types are already fully compatible with and supported for use within story variables—e.g., Array, Date, Map, and Set. All other non-generic object types, on the other hand, must be made compatible to be successfully stored within story variables.

Making custom non-generic object types fully compatible requires that two methods be added to their prototype, .clone() and .toJSON(), to support cloning—i.e., deep copying—instances of the type.

In both cases, since the end goal is roughly the same, this means creating a new instance of the base object type and populating it with clones of the original instance's data. There is no one size fits all example for either of these methods because an instance's properties, and the data contained therein, are what determine what you need to do.

See Also: The Serial.createReviver() method for additional information on implementing the .toJSON() method.

Examples: (not an exhaustive list)

 class-based syntax (newer, preferred)

Configuration object parameter constructor (w/ automatic copying of own data)

Here's a simple example whose constructor takes a single configuration object parameter:

window.Character = class Character {
	constructor(config) {
		// Set up our own data properties with some defaults.
		this.name = '(none)';
		this.race = '(none)';
		this.st   = 10;
		this.dx   = 10;
		this.iq   = 10;
		this.ht   = 10;
		this.hp   = 10;

		// Clone the given config object's own properties into our own properties.
		//
		// NOTE: We use the SugarCube built-in `clone()` function to make deep
		// copies of each of the properties' values.
		Object.keys(config).forEach(prop => {
			this[prop] = clone(config[prop]);
		});
	}

	clone() {
		// Return a new instance containing our own data.
		return new this.constructor(this);
	}

	toJSON() {
		// Return a code string that will create a new instance containing our
		// own data.
		//
		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
		// `Serial.createReviver()` call will trigger out of control recursion in
		// the serializer, so we must pass it a clone of our own data instead.
		var ownData = {};
		Object.keys(this).forEach(prop => {
			ownData[prop] = clone(this[prop]);
		});
		return Serial.createReviver(`new ${this.constructor.name}($ReviveData$)`, ownData);
	}
};

Creating a new instance of this Character example would be something like:

<<set $Joe to new Character({
	name : 'Joe the Barbarian',
	race : 'human',
	st   : 20,
	dx   : 12,
	iq   : 9,
	ht   : 18,
	hp   : 18
})>>

Discrete parameters constructor (w/ manual copying of own data)

Here's a simple example whose constructor takes multiple discrete parameters:

window.Character = class Character {
	constructor(
		name,
		race,
		st,
		dx,
		iq,
		ht,
		hp
	) {
		// Set up our own data properties with the given values or defaults.
		this.name = name ?? '(none)';
		this.race = race ?? '(none)';
		this.st   = st ?? 10;
		this.dx   = dx ?? 10;
		this.iq   = iq ?? 10;
		this.ht   = ht ?? 10;
		this.hp   = hp ?? 10;
	}

	clone() {
		// Return a new instance containing our own data.
		return new this.constructor(
			this.name,
			this.race,
			this.st,
			this.dx,
			this.iq,
			this.ht,
			this.hp
		);
	}

	toJSON() {
		// Return a code string that will create a new instance containing our
		// own data.
		return Serial.createReviver(String.format(
			'new {0}({1},{2},{3},{4},{5},{6},{7})',
			this.constructor.name,
			JSON.stringify(this.name),
			JSON.stringify(this.race),
			JSON.stringify(this.st),
			JSON.stringify(this.dx),
			JSON.stringify(this.iq),
			JSON.stringify(this.ht),
			JSON.stringify(this.hp)
		));
	}
};

Creating a new instance of this Character example would be something like:

<<set $Joe to new Character(
	'Joe the Barbarian',
	'human',
	20,
	12,
	9,
	18,
	18
)>>

 function-based syntax (classic, not recommended)

Configuration object parameter constructor (w/ automatic copying of own data)

Here's a simple example whose constructor takes a single configuration object parameter:

window.Character = function Character(config) {
	// Set up our own data properties with some defaults.
	this.name = '(none)';
	this.race = '(none)';
	this.st   = 10;
	this.dx   = 10;
	this.iq   = 10;
	this.ht   = 10;
	this.hp   = 10;

	// Clone the given config object's own properties into our own properties.
	//
	// NOTE: We use the SugarCube built-in `clone()` function to make deep
	// copies of each of the properties' values.
	Object.keys(config).forEach(function (prop) {
		this[prop] = clone(config[prop]);
	}, this);
};

Character.prototype.clone = function () {
	// Return a new instance containing our own data.
	return new Character(this);
};

Character.prototype.toJSON = function () {
	// Return a code string that will create a new instance containing our
	// own data.
	//
	// NOTE: Supplying `this` directly as the `reviveData` parameter to the
	// `Serial.createReviver()` call will trigger out of control recursion in
	// the serializer, so we must pass it a clone of our own data instead.
	var ownData = {};
	Object.keys(this).forEach(function (prop) {
		ownData[prop] = clone(this[prop]);
	}, this);
	return Serial.createReviver('new Character($ReviveData$)', ownData);
};

Creating a new instance of this Character example would be something like:

<<set $Joe to new Character({
	name : 'Joe the Barbarian',
	race : 'human',
	st   : 20,
	dx   : 12,
	iq   : 9,
	ht   : 18,
	hp   : 18
})>>

Discrete parameters constructor (w/ manual copying of own data)

Here's a simple example whose constructor takes multiple discrete parameters:

window.Character = function (
	name,
	race,
	st,
	dx,
	iq,
	ht,
	hp
) {
	// Set up our own data properties with the given values or defaults.
	this.name = name || '(none)';
	this.race = race || '(none)';
	this.st   = st || 10;
	this.dx   = dx || 10;
	this.iq   = iq || 10;
	this.ht   = ht || 10;
	this.hp   = hp || 10;
};

Character.prototype.clone = function () {
	// Return a new instance containing our own data.
	return new Character(
		this.name,
		this.race,
		this.st,
		this.dx,
		this.iq,
		this.ht,
		this.hp
	);
};

Character.prototype.toJSON = function () {
	// Return a code string that will create a new instance containing our
	// own data.
	return Serial.createReviver(String.format(
		'new Character({0},{1},{2},{3},{4},{5},{6})',
		JSON.stringify(this.name),
		JSON.stringify(this.race),
		JSON.stringify(this.st),
		JSON.stringify(this.dx),
		JSON.stringify(this.iq),
		JSON.stringify(this.ht),
		JSON.stringify(this.hp)
	));
};

Creating a new instance of this Character example would be something like:

<<set $Joe to new Character(
	'Joe the Barbarian',
	'human',
	20,
	12,
	9,
	18,
	18
)>>

 Guide: Tips

This is a collection of tips, from how-tos to best practices.

Suggestions for new entries may be submitted by creating a new issue at SugarCube's source code repository.

 Arbitrarily long return

Warning: Navigating back to a previous passage, for whatever reason, can be problematic. There's no way for the system to know ahead of time whether it's safe to re-execute a passage's contents. Even if it did know that, there's no way for it to know which operations may or may not have side-effects—e.g., changing variables. Thus, if you allow players to return to passages, then you should either: ensure the passages contain no code that has side-effects or wrap that code in something to prevent re-execution—e.g., <<if visited() is 1>>side-effects<</if>>.

Note: An alternative to navigating to passages to create menus, inventories, and the like would be to use the Dialog API.

When you have a situation where you're using a set of passages as some kind of menu/inventory/etc and it's possible for the player to interact with several of those passages, or even simply the same one multiple times, then returning them to the passage they were at before entering the menu can be problematic as they're possibly several passages removed from that originating passage—thus, the <<return>> macro and link constructs like [[Return|previous()]] will not work.

The most common way to resolve this arbitrarily long return issue is to use a bit of JavaScript to record the last non-menu passage the player visited into a story variable and then to create a link with that.

For instance, you may use one of the following examples—they both do the same thing—to record the last non-menu passage into the $return story variable.

Via JavaScript (Twine 2: the Story JavaScript, Twine 1/Twee: a script-tagged passage)

$(document).on(':passagestart', function (ev) {
	if (!ev.passage.tags.includes('noreturn')) {
		State.variables.return = ev.passage.name;
	}
});

Via macros (best used in the PassageReady special passage)

<<if not tags().includes("noreturn")>>
	<<set $return to passage()>>
<</if>>

You'll need to tag each and every one of your menu passages with noreturn—you may use any tag you wish (e.g., menu, inventory), just ensure you change the name in the code if you decide upon another. If necessary, you may also use multiple tags by switching from <Array>.includes() to <Array>.includesAny() in whichever of the above examples you choose to use.

In your menu passages, your long return links will simply reference the $return story variable, like so:

→ Using link markup
[[Return|$return]]

→ Using <<link>> macro (separate argument form)
<<link "Return" $return>><</link>>

Warning (Twine 2): Due to how the Twine 2 automatic passage creation feature currently works, using the link markup form will cause a passage named $return to be created that will need to be deleted. To avoid this problem, it's suggested that you use the separate argument form of the <<link>> macro in Twine 2—as shown above.

 Guide: Media Passages

Media passages are simply a way to embed media into your project—specially tagged passages that contain the data URI of a Base64-encoded media source. Audio, image, video, and VTT passages are supported.

For example, the following is the data URI of a Base64-encoded PNG image of a red dot (Red dot):

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHE
lEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

History:

 Creation

Generally, it's expected that you will use a compiler that supports the automatic creation of media passages, however, they may be created manually.

Automatically

Compilers supporting automatic creation of media passages:

Manually

Warning (Twine 2): Due to various limitations in its design, if you're using Twine 2 as your IDE/compiler, then it is strongly recommended that you do not create more than a few media passages and definitely do not use large sources.

To manually create a media passage:

  1. Create a new passage, which will only be used as a media passage—one per media source.
  2. Tag it with the appropriate media passage special tag, and only that tag—see below.
  3. Paste in the Base64-encoded media source as the passage's content.

See the MDN article Media formats for HTML audio and video for more information on formats commonly supported in browsers—pay special attention to the Browser compatibility section.

Media passage special tags

Note: As with all special tags, media passage tags are case sensitive, so their spelling and capitalization must be exactly as shown.

Passage type Tag
Audio passage Twine.audio
Image passage Twine.image
Video passage Twine.video
VTT passage Twine.vtt

 Guide: Icon Font

This guide is a reference to the icon font used by SugarCube, sc-icons.

The sc-icons font is a custom subset of Font Awesome Free (Solid) v5.15.2 (https://fontawesome.com), which is licensed under the SIL OFL 1.1 (https://scripts.sil.org/OFL).

 Icon Styling

The following CSS properties should used with any icon style rule.

font-family: sc-icons !important;
font-style: normal;
font-weight: normal;
font-variant: normal;
line-height: 1;
speak: never;
text-rendering: auto;
text-transform: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

 Icon Reference

Each icon's hexadecimal reference ID is listed below it. How to use the reference IDs varies based on where you're using them.

f002
f004
f005
f00c
f00d
f00e
f010
f011
f013
f015
f019
f023
f026
f027
f028
f02e
f048
f049
f04a
f04b
f04c
f04d
f04e
f050
f051
f052
f053
f054
f055
f056
f057
f058
f059
f05a
f05e
f060
f061
f062
f063
f065
f066
f067
f068
f06a
f06e
f070
f071
f077
f078
f089
f08d
f093
f09c
f0a0
f0c1
f0c2
f0c7
f0c9
f0d0
f0d7
f0d8
f0d9
f0da
f0e7
f0eb
f0fe
f10d
f10e
f110
f127
f137
f138
f139
f13a
f13e
f141
f142
f144
f146
f14a
f150
f151
f152
f185
f186
f188
f191
f1ab
f1dd
f1e0
f1f8
f204
f205
f240
f241
f242
f243
f244
f28b
f28d
f2ea
f2ed
f2f1
f2f9
f31e
f35d
f381
f382
f410
f55a
f56d
f56e
f56f
f574
f5c0
f6a9
f78c
f7d9
f829
f82a

 Guide: Harlowe to SugarCube

There are many differences between Harlowe and SugarCube, this guide will document some of the most critical you will need to account for if you're coming to SugarCube from a background in Harlowe.

 Macro Overview

Aside from general syntax, SugarCube macros do not use hooks, separate arguments differently, and don't allow other macros to be passed as arguments.

Macro Arguments

Like in Harlowe, some SugarCube macros accept expressions and others accept discreet arguments. In SugarCube, discreet arguments passed to a macro are separated by spaces instead of commas. To pass expressions or the results of functions to macros as an argument, you must wrap the expression in backquotes (`).

Additionally, macros in SugarCube do not return values, so macros cannot be used as arguments to other macros. SugarCube provides a variety of functions and methods that may be used instead, and standard JavaScript functions and methods may also be used.

Consider the following Harlowe code:

(link-goto: "Go somewhere else", (either: "this passage", "that passage", "the other passage"))

A version of the above code in SugarCube might look like this:

<<link "Go somewhere else" `either("this passage", "that passage", "the other passage")`>><</link>>

See: Macro Arguments.

Container Macros

Where Harlowe uses its hook syntax (square brackets) to associate a macro with its contents, SugarCube instead uses "container" macros—macros that can have content associated with them have opening and closing tags.

Consider the following Harlowe code:

(if: $var is 1)[
	The variable is 1.
]

In SugarCube, you instead open and close the <<if>> macro itself:

<<if $var is 1>>
	The variable is 1.
<</if>>

 Specific Macros

Some macros in Harlowe and SugarCube share a name but work a bit differently. We'll cover some of these differences below.

Link and Click Macros

SugarCube does not have any equivalents to Harlowe's (click:) family of macros. Additionally, SugarCube's normal <<link>> macro does not have an output element associated with it and is not, by default, a single-use link like its Harlowe equivalent. Both of these features can be constructed in SugarCube, however, using macros like <<linkreplace>> or by combining <<link>> macros with DOM macros. Additionally, SugarCube's link macro accepts a passage argument, that, if included, turns any <<link>> into something similar to Harlowe's (link-goto:) macro.

Consider the following Harlowe link macros:

(link: "Hey there.")[Hay is for horses.]
(link-repeat: "Get some money")[(set: $cash to it + 1)]
(link-goto: "Move on", "next passage")

The equivalent SugarCube code for each link might look something like this:

<<linkreplace "Hey there.">>Hay is for horses.<</linkreplace>>
<<link "Get some money">><<set $cash += 1>><</link>>
<<link "Move on" "next passage">><</link>>

SugarCube's <<link>> and <<button>> macros can also accept the link markup as an argument:

<<link [[Move on|next passage]]>><</link>>

DOM Macros

Note: Harlowe refers to these as "revision macros".

SugarCube's DOM macros can target any HTML element on the page, not just hooks, and unlike their Harlowe equivalents, they cannot target arbitrary strings. You can use custom style markup or HTML to create the elements, and then target them with a query selector.

Consider the following Harlowe code:

(set: _greetings to (a: "hi", "hello", "good day", "greetings"))\
The man says, "|target>[(either: ..._greetings)]."

{
(link-repeat: "Change")[
	(replace: ?target)[(either: ..._greetings)]
]
}

The equivalent SugarCube code to achieve a similar result would be:

<<set _greetings to ["hi", "hello", "good day", "greetings"]>>\
The man says, "@@#target;<<= _greetings.random()>>@@."

<<link "Change">>
	<<replace "#target">><<= _greetings.random()>><</replace>>
<</link>>

Note: The DOM macros do have a limitation that you should familiarize yourself with.

The Goto Macro

Harlowe's implementation of the (goto:) macro terminates the rendering passage. In SugarCube, the passage is not terminated, and anything in the code below the <<goto>> macro will have side effects.

Consider this Harlowe code:

:: some passage
(set: $count to 0)
(goto: "next")
(set: $count to it + 1)

:: next
$count <!-- 0 -->

In the above, the second (set:) macro is never run, and the $count variable remains at 0.

The equivalent SugarCube code works a bit differently:

:: some passage
<<set $count to 0>>
<<goto "next">>
<<set $count += 1>>

:: next
$count /* 1 */

SugarCube does not terminate the parsing of the calling passage, so some care is required when calling <<goto>>.

As with <<link>> and <<button>>, <<goto>> can accept link markup as its argument:

<<goto [[next]]>>

 User Input

SugarCube's user input macros, like <<textbox>>, cannot be nested inside a <<set>> macro, as you might do with a (prompt:) and a (set:) in Harlowe. Instead, the macro is passed a receiver variable which is set to the value input by the user.

For example, if you wanted to ask the user to enter a name, your code may look like this in Harlowe:

(set: $name to (prompt: "What is your name?", "Frank"))

In SugarCube, you would likely want to use the <<textbox>> macro instead, and pass $name in as the receiving variable:

<label>What is your name? <<textbox "$name" "Frank">></label>

Harlowe's newer input macros, like (dropdown:) and (cycling-link:) use "bound" variables, which are similar in concept to SugarCube's receiver variables.

 Data Types

Harlowe's implementation of data types differs significantly from SugarCube's. A data type refers to the "type" of data a variable is holding, such as a number, a string, an array, or anything else. Harlowe has stricter typing than SugarCube, requiring authors to call macros like (str:) or (num:) on variables to change their type. SugarCube, like JavaScript, uses dynamic typing.

Dynamic Typing

SugarCube, like JavaScript, will try to make sense of expressions passed to it by coercing their values if necessary:

<<set $number to 1>>
<<set $string to "2">>
<<= $string + $number>> /* "21" */

In the above case, since the string value "2" cannot be added to a number value, the number value is coerced into a string, and the two strings are then concatenated. In Harlowe, the same operation will yield an error:

(set: $number to 1)
(set: $string to "2")
(print: $string + $number) <!-- error! -->

You must convert the values to the same type in Harlowe. In SugarCube you can convert them if you need to.

In Harlowe:

(set: $number to 1)
(set: $string to "2")
(print: $string + $number) <!-- error! -->
(print: $string + (str: $number)) <!-- "21" -->
(print: (num: $string) + $number) <!-- 3 -->

In SugarCube:

<<set $number to 1>>
<<set $string to "2">>
<<= $string + $number>> /* "21" */
<<= $string + String($number)>> /* "21" */
<<= Number($string) + $number>> /* 3 */

Arrays, Datamaps, and Datasets

Harlowe's arrays, datamaps, and datasets are functionally similar to JavaScript Arrays, Maps, and Sets, but with a few key differences. SugarCube requires authors to define and work with these data types using the standard JavaScript methods rather than providing macros for them.

Using an array in Harlowe:

(set: $array to (a:))
(set: $array to it + (a: "something"))
(if: $array contains "something")[…]

In SugarCube:

<<set $array to []>>
<<run $array.push("something")>>
<<if $array.includes("something")>>…<</if>>

Using a datamap in Harlowe:

(set: $map to (dm: "key", "value"))
(set: $map's key to "another value")
(if: $map contains key)[…]

In SugarCube:

<<set $map to new Map([["key", "value"]])>>
<<run $map.set("key", "another value")>>
<<if $map.has("key")>>…<</if>>

SugarCube also allows the use of JavaScript generic objects, which may be better in some situations than a map:

<<set $object to { key : "value" }>>
<<set $object.key to "another value">>
<<if $object.hasOwnProperty("key")>>…<</if>>

Another important difference in the way Harlowe handles its non-primitive data types like arrays, datamaps, and datasets is that they are passed by value rather than passed by reference.

Consider the following Harlowe code:

(set: $player to (dm: "hp", 100, "mp", 50))
(set: $partyMember to $player)
(set: $partyMember's hp to it - 50)
(print: $player's hp) <!-- 100 -->
(print: $partyMember's hp) <!-- 50 -->

As you can see, Harlowe creates a deep copy/clone of its non-primitive data types each time they're modified.

In SugarCube, both variables would still point to the same underlying object—at least initially (see below):

<<set $player to { hp : 100, mp : 50 }>>
<<set $partyMember to $player>>
<<set $partyMember.hp -= 50>>
$player.hp /* 50 */
$partyMember.hp /* 50 */

SugarCube does eventually clone its non-primitive data types as well, but does at the start of passage navigation, rather than each time they're modified.

 Guide: Test Mode

History:

 Introduction

In test mode, SugarCube will wrap all macros, and some non-macro markup—e.g., link & image markup—within additional HTML elements, called "debug views" ("views" for short). Views make their associated code visible, thus providing onscreen feedback—they may also be hovered over which, generally, exposes additional information about the underlying code.

Warning: Because of the additional HTML elements added by the debug views, some nested markup and selectors may be broken. This only affects test mode.

Tip: In versions of SugarCube ≥v2.23.0, the debugging interface offers additional tools, namely variable watches and arbitrary history navigation.

 Enabling Test Mode

Automatically

In Tweego

To enable test mode, use the test option (-t, --test).

In Twine 2 (≥v2.2)

To enable test mode from the Stories screen, click on the story's gear menu and select the Test Story menu item.

To enable test mode from the story editor/map screen, click on the Test menu item (right side of the bottom bar).

To enable test mode from the story editor/map screen while starting at a specific passage, hover over a passage and select the menu item.

In Twine 2 (<v2.2)

To enable test mode from the Stories screen, click on the story's gear menu and select the Test Play menu item.

To enable test mode from the story editor/map screen, click on the Test menu item (right side of the bottom bar).

To enable test mode from the story editor/map screen while starting at a specific passage, hover over a passage and select the menu item.

In Twine 1

To enable test mode while starting at a specific passage, right-click on a passage and select the Test Play From Here context menu item.

Note: Unfortunately, due to limitations in the current release of Twine 1, the Build menu's Test Play menu item is not able to trigger test mode. You may, however, simply use the Test Play From Here context menu item on the Start passage to achieve the same result.

Manually

You may forcibly enable test mode manually by setting the Config object's debug property to true. For example:

Config.debug = true; // forcibly enable test mode

See: The Config.debug setting for more information.

 Debug Bar (≥v2.23.0)

The debug bar (bottom right corner of the page) allows you to: watch the values of story and temporary variables, toggle the debug views, and jump to any moment/turn within the history.

The variable watch panel may be toggled via the Watch  button. To add a watch for a variable, type its name into the Add field and then either press enter/return or click the button—n.b. depending on the age of your browser, you may also see a list of all current variables when interacting with the Add field. To delete a watch, click the button next to its name in the watch panel. To add watches for all current variables, click the button. To delete all current watches, click the button.

The debug views may be toggled via the Views  button.

To jump to any moment/turn within the available history, select the moment/turn from the Turn select field.

 Debug Views (≤v2.22.0)

The debug views themselves may be toggled on and off (default: on) via the Debug View button (top of the UI bar).

If you've removed/hidden the UI bar, a construct like the following will allow you to toggle the views on and off:

<<button "Toggle Debug Views">><<script>>DebugView.toggle()<</script>><</button>>

Note: That will only toggles the views, test mode must still be enabled first.

 Guide: TypeScript

TypeScript bindings for SugarCube APIs can found as the Definitely Typed package: @types/twine-sugarcube.

To install the package via NPM, use the following command:

npm install --save-dev @types/twine-sugarcube

 Guide: Installation

This is a reference on how to install SugarCube in Tweego, Twine 2, and Twine 1/Twee.

Note (Twine 2): Newer versions of Twine 2 come bundled with a version of SugarCube v2, so you only need to read these instructions if you want to install a newer version of SugarCube v2 than is bundled or a non-standard release.

 Local Install For Tweego

See Tweego's documentation for more information.

 Local Install For Twine 2

There are two primary branches of Twine 2 as far as SugarCube is concerned:

Regardless of the version of Twine 2 you're using, follow these instructions to install a local copy of SugarCube v2:

  1. Download the current version of SugarCube v2 for Twine 2—comes as a ZIP archive. NOTE: There are separate downloads for Twine ≥2.1 and Twine 2.0, so you must ensure that you download the one that matches your version of Twine 2 or you will likely run into issues.
  2. Extract the archive to a safe location on your computer and make note of the path to it. Make sure to keep the files together if you move them out of the included directory.
  3. Launch Twine 2.
  4. Click on the Formats link in the Twine 2 sidebar.
  5. In the dialog that opens, click on the Add a New Format tab.
  6. Finally, paste a file URL to the format.js file, based upon the path from step #2, into the textbox and click the +Add button (see below for examples).

UNIX (and similar) file URL examples

Note: If constructing the file URL from a shell path, ensure that either it does not contain escapes or you properly convert them into the correct URL percent-encoded form.

If the full path to the contents of the archive is something like:

/home/soandso/Twine/StoryFormats/SugarCube-2/format.js

Then the file URL to it would be:

file:///home/soandso/Twine/StoryFormats/SugarCube-2/format.js

Windows file URL examples

If the full path to the contents of the archive is something like:

C:\Users\soandso\Documents\Twine\StoryFormats\SugarCube-2\format.js

Then the file URL to it would be (note the changed slashes):

file:///C:/Users/soandso/Documents/Twine/StoryFormats/SugarCube-2/format.js

 Online Install For Twine 2

The online SugarCube install, delivered by the jsDelivr CDN, supports only versions of Twine 2 ≥2.1.

Copy the following URL and paste it into the Add a New Format tab of the Formats menu, from Twine 2's sidebar.

URL: https://cdn.jsdelivr.net/gh/tmedwards/sugarcube-2/dist/format.js

 Local Install For Twine 1/Twee

Follow these instructions to install a local copy of SugarCube v2:

  1. Download the current version of SugarCube v2 for Twine 1/Twee—comes as a ZIP archive.
  2. Go to your Twine 1/Twee installation directory and open the targets directory within.
  3. Move the downloaded archive into the targets directory and extract it, included directory and all.

If you followed the steps correctly, within Twine 1/Twee's targets directory you should now have a sugarcube-2 directory, which contains several files—e.g., header.html, sugarcube-2.py, etc.

Warning (Twine 1):

Due to a flaw in the current release of Twine 1/Twee (v1.4.2), if you rename the directory included in the archive (or simply copy its contents to your current SugarCube v2 install), then you must ensure that the file with the extension .py (the story format's custom Twine 1 Header class file) within is named the same as the directory—i.e., the name of the directory and .py file must match.

For example, if the name of SugarCube's directory is sugarcube, then the name of the .py file within must be sugarcube.py. Similarly, if the directory is sugarcube-2, then the name of the .py file within must be sugarcube-2.py. Etc.

The directory and .py file names within the archive available for download are already properly matched—as sugarcube-2 and sugarcube-2.py—and to avoid issues it recommended that you simply do not rename them.

 Guide: Code Updates

This is a reference on how to update existing SugarCube code to work with newer versions of SugarCube.

Note: The majority of newer SugarCube versions do not have any changes that would require an update. For those versions that do, the updates are normally completely elective and may be addressed at your leisure, or not at all. Sometimes there are breaking changes, however, and these must be addressed immediately.

 Updating to any version ≥2.37.0 from a lesser version

Warning: Some changes within this version are breaking changes that you must address immediately, while others are elective changes that you may address at your leisure. All breaking changes will be so noted.

Note: The removals herein are of features that have been deprecated for years. Most are v1 compatibility APIs that have always been deprecated in v2. Nothing of value has been lost.

Deprecated legacy APIs

API Change
browser BREAKING: This deprecated legacy API has been removed. Its replacement is Browser.
config BREAKING: This deprecated legacy API has been removed. Its replacement is Config.
has BREAKING: This deprecated legacy API has been removed. Its replacement is Has.
History BREAKING: This deprecated legacy API has been removed. Its replacement is State.
state BREAKING: This deprecated legacy API has been removed. Its replacement is State.
tale BREAKING: This deprecated legacy API has been removed. Its replacement is Story.
TempVariables BREAKING: This deprecated legacy API has been removed. Its replacement is State.temporary.

Array API

Method Change
Array.random() BREAKING: This deprecated static method has been removed. See the <Array>.random() instance method.
<Array>.contains() BREAKING: The polyfill for this instance method has been removed. See the <Array>.includes() instance method.
<Array>.containsAll() BREAKING: This instance method has been removed. See the <Array>.includesAll() instance method.
<Array>.containsAny() BREAKING: This instance method has been removed. See the <Array>.includesAny() instance method.
<Array>.flatten() BREAKING: This instance method has been removed. See the <Array>.flat() instance method while providing a depth parameter of Infinity.

Config API

Setting Change
Config.macros.ifAssignError This setting has been deprecated and should no longer be used. See the Config.enableOptionalDebugging setting for its replacement.
Config.passages.descriptions This setting has been deprecated and should no longer be used. See the Config.saves.descriptions setting for its replacement.
Config.saves.autoload This setting has been deprecated and should no longer be used. The default UI now includes a Continue button, which loads the latest save. If disabling or replacing the default UI, see the Save.browser.continue() method to replicate the functionality.
Config.saves.autosave This setting has been deprecated and should no longer be used. See the Config.saves.maxAutoSaves setting to set the number of available auto saves and the Config.saves.isAllowed setting to control when new auto saves are created.
Config.saves.isAllowed This setting, to which you assign a function, has had the parameters provided to the assigned function changed. See its documentation entry for details.
Config.saves.slots This setting has been deprecated and should no longer be used. See the Config.saves.maxSlotSaves setting for its replacement.
Config.saves.tryDiskOnMobile This setting has been deprecated and should no longer be used. Saving to disk on mobile devices is now unconditionally enabled.

Dialog API

Method Change
Dialog.addClickHandler() BREAKING: This deprecated static method has been removed.
Dialog.setup() This static method has been deprecated in favor of the Dialog.create() static method.

JSON API

Method Change
JSON.reviveWrapper() This static method has been deprecated in favor of the Serial.createReviver() static method.

Macro library

Macro Change
<<actions>> This macro has been deprecated.
<<choice>> This macro has been deprecated.
<<click>> BREAKING: This deprecated macro has been removed. See the <<link>> macro.
<<display>> BREAKING: This deprecated macro has been removed. See the <<include>> macro.
<<forget>> BREAKING: This deprecated macro has been removed. See the forget() function.
<<remember>> BREAKING: This deprecated macro has been removed. See the memorize() and recall() functions.
<<setplaylist>> BREAKING: This deprecated macro has been removed. See the <<createplaylist>> macro.
<<stopallaudio>> BREAKING: This deprecated macro has been removed. See the <<masteraudio>> macro.

MacroContext API

Member Change
<MacroContext>.contextHas() This instance method has been deprecated in favor of the <MacroContext>.contextSome() instance method.
<MacroContext>.contextSelect() This instance method has been deprecated in favor of the <MacroContext>.contextFind() instance method.
<MacroContext>.contextSelectAll() This instance method has been deprecated in favor of the <MacroContext>.contextFilter() instance method.

Number API

Method Change
<Number>.clamp() This instance method has been deprecated. See the Math.clamp() static method.

Passage API

Member Change
<Passage>.domId This instance property has been deprecated in favor of the <Passage>.id instance property.
<Passage>.title This instance property has been deprecated in favor of the <Passage>.name instance property.
<Passage>.description() This instance method has been deprecated.

Save API

Method Change
Save.get() BREAKING: This static method has been removed. See the Save.browser.auto.entries() and Save.browser.slot.entries() static methods for its closest replacements.
Save.clear() This static method has been deprecated in favor of the Save.browser.clear() static method.
Save.ok() This static method has been deprecated in favor of the Save.browser.isEnabled() static method.
Save.autosave.delete() This static method has been deprecated in favor of the Save.browser.auto.delete() static method.
Save.autosave.get() This static method has been deprecated in favor of the Save.browser.auto.get() static method.
Save.autosave.has() This static method has been deprecated in favor of the Save.browser.auto.has() static method.
Save.autosave.load() This static method has been deprecated in favor of the Save.browser.auto.load() static method.
Save.autosave.ok() This static method has been deprecated in favor of the Save.browser.auto.isEnabled() static method.
Save.autosave.save() This static method has been deprecated in favor of the Save.browser.auto.save() static method.
Save.slots.length This static property has been deprecated in favor of the Config.saves.maxSlotSaves setting.
Save.slots.count() This static method has been deprecated in favor of the Save.browser.slot.size static getter.
Save.slots.delete() This static method has been deprecated in favor of the Save.browser.slot.delete() static method.
Save.slots.get() This static method has been deprecated in favor of the Save.browser.slot.get() static method.
Save.slots.has() This static method has been deprecated in favor of the Save.browser.slot.has() static method.
Save.slots.isEmpty() This static method has been deprecated in favor of the Save.browser.slot.size static getter.
Save.slots.load() This static method has been deprecated in favor of the Save.browser.slot.load() static method.
Save.slots.ok() This static method has been deprecated in favor of the Save.browser.slot.isEnabled() static method.
Save.slots.save() This static method has been deprecated in favor of the Save.browser.slot.save() static method.
Save.export() This static method has been deprecated in favor of the Save.disk.save() static method.
Save.import() This static method has been deprecated in favor of the Save.disk.load() static method.
Save.deserialize() This static method has been deprecated in favor of the Save.base64.load() static method.
Save.serialize() This static method has been deprecated in favor of the Save.base64.save() static method.

Scripting API

Method Change
Scripting.desugar() BREAKING: The undocumented is not to isnot operator mapping has been removed.
Scripting.parse() This static method has been deprecated in favor of the Scripting.desugar() static method.

State API

Method Change
State.backward() BREAKING: This deprecated static method has been removed.
State.display() BREAKING: This deprecated static method has been removed.
State.forward() BREAKING: This deprecated static method has been removed.
State.initPRNG() BREAKING: This deprecated static method has been removed.
State.play() BREAKING: This deprecated static method has been removed.
State.restart() BREAKING: This deprecated static method has been removed.
State.show() BREAKING: This deprecated static method has been removed.

Story API

Member Change
Story.domId This static property has been deprecated in favor of the Story.id static property.
Story.title This static property has been deprecated in favor of the Story.name static property.

StoryInterface special passage

POSSIBLY BREAKING: The default UI's <div id="story" role="main"> container has been made a core part of the base UI, for both native and custom end user markup. As a consequence, this means that the custom markup generated by using the StoryInterface special passage may no longer itself contain a #story element and will be added to the core #story container, rather than <body>.

An example of the new hierarchy:

<body>
	<div id="story" role="main">
		<!-- StoryInterface elements added here -->
	</div>
</body>

It is strongly recommended that you review your selectors related to the generated markup, both for DOM manipulation and CSS styling, to ensure that they're still functional. Primarily this will affect selectors that use the child combinator (>) with a body parent—e.g., body > … where is one the elements within your custom markup.

This change was required to fix a bug related to the interaction between open dialogs and <body>.

String API

Method Change
<String>.readBracketedList() BREAKING: This deprecated instance method has been removed.

UI API

Method Change
UI.buildAutoload() This static method has been deprecated.
UI.addClickHandler() BREAKING: This deprecated static method has been removed.
UI.body() BREAKING: This deprecated static method has been removed.
UI.close() BREAKING: This deprecated static method has been removed.
UI.isOpen() BREAKING: This deprecated static method has been removed.
UI.open() BREAKING: This deprecated static method has been removed.
UI.resize() BREAKING: This deprecated static method has been removed.
UI.setStoryElements() BREAKING: This deprecated static method has been removed.
UI.setup() BREAKING: This deprecated static method has been removed.
UI.stow() BREAKING: This deprecated static method has been removed.
UI.unstow() BREAKING: This deprecated static method has been removed.

UIBar toggle and history button markup & styles

The icons of the UI bar history control buttons have been changed from being text content in their markup to a part of their styles, as with most native SugarCube icon bearing buttons.

The styles of the UI bar toggle and history control buttons have been simplified. If you've customized the styling of any of these buttons, then it is strongly recommended that you review the ui-bar.css file for exact details.

 Updating to any version ≥2.36.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Config API

Property Change
Config.history.maxStates This setting property has been updated to disallow unlimited states.
Config.saves.onLoad This setting property has been deprecated in favor of the Save Events API, specifically the Save.onLoad.add static method.
Config.saves.onSave This setting property has been deprecated in favor of the Save Events API, specifically the Save.onSave.add static method.

Macro library

Macro Change
<<widget>> The special $args story variable has been deprecated in favor of the _args_ temporary variable.

 Updating to any version ≥2.31.0 from a lesser version

Warning: All changes within this version are breaking changes that you must address immediately.

Parser library

Parser Change
HTML tag The parser has been updated to disallow use of the evaluation attribute directive on the data-setter content attribute. They were never supposed to be combined.

 Updating to any version ≥2.30.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Config API

Property Change
Config.saves.autosave This setting property has been updated to accept function values and its acceptance of string values has been deprecated. String values will still be accepted for further releases of v2, however, switching to an array is recommended—e.g., the string value "autosave" would become the array ["autosave"]. See the Config.saves.autosave property for more information.

 Updating to any version ≥2.29.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Dialog API

Method Change
Dialog.addClickHandler() This method has been deprecated and should no longer be used. The core of what it does is simply to wrap a call to Dialog.open() within a call to <jQuery>.ariaClick(), which can be done directly and with greater flexibility.

Macro library

Macro Change
<<track>> (of: <<createplaylist>>) The <<createplaylist>> macro's <<track>> child macro has had its copy keyword deprecated in favor of own. See the <<createplaylist>> macro for more information.
<<forget>> The <<forget>> macro has been deprecated in favor of the forget() function.
<<remember>> The <<remember>> macro has been deprecated in favor of the memorize() and recall() functions.

Method library

Method Change
<Array>.flatten() This method has been deprecated in favor of the <Array>.flat() method.

State API

Method Change
State.initPRNG() This method has been deprecated in favor of the State.prng.init() method.

 Updating to any version ≥2.28.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Macro library

Macro Change
<<cacheaudio>> The <<cacheaudio>> macro's original optional format specifier syntax, format:formatId;…, has been deprecated in favor of the new syntax, formatId|…..

 Updating to any version ≥2.20.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Method library

Method Change
Array.random() This method has been deprecated and should no longer be used. In general, look to the <Array>.random() method instead. If you need a random member from an array-like object or iterable, use the Array.from() method to convert it to an array, then use <Array>.random()—e.g., Array.from(something).random().

 Updating to any version ≥2.15.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Macro library

Macro Change
<<display>> The <<display>> macro has been deprecated in favor of the <<include>> macro.

 Updating to any version ≥2.10.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Method library

Method Change
<Array>.contains() The <Array>.contains() method has been deprecated in favor of the <Array>.includes() method.
<Array>.containsAll() The <Array>.containsAll() method has been deprecated in favor of the <Array>.includesAll() method.
<Array>.containsAny() The <Array>.containsAny() method has been deprecated in favor of the <Array>.includesAny() method.

strings object

The strings API object has been replaced by the l10nStrings object. See the Localization guide for more information.

 Updating to any version ≥2.8.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

Macro library

Macro Change
<<click>> The <<click>> macro has been deprecated in favor of the <<link>> macro.
<<playlist>> The <<playlist>> macro has had its argument list changed, for compatibility with <<createplaylist>>. See the <<playlist>> macro for more information.
<<setplaylist>> The <<setplaylist>> macro has been deprecated in favor of the <<createplaylist>> macro.
<<stopallaudio>> The <<stopallaudio>> macro has been deprecated in favor of <<audio ":all" stop>>. See the <<audio>> macro for more information.

 Updating to any version ≥2.5.0 from a lesser version

All changes within this version are elective changes that you may address at your leisure.

config API

The config API has been renamed Config for better consistency with the other APIs.

State API

Several State API methods have moved to the new Engine API. See the Engine API docs for more information.

Old State method New Engine method
State.backward() Engine.backward()
State.display() Engine.display()1
State.forward() Engine.forward()
State.play() Engine.play()
State.restart() Engine.restart()
State.show() Engine.show()
  1. While the Engine.display() static methods exists, it, like State.display() before it, is deprecated. See the Engine.play() static method for the replacement. NOTE: Their parameters differ, so read the description of Engine.play() carefully.

UI API

Several UI API methods have moved to the new Dialog API. See the Dialog API docs for more information.

Old UI method New Dialog method
UI.addClickHandler() Dialog.addClickHandler()
UI.body() Dialog.body()
UI.close() Dialog.close()
UI.isOpen() Dialog.isOpen()
UI.open() Dialog.open()
UI.setup() Dialog.setup()

 Updating to any version ≥2.0.0 from a lesser version

Warning: All changes within this version are breaking changes that you must address immediately.

HTML & CSS

The HTML & CSS have undergone significant changes. See the HTML and CSS docs for more information.

Special passages

Passage Change
MenuOptions The MenuOptions special passage has been removed. See the Options system section for more information.
MenuShare The MenuShare special passage has been removed. See the StoryShare special passage.
MenuStory The MenuStory special passage has been removed. See the StoryMenu special passage.

config object

The config object has been renamed to Config and some of its properties have also changed. See the Config API docs for more information.

Property Change
config.altPassageDescription Changed the config.altPassageDescription property to Config.passages.descriptions.
config.disableHistoryControls Changed the config.disableHistoryControls property to Config.history.controls. This change also inverted the meaning of the property, so take note of that.
config.disableHistoryTracking Replaced the config.disableHistoryTracking property with Config.history.maxStates. The new property works differently, so take note of that.
config.displayPassageTitles Changed the config.displayPassageTitles property to Config.passages.displayTitles.
config.historyMode Removed the config.historyMode property. It's unnecessary since there's now only one history mode in the engine.
config.macros.disableIfAssignmentError Changed the config.macros.disableIfAssignmentError property to Config.macros.ifAssignmentError. This change also inverted the meaning of the property, so take note of that.
config.passageTransitionOut Changed the config.passageTransitionOut property to Config.passages.transitionOut. Additionally, it no longer accepts a boolean value, which has been replaced by the name of the animating property (necessitated by changes to browser handling of transition animations).
config.startPassage Changed the config.startPassage property to Config.passages.start.
config.updatePageElements Changed the config.updatePageElements property to Config.ui.updateStoryElements.

History object & prototype (instance: state)

The History API object has been renamed to State and some of its methods have also changed. Furthermore, it is no longer instantiated into the legacy state object—which still exists, so legacy code will continue to work. See the State API docs for more information.

The State.display() method—formerly state.display()—is no longer overridable, meaning it cannot be wrapped—e.g., the "StoryRegions" 3rd-party add-ons do this. See Navigation Events or Tasks.

Calling the State.prng.init() method—formerly History.initPRNG()—outside of story initialization will now throw an error. It has always been required that the call happen during story initialization, the only change is the throwing of the error.

Seedable pseudo-random number generator (PRNG)

Math.random() is no longer replaced by the integrated seedable PRNG when State.prng.init() is called. See either the built-in functions random() & randomFloat() or the State.random() method, if you need direct access to the PRNG—since it returns a call to either Math.random() or the seedable PRNG, as appropriate.

Macro system

The Macros API object has been renamed to Macro and several of its methods have also changed, for better consistency with the other APIs. Furthermore, it is no longer instantiated into the legacy macros object—which still exists, so SugarCube-compatible legacy macros will continue to work. See the Macro API docs for more information.

Macro library

Macro Change
<<back>> & <<return>> Replaced the ungainly link text syntax—<<back::linktext "…">>/<<return::linktext "…">>—with l10nStrings object properties—l10nStrings.macroBackText and l10nStrings.macroReturnText.
<<if>> & <<elseif>> The <<if>> macro will now, optionally, return an error if the JavaScript = assignment operator is used (default: enabled). Configured via the Config.macros.ifAssignmentError config property.
Options macros The various Options macros have been removed. See the Options system section for more information.

Options system

The entire Options system—MenuOptions special passage, options special variable, and associated macros—has been scrapped for numerous reasons—it was always a hack, required copious amounts of boilerplate code to be useful, etc. It is replaced by the Setting API and settings special variable. See the Setting API docs for more information.

Save system

The SaveSystem API object has been renamed to Save and several of its methods have also changed, for better consistency with the other APIs. See the Save API docs for more information.

UI system

The UISystem API object has been split into two APIs Dialog and UI, and some of its methods have also changed. In particular, the parameter list for the Dialog.setup() method has changed. See the Dialog API and UI API docs for more information.

 Guide: Localization

This is a reference for localizing SugarCube's default UI text, in general, and its l10nStrings object specifically.

Note: If you're simply looking to download ready-to-use localizations, see SugarCube's website (under Downloads > Localizations).

History:

A note about strings vs. l10nStrings

Prior to SugarCube v2.10.0, the strings localization object was named strings. The new l10nStrings object has a simpler, flatter, set of properties and better support for replacement strings. Unfortunately, this means that the two objects are incompatible.

To ensure backwards compatibility of existing strings objects, if one exists within a project's scripts, the older object is mapped to the new l10nStrings object.

 Translation Notes

The capitalization and punctuation used within the default replacement strings is deliberate, especially within the error and warning strings. You would do well to keep your translations similar when possible.

Replacement patterns have the format {NAME}—e.g., {textIdentity}—where NAME is the name of a property within either the l10nStrings object or, in a few cases, an object supplied locally where the string is used—these instances will be commented.

By convention, properties starting with an underscore—e.g., _warningOutroDegraded—are used as templates, only being included within other localized strings. Feel free to add your own if that makes localization easier—e.g., for gender, plurals, and whatnot. As an example, the default replacement strings make use of this to handle various warning intros and outros.

In use, replacement patterns are replaced recursively, so replacement strings may contain patterns whose replacements contain other patterns. Because replacement is recursive, care must be taken to ensure infinite loops are not created—the system will detect an infinite loop and throw an error.

 Usage

Properties on the strings localization object (l10nStrings) should be set within your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) to override the defaults.

For the template that should be used as the basis of localizations, see the locale/l10n-template.js file @github.com.

Examples

// Changing the project's reported identity to "story"
l10nStrings.textIdentity = "story";

// Changing the text of all dialog OK buttons to "Eeyup"
l10nStrings.ok = "Eeyup";

// Localizing the title of the Restart dialog (n.b. machine translations, for example only)
l10nStrings.restartTitle = "Neustart";  // German (de)
l10nStrings.restartTitle = "Reiniciar"; // Spanish (es)
×
拖拽到此处完成下载
图片将完成下载
AIX智能下载器