这是用户在 2024-6-10 24:51 为 https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?
Skip to content
inkle  /   ink  /  
Open in github.dev Open in a new github.dev tab Open in codespace

Files

t

Latest commit

ec462d9 · Jun 5, 2024

History

History
3454 lines (2250 loc) · 121 KB

WritingWithInk.md

File metadata and controls

3454 lines (2250 loc) · 121 KB

Writing with ink 用墨水书写

Table of Contents 目录

Introduction 介绍

ink is a scripting language built around the idea of marking up pure-text with flow in order to produce interactive scripts.
ink 是一种脚本语言,其理念是用流标记纯文本以生成交互式脚本。

At its most basic, it can be used to write a Choose Your Own-style story, or a branching dialogue tree. But its real strength is in writing dialogues with lots of options and lots of recombination of the flow.
最基本的是,它可以用来编写“选择你自己的”风格的故事,或分支对话树。但它的真正优势在于编写具有大量选项和大量流程重组的对话。

ink offers several features to enable non-technical writers to branch often, and play out the consequences of those branches, in both minor and major ways, without fuss.
ink 提供了多种功能,使非技术作者能够经常分支,并以次要和主要方式发挥这些分支的后果,而无需大惊小怪。

The script aims to be clean and logically ordered, so branching dialogue can be tested "by eye". The flow is described in a declarative fashion where possible.
该脚本的目标是简洁且逻辑有序,因此分支对话可以“通过肉眼”进行测试。流程尽可能以声明方式描述。

It's also designed with redrafting in mind; so editing a flow should be fast.
它的设计也考虑到了重新起草;所以编辑流程应该很快。

Part One: The Basics 第一部分:基础知识

1) Content 1)内容

The simplest ink script 最简单的水墨字

The most basic ink script is just text in a .ink file.
最基本的墨迹脚本只是 .ink 文件中的文本。

Hello, world!

On running, this will output the content, and then stop.
运行时,会输出内容,然后停止。

Text on separate lines produces new paragraphs. The script:
单独行上的文本会生成新段落。剧本:

Hello, world!
Hello?
Hello, are you there?

produces output that looks the same.
产生看起来相同的输出。

Comments 评论

By default, all text in your file will appear in the output content, unless specially marked up.
默认情况下,文件中的所有文本都将出现在输出内容中,除非特别标记。

The simplest mark-up is a comment. ink supports two kinds of comment. There's the kind used for someone reading the code, which the compiler ignores:
最简单的标记是注释。 ink支持两种注释。有一种用于阅读代码的人,编译器会忽略它:

"What do you make of this?" she asked.

// Something unprintable...

"I couldn't possibly comment," I replied.

/*
	... or an unlimited block of text
*/

and there's the kind used for reminding the author what they need to do, that the compiler prints out during compilation:
还有一种用于提醒作者需要做什么,编译器在编译期间会打印出:

TODO: Write this section properly!

Tags 标签

Text content from the game will appear 'as is' when the engine runs. However, it can sometimes be useful to mark up a line of content with extra information to tell the game what to do with that content.
当引擎运行时,游戏中的文本内容将“按原样”显示。然而,有时用额外信息标记一行内容来告诉游戏如何处理该内容可能很有用。

ink provides a simple system for tagging lines of content, with hashtags.
ink 提供了一个简单的系统,用于使用主题标签来标记内容行。

A line of normal game-text. # colour it blue

These don't show up in the main text flow, but can be read off by the game and used as you see fit. See Running Your Ink for more information.
这些不会出现在主文本流中,但可以由游戏读取并根据您认为合适的方式使用。有关详细信息,请参阅运行 Ink。

2) Choices 2)选择

Input is offered to the player via text choices. A text choice is indicated by an * character.
通过文本选择向玩家提供输入。文本选择由 * 字符指示。

If no other flow instructions are given, once made, the choice will flow into the next line of text.
如果没有给出其他流程指令,一旦做出,选择将流入下一行文本。

Hello world!
*	Hello back!
	Nice to hear from you!

This produces the following game:
这会产生以下游戏:

Hello world!
1: Hello back!

> 1
Hello back!
Nice to hear from you!

By default, the text of a choice appears again, in the output.
默认情况下,选择的文本会再次出现在输出中。

Suppressing choice text 抑制选择文本

Some games separate the text of a choice from its outcome. In ink, if the choice text is given in square brackets, the text of the choice will not be printed into response.
有些游戏将选择的文本与其结果分开。在 ink 中,如果选择文本在方括号中给出,则选择的文本将不会打印到响应中。

Hello world!
*	[Hello back!]
	Nice to hear from you!

produces 产生

Hello world!
1: Hello back!

> 1
Nice to hear from you!

Advanced: mixing choice and output text
高级:混合选择和输出文本

The square brackets in fact divide up the option content. What's before is printed in both choice and output; what's inside only in choice; and what's after, only in output. Effectively, they provide alternative ways for a line to end.
方括号实际上划分了选项内容。之前的内容都会打印在选择和输出中;里面的东西只能选择;以及之后的内容,仅在输出中。实际上,它们提供了结束线路的替代方式。

Hello world!
*	Hello [back!] right back to you!
	Nice to hear from you!

produces: 产生:

Hello world!
1: Hello back!
> 1
Hello right back to you!
Nice to hear from you!

This is most useful when writing dialogue choices:
这在编写对话选择时最有用:

"What's that?" my master asked.
*	"I am somewhat tired[."]," I repeated.
	"Really," he responded. "How deleterious."

produces: 产生:

"What's that?" my master asked.
1: "I am somewhat tired."
> 1
"I am somewhat tired," I repeated.
"Really," he responded. "How deleterious."

Multiple Choices 多种选择

To make choices really choices, we need to provide alternatives. We can do this simply by listing them:
为了做出真正的选择,我们需要提供替代方案。我们可以简单地通过列出它们来做到这一点:

"What's that?" my master asked.
*	"I am somewhat tired[."]," I repeated.
	"Really," he responded. "How deleterious."
*	"Nothing, Monsieur!"[] I replied.
	"Very good, then."
*  "I said, this journey is appalling[."] and I want no more of it."
	"Ah," he replied, not unkindly. "I see you are feeling frustrated. Tomorrow, things will improve."

This produces the following game:
这会产生以下游戏:

"What's that?" my master asked.

1: "I am somewhat tired."
2: "Nothing, Monsieur!"
3: "I said, this journey is appalling."

> 3
"I said, this journey is appalling and I want no more of it."
"Ah," he replied, not unkindly. "I see you are feeling frustrated. Tomorrow, things will improve."

The above syntax is enough to write a single set of choices. In a real game, we'll want to move the flow from one point to another based on what the player chooses. To do that, we need to introduce a bit more structure.
上面的语法足以编写一组选择。在真实的游戏中,我们希望根据玩家的选择将流程从一个点移动到另一个点。为此,我们需要引入更多的结构。

3) Knots 3) 结

Pieces of content are called knots
内容片段称为结

To allow the game to branch we need to mark up sections of content with names (as an old-fashioned gamebook does with its 'Paragraph 18', and the like.)
为了让游戏有分支,我们需要用名称标记内容的各个部分(就像老式游戏书的“第 18 段”等)。

These sections are called "knots" and they're the fundamental structural unit of ink content.
这些部分称为“节”,它们是墨内容的基本结构单位。

Writing a knot 写一个结

The start of a knot is indicated by two or more equals signs, as follows.
结的开始由两个或多个等号表示,如下所示。

=== top_knot ===

(The equals signs on the end are optional; and the name needs to be a single word with no spaces.)
(末尾的等号是可选的;名称必须是单个单词,不能有空格。)

The start of a knot is a header; the content that follows will be inside that knot.
结的起点是标题;接下来的内容将在该结内。

=== back_in_london ===

We arrived into London at 9.45pm exactly.

Advanced: a knottier "hello world"
高级:更复杂的“hello world”

When you start an ink file, content outside of knots will be run automatically. But knots won't. So if you start using knots to hold your content, you'll need to tell the game where to go. We do this with a divert arrow ->, which is covered properly in the next section.
当您启动 ink 文件时,knots 之外的内容将自动运行。但结不会。因此,如果您开始使用结来保存内容,您需要告诉游戏该去哪里。我们使用转向箭头 -> 来完成此操作,下一节将对此进行正确介绍。

The simplest knotty script is:
最简单的棘手脚本是:

-> top_knot

=== top_knot ===
Hello world!

However, ink doesn't like loose ends, and produces a warning on compilation and/or run-time when it thinks this has happened. The script above produces this on compilation:
但是,ink 不喜欢松散的结果,并且当它认为发生这种情况时会在编译和/或运行时生成警告。上面的脚本在编译时会产生以下结果:

WARNING: Apparent loose end exists where the flow runs out. Do you need a '-> END' statement, choice or divert? on line 3 of tests/test.ink

and this on running:
运行时:

Runtime error in tests/test.ink line 3: ran out of content. Do you need a '-> DONE' or '-> END'?

The following plays and compiles without error:
以下播放并编译没有错误:

=== top_knot ===
Hello world!
-> END

-> END is a marker for both the writer and the compiler; it means "the story flow should now stop".
-> END 是编写器和编译器的标记;这意味着“故事流现在应该停止”。

4) Diverts 4) 改道

Knots divert to knots 结转向结

You can tell the story to move from one knot to another using ->, a "divert arrow". Diverts happen immediately without any user input.
您可以使用 -> (“转向箭头”)讲述从一个结移动到另一个结的故事。无需任何用户输入即可立即发生转移。

=== back_in_london ===

We arrived into London at 9.45pm exactly.
-> hurry_home

=== hurry_home ===
We hurried home to Savile Row as fast as we could.

Diverts are invisible 转移是看不见的

Diverts are intended to be seamless and can even happen mid-sentence:
转移的目的是无缝的,甚至可以发生在句子中间:

=== hurry_home ===
We hurried home to Savile Row -> as_fast_as_we_could

=== as_fast_as_we_could ===
as fast as we could.

produces the same line as above:
产生与上面相同的行:

We hurried home to Savile Row as fast as we could.

Glue 胶水

The default behaviour inserts line-breaks before every new line of content. In some cases, however, content must insist on not having a line-break, and it can do so using <>, or "glue".
默认行为会在每个新内容行之前插入换行符。然而,在某些情况下,内容必须坚持没有换行符,并且可以使用 <> 或“粘合”来实现。

=== hurry_home ===
We hurried home <>
-> to_savile_row

=== to_savile_row ===
to Savile Row
-> as_fast_as_we_could

=== as_fast_as_we_could ===
<> as fast as we could.

also produces: 还生产:

We hurried home to Savile Row as fast as we could.

You can't use too much glue: multiple glues next to each other have no additional effect. (And there's no way to "negate" a glue; once a line is sticky, it'll stick.)
你不能使用太多的胶水:多个相邻的胶水不会产生额外的效果。 (而且没有办法“否定”胶水;一旦线粘起来,它就会粘住。)

5) Branching The Flow 5)分支流程

Basic branching 基本分支

Combining knots, options and diverts gives us the basic structure of a choose-your-own game.
将结、选项和转移结合在一起,为我们提供了选择您自己的游戏的基本结构。

=== paragraph_1 ===
You stand by the wall of Analand, sword in hand.
* [Open the gate] -> paragraph_2
* [Smash down the gate] -> paragraph_3
* [Turn back and go home] -> paragraph_4

=== paragraph_2 ===
You open the gate, and step out onto the path.

...

Branching and joining 分支和加入

Using diverts, the writer can branch the flow, and join it back up again, without showing the player that the flow has rejoined.
使用转移,编写者可以对流程进行分支,然后再次将其重新加入,而无需向玩家显示流程已重新加入。

=== back_in_london ===

We arrived into London at 9.45pm exactly.

*	"There is not a moment to lose!"[] I declared.
	-> hurry_outside

*	"Monsieur, let us savour this moment!"[] I declared.
	My master clouted me firmly around the head and dragged me out of the door.
	-> dragged_outside

*	[We hurried home] -> hurry_outside


=== hurry_outside ===
We hurried home to Savile Row -> as_fast_as_we_could


=== dragged_outside ===
He insisted that we hurried home to Savile Row
-> as_fast_as_we_could


=== as_fast_as_we_could ===
<> as fast as we could.

The story flow 故事流程

Knots and diverts combine to create the basic story flow of the game. This flow is "flat" - there's no call-stack, and diverts aren't "returned" from.
结点和转移结合起来创造了游戏的基本故事流程。这个流程是“扁平的”——没有调用堆栈,并且转移不会从中“返回”。

In most ink scripts, the story flow starts at the top, bounces around in a spaghetti-like mess, and eventually, hopefully, reaches a -> END.
在大多数墨迹脚本中,故事流程从顶部开始,在意大利面条般的混乱中弹跳,并希望最终达到 -> END

The very loose structure means writers can get on and write, branching and rejoining without worrying about the structure that they're creating as they go. There's no boiler-plate to creating new branches or diversions, and no need to track any state.
非常松散的结构意味着作者可以继续写作、分支和重新加入,而不必担心他们边写边创建的结构。没有用于创建新分支或转移的样板,也不需要跟踪任何状态。

Advanced: Loops 高级:循环

You absolutely can use diverts to create looped content, and ink has several features to exploit this, including ways to make the content vary itself, and ways to control how often options can be chosen.
您绝对可以使用转移来创建循环内容,并且 Ink 有多种功能可以利用这一点,包括使内容自行变化的方法,以及控制选择选项频率的方法。

See the sections on Varying Text and Conditional Choices for more information.
有关详细信息,请参阅有关变化文本和条件选择的部分。

Oh, and the following is legal and not a great idea:
哦,以下是合法的,但不是一个好主意:

=== round ===
and
-> round

6) Includes and Stitches 6) 包含和缝合

Knots can be subdivided 结可以细分

As stories get longer, they become more confusing to keep organised without some additional structure.
随着故事变得越来越长,如果没有额外的结构,它们就会变得更加混乱。

Knots can include sub-sections called "stitches". These are marked using a single equals sign.
结可以包括称为“针迹”的子部分。它们使用单​​个等号进行标记。

=== the_orient_express ===
= in_first_class
	...
= in_third_class
	...
= in_the_guards_van
	...
= missed_the_train
	...

One could use a knot for a scene, for instance, and stitches for the events within the scene.
例如,人们可以为场景使用结,为场景内的事件使用缝线。

Stitches have unique names
缝线有独特的名称

A stitch can be diverted to using its "address".
可以将针迹转移到使用其“地址”。

*	[Travel in third class]
	-> the_orient_express.in_third_class

*	[Travel in the guard's van]
	-> the_orient_express.in_the_guards_van

The first stitch is the default
默认第一针

Diverting to a knot which contains stitches will divert to the first stitch in the knot. So:
转向包含针迹的结将转向该结中的第一个针迹。所以:

*	[Travel in first class]
	"First class, Monsieur. Where else?"
	-> the_orient_express

is the same as:
是相同的:

*	[Travel in first class]
	"First class, Monsieur. Where else?"
	-> the_orient_express.in_first_class

(...unless we move the order of the stitches around inside the knot!)
(......除非我们移动结内的针迹顺序!)

You can also include content at the top of a knot outside of any stitch. However, you need to remember to divert out of it - the engine won't automatically enter the first stitch once it's worked its way through the header content.
您还可以在任何针迹之外的结顶部添加内容。但是,您需要记住要避开它 - 一旦引擎完成了标题内容,它就不会自动进入第一针。

=== the_orient_express ===

We boarded the train, but where?
*	[First class] -> in_first_class
*	[Second class] -> in_second_class

= in_first_class
	...
= in_second_class
	...

Local diverts 本地改道

From inside a knot, you don't need to use the full address for a stitch.
从结内部,您不需要使用完整的针迹地址。

-> the_orient_express

=== the_orient_express ===
= in_first_class
	I settled my master.
	*	[Move to third class]
		-> in_third_class

= in_third_class
	I put myself in third.

This means stitches and knots can't share names, but several knots can contain the same stitch name. (So both the Orient Express and the SS Mongolia can have first class.)
这意味着针迹和结不能共享名称,但多个结可以包含相同的针迹名称。 (所以东方快车和蒙古号都可以坐头等舱。)

The compiler will warn you if ambiguous names are used.
如果使用不明确的名称,编译器会警告您。

Script files can be combined
脚本文件可以合并

You can also split your content across multiple files, using an include statement.
您还可以使用 include 语句将内容拆分到多个文件中。

INCLUDE newspaper.ink
INCLUDE cities/vienna.ink
INCLUDE journeys/orient_express.ink

Include statements should always go at the top of a file, and not inside knots.
Include 语句应始终位于文件顶部,而不是位于结内。

There are no rules about what file a knot must be in to be diverted to. (In other words, separating files has no effect on the game's namespacing).
没有关于结必须转移到哪个文件的规则。 (换句话说,分隔文件对游戏的命名空间没有影响)。

7) Varying Choices 7)不同的选择

Choices can only be used once
选项只能使用一次

By default, every choice in the game can only be chosen once. If you don't have loops in your story, you'll never notice this behaviour. But if you do use loops, you'll quickly notice your options disappearing...
默认情况下,游戏中的每个选择只能选择一次。如果您的故事中没有循环,您将永远不会注意到这种行为。但如果你确实使用循环,你很快就会发现你的选项消失了......

=== find_help ===

	You search desperately for a friendly face in the crowd.
	*	The woman in the hat[?] pushes you roughly aside. -> find_help
	*	The man with the briefcase[?] looks disgusted as you stumble past him. -> find_help

produces: 产生:

You search desperately for a friendly face in the crowd.

1: The woman in the hat?
2: The man with the briefcase?

> 1
The woman in the hat pushes you roughly aside.
You search desperately for a friendly face in the crowd.

1: The man with the briefcase?

>

... and on the next loop you'll have no options left.
...在下一个循环中你将没有选择。

Fallback choices 后备选择

The above example stops where it does, because the next choice ends up in an "out of content" run-time error.
上面的示例就停止了,因为下一个选择最终会出现“内容不足”运行时错误。

> 1
The man with the briefcase looks disgusted as you stumble past him.
You search desperately for a friendly face in the crowd.

Runtime error in tests/test.ink line 6: ran out of content. Do you need a '-> DONE' or '-> END'?

We can resolve this with a 'fallback choice'. Fallback choices are never displayed to the player, but are 'chosen' by the game if no other options exist.
我们可以通过“后备选择”来解决这个问题。后备选项永远不会向玩家显示,但如果不存在其他选项,则由游戏“选择”。

A fallback choice is simply a "choice without choice text":
后备选择只是“没有选择文本的选择”:

*	-> out_of_options

And, in a slight abuse of syntax, we can make a default choice with content in it, using an "choice then arrow":
而且,稍微滥用语法,我们可以使用“选择然后箭头”对其中的内容进行默认选择:

* 	->
	Mulder never could explain how he got out of that burning box car. -> season_2

Example of a fallback choice
后备选择示例

Adding this into the previous example gives us:
将其添加到前面的示例中可以得到:

=== find_help ===

	You search desperately for a friendly face in the crowd.
	*	The woman in the hat[?] pushes you roughly aside. -> find_help
	*	The man with the briefcase[?] looks disgusted as you stumble past him. -> find_help
	*	->
		But it is too late: you collapse onto the station platform. This is the end.
		-> END

and produces: 并产生:

You search desperately for a friendly face in the crowd.

1: The woman in the hat?
2: The man with the briefcase?

> 1
The woman in the hat pushes you roughly aside.
You search desperately for a friendly face in the crowd.

1: The man with the briefcase?

> 1
The man with the briefcase looks disgusted as you stumble past him.
You search desperately for a friendly face in the crowd.
But it is too late: you collapse onto the station platform. This is the end.

Sticky choices 粘性选择

The 'once-only' behaviour is not always what we want, of course, so we have a second kind of choice: the "sticky" choice. A sticky choice is simply one that doesn't get used up, and is marked by a + bullet.
当然,“一次性”行为并不总是我们想要的,因此我们有第二种选择:“粘性”选择。粘性选择就是不会用完的选择,并用 + 项目符号标记。

=== homers_couch ===
	+	[Eat another donut]
		You eat another donut. -> homers_couch
	*	[Get off the couch]
		You struggle up off the couch to go and compose epic poetry.
		-> END

Fallback choices can be sticky too.
后备选择也可能很棘手。

=== conversation_loop
	*	[Talk about the weather] -> chat_weather
	*	[Talk about the children] -> chat_children
	+	-> sit_in_silence_again

Conditional Choices 有条件的选择

You can also turn choices on and off by hand. ink has quite a lot of logic available, but the simplest tests is "has the player seen a particular piece of content".
您还可以手动打开和关闭选项。 ink 有相当多的可用逻辑,但最简单的测试是“玩家是否看到了特定的内容”。

Every knot/stitch in the game has a unique address (so it can be diverted to), and we use the same address to test if that piece of content has been seen.
游戏中的每个结/针都有一个唯一的地址(因此可以转移到),我们使用相同的地址来测试该内容是否已被看到。

*	{ not visit_paris } 	[Go to Paris] -> visit_paris
+ 	{ visit_paris 	 } 		[Return to Paris] -> visit_paris

*	{ visit_paris.met_estelle } [ Telephone Mme Estelle ] -> phone_estelle

Note that the test knot_name is true if any stitch inside that knot has been seen.
请注意,如果看到该结内有任何针迹,则测试 knot_name 为真。

Note also that conditionals don't override the once-only behaviour of options, so you'll still need sticky options for repeatable choices.
另请注意,条件不会覆盖选项的一次性行为,因此您仍然需要粘性选项来进行可重复的选择。

Advanced: multiple conditions
高级:多种条件

You can use several logical tests on an option; if you do, all the tests must all be passed for the option to appear.
您可以对一个选项使用多个逻辑测试;如果这样做,则必须通过所有测试才能显示该选项。

*	{ not visit_paris } 	[Go to Paris] -> visit_paris
+ 	{ visit_paris } { not bored_of_paris }
	[Return to Paris] -> visit_paris

Logical operators: AND and OR
逻辑运算符:AND 和 OR

The above "multiple conditions" are really just conditions with an the usual programming AND operator. Ink supports and (also written as &&) and or (also written as ||) in the usual way, as well as brackets.
上面的“多个条件”实际上只是使用通常的编程 AND 运算符的条件。 Ink 以通常的方式支持 and (也写为 && )和 or (也写为 || )以及括号。

*	{ not (visit_paris or visit_rome) && (visit_london || visit_new_york) } [ Wait. Go where? I'm confused. ] -> visit_someplace

For non-programmers X and Y means both X and Y must be true. X or Y means either or both. We don't have a xor.
对于非程序员 X and Y 意味着X和Y都必须为真。 X or Y 表示其中之一或两者。我们没有 xor

You can also use the standard ! for not, though it'll sometimes confuse the compiler which thinks {!text} is a once-only list. We recommend using not because negated boolean tests are never that exciting.
您还可以对 not 使用标准 ! ,尽管它有时会让编译器感到困惑,因为编译器认为 {!text} 是一次性列表。我们建议使用 not 因为否定布尔测试从来都不是那么令人兴奋。

Advanced: knot/stitch labels are actually read counts
高级:结/针标签实际上是读取计数

The test: 考试:

*	{seen_clue} [Accuse Mr Jefferson]

is actually testing an integer and not a true/false flag. A knot or stitch used this way is actually an integer variable containing the number of times the content at the address has been seen by the player.
实际上是测试一个整数而不是真/假标志。以这种方式使用的结或针实际上是一个整数变量,其中包含玩家看到该地址处的内容的次数。

If it's non-zero, it'll return true in a test like the one above, but you can also be more specific as well:
如果它非零,它将在上面的测试中返回 true,但您也可以更具体:

* {seen_clue > 3} [Flat-out arrest Mr Jefferson]

Advanced: more logic 高级:更多逻辑

ink supports a lot more logic and conditionality than covered here - see the section on variables and logic.
ink 支持比这里介绍的更多的逻辑和条件 - 请参阅有关变量和逻辑的部分。

8) Variable Text 8) 可变文本

Text can vary 文字可能会有所不同

So far, all the content we've seen has been static, fixed pieces of text. But content can also vary at the moment of being printed.
到目前为止,我们看到的所有内容都是静态的、固定的文本片段。但内容在印刷时也可能会有所不同。

Sequences, cycles and other alternatives
序列、循环和其他替代方案

The simplest variations of text are provided by alternatives, which are selected from depending on some kind of rule. ink supports several types. Alternatives are written inside {...} curly brackets, with elements separated by | symbols (vertical divider lines).
最简单的文本变体是由替代项提供的,这些替代项是根据某种规则进行选择的。 ink 支持多种类型。替代方案写在 { ... } 大括号内,元素由 | 符号(垂直分隔线)分隔。

These are only useful if a piece of content is visited more than once!
仅当某个内容被多次访问时这些才有用!

Types of alternatives 替代方案的类型

Sequences (the default): 序列(默认):

A sequence (or a "stopping block") is a set of alternatives that tracks how many times its been seen, and each time, shows the next element along. When it runs out of new content it continues the show the final element.
序列(或“停止块”)是一组替代项,用于跟踪其被看到的次数,并且每次都会显示下一个元素。当新内容用完时,它会继续显示最终元素。

The radio hissed into life. {"Three!"|"Two!"|"One!"|There was the white noise racket of an explosion.|But it was just static.}

{I bought a coffee with my five-pound note.|I bought a second coffee for my friend.|I didn't have enough money to buy any more coffee.}

Cycles (marked with a &):
循环(用 & 标记):

Cycles are like sequences, but they loop their content.
循环就像序列,但它们循环其内容。

It was {&Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday} today.

Once-only (marked with a !):
一次性(用 ! 标记):

Once-only alternatives are like sequences, but when they run out of new content to display, they display nothing. (You can think of a once-only alternative as a sequence with a blank last entry.)
一次性替代方案就像序列一样,但是当它们用完要显示的新内容时,它们就不会显示任何内容。 (您可以将一次性替代方案视为最后一个条目为空的序列。)

He told me a joke. {!I laughed politely.|I smiled.|I grimaced.|I promised myself to not react again.}

Shuffles (marked with a ~):
随机播放(用 ~ 标记):

Shuffles produce randomised output.
洗牌产生随机输出。

I tossed the coin. {~Heads|Tails}.

Features of Alternatives 替代品的特点

Alternatives can contain blank elements.
替代项可以包含空白元素。

I took a step forward. {!||||Then the lights went out. -> eek}

Alternatives can be nested.
替代方案可以嵌套。

The Ratbear {&{wastes no time and |}swipes|scratches} {&at you|into your {&leg|arm|cheek}}.

Alternatives can include divert statements.
替代方案可以包括转移语句。

I {waited.|waited some more.|snoozed.|woke up and waited more.|gave up and left. -> leave_post_office}

They can also be used inside choice text:
它们也可以用在选择文本中:

+ 	"Hello, {&Master|Monsieur Fogg|you|brown-eyes}!"[] I declared.

(...with one caveat; you can't start an option's text with a {, as it'll look like a conditional.)
(...有一个警告;您不能以 { 开始选项的文本,因为它看起来像一个条件。)

(...but the caveat has a caveat, if you escape a whitespace \ before your { ink will recognise it as text.)
(...但是警告有一个警告,如果您在 { 墨迹将其识别为文本之前转义了空格 \ 。)

+\	{&They headed towards the Sandlands|They set off for the desert|The party followed the old road South}

Examples 例子

Alternatives can be used inside loops to create the appearance of intelligent, state-tracking gameplay without particular effort.
可以在循环内部使用替代方案来创建智能、状态跟踪游戏的外观,而无需付出特别的努力。

Here's a one-knot version of whack-a-mole. Note we use once-only options, and a fallback, to ensure the mole doesn't move around, and the game will always end.
这是打地鼠的单结版本。请注意,我们使用一次性选项和后备方案,以确保鼹鼠不会四处移动,并且游戏始终会结束。

=== whack_a_mole ===
	{I heft the hammer.|{~Missed!|Nothing!|No good. Where is he?|Ah-ha! Got him! -> END}}
	The {&mole|{&nasty|blasted|foul} {&creature|rodent}} is {in here somewhere|hiding somewhere|still at large|laughing at me|still unwhacked|doomed}. <>
	{!I'll show him!|But this time he won't escape!}
	* 	[{&Hit|Smash|Try} top-left] 	-> whack_a_mole
	*  [{&Whallop|Splat|Whack} top-right] -> whack_a_mole
	*  [{&Blast|Hammer} middle] -> whack_a_mole
	*  [{&Clobber|Bosh} bottom-left] 	-> whack_a_mole
	*  [{&Nail|Thump} bottom-right] 	-> whack_a_mole
	*   ->
    	    Then you collapse from hunger. The mole has defeated you!
            -> END

produces the following 'game':
产生以下“游戏”:

I heft the hammer.
The mole is in here somewhere. I'll show him!

1: Hit top-left
2: Whallop top-right
3: Blast middle
4: Clobber bottom-left
5: Nail bottom-right

> 1
Missed!
The nasty creature is hiding somewhere. But this time he won't escape!

1: Splat top-right
2: Hammer middle
3: Bosh bottom-left
4: Thump bottom-right

> 4
Nothing!
The mole is still at large.
1: Whack top-right
2: Blast middle
3: Clobber bottom-left

> 2
Where is he?
The blasted rodent is laughing at me.
1: Whallop top-right
2: Bosh bottom-left

> 1
Ah-ha! Got him!

And here's a bit of lifestyle advice. Note the sticky choice - the lure of the television will never fade:
这里有一些生活方式建议。请注意粘性选择 - 电视的诱惑永远不会消失:

=== turn_on_television ===
I turned on the television {for the first time|for the second time|again|once more}, but there was {nothing good on, so I turned it off again|still nothing worth watching|even less to hold my interest than before|nothing but rubbish|a program about sharks and I don't like sharks|nothing on}.
+	[Try it again]	 		-> turn_on_television
*	[Go outside instead]	-> go_outside_instead

=== go_outside_instead ===
-> END

Sneak Preview: Multiline alternatives
抢先预览:多行替代方案

ink has another format for making alternatives of varying content blocks, too. See the section on multiline blocks for details.
ink 还有另一种格式来制作不同内容块的替代品。有关详细信息,请参阅有关多行块的部分。

Conditional Text 条件文本

Text can also vary depending on logical tests, just as options can.
正如选项一样,文本也可能根据逻辑测试而变化。

{met_blofeld: "I saw him. Only for a moment." }

and 

"His real name was {met_blofeld.learned_his_name: Franz|a secret}."

These can appear as separate lines, or within a section of content. They can even be nested, so:
它们可以显示为单独的行,也可以显示在内容的一部分中。它们甚至可以嵌套,所以:

{met_blofeld: "I saw him. Only for a moment. His real name was {met_blofeld.learned_his_name: Franz|kept a secret}." | "I missed him. Was he particularly evil?" }

can produce either: 可以产生:

"I saw him. Only for a moment. His real name was Franz."

or: 或者:

"I saw him. Only for a moment. His real name was kept a secret."

or: 或者:

"I missed him. Was he particularly evil?"

9) Game Queries and Functions
9) 游戏查询和函数

ink provides a few useful 'game level' queries about game state, for use in conditional logic. They're not quite parts of the language, but they're always available, and they can't be edited by the author. In a sense, they're the "standard library functions" of the language.
ink 提供了一些关于游戏状态的有用的“游戏级别”查询,用于条件逻辑。它们不完全是语言的一部分,但它们始终可用,并且作者无法对其进行编辑。从某种意义上说,它们是该语言的“标准库函数”。

The convention is to name these in capital letters.
惯例是用大写字母命名它们。

CHOICE_COUNT()

CHOICE_COUNT returns the number of options created so far in the current chunk. So for instance.
CHOICE_COUNT 返回当前块中迄今为止创建的选项数量。举例来说。

*	{false} Option A
* 	{true} Option B
*  {CHOICE_COUNT() == 1} Option C

produces two options, B and C. This can be useful for controlling how many options a player gets on a turn.
产生两个选项,B 和 C。这对于控制玩家在回合中获得的选项数量很有用。

TURNS() 转弯()

This returns the number of game turns since the game began.
这将返回自游戏开始以来的游戏回合数。

TURNS_SINCE(-> knot) TURNS_SINCE(-> 结)

TURNS_SINCE returns the number of moves (formally, player inputs) since a particular knot/stitch was last visited.
TURNS_SINCE 返回自上次访问特定结/针迹以来的移动次数(正式而言,玩家输入)。

A value of 0 means "was seen as part of the current chunk". A value of -1 means "has never been seen". Any other positive value means it has been seen that many turns ago.
值 0 表示“被视为当前块的一部分”。值 -1 表示“从未见过”。任何其他正值都意味着在很多回合之前就已经看到过。

*	{TURNS_SINCE(-> sleeping.intro) > 10} You are feeling tired... -> sleeping
* 	{TURNS_SINCE(-> laugh) == 0}  You try to stop laughing.

Note that the parameter passed to TURNS_SINCE is a "divert target", not simply the knot address itself (because the knot address is a number - the read count - not a location in the story...)
请注意,传递给 TURNS_SINCE 的参数是“转移目标”,而不仅仅是结地址本身(因为结地址是一个数字 - 读取计数 - 不是故事中的位置......)

TODO: (requirement of passing -c to the compiler)
TODO:(将 -c 传递给编译器的要求)

Sneak preview: using TURNS_SINCE in a function
预览:在函数中使用 TURNS_SINCE

The TURNS_SINCE(->x) == 0 test is so useful it's often worth wrapping it up as an ink function.
TURNS_SINCE(->x) == 0 测试非常有用,通常值得将其包装为墨迹函数。

=== function came_from(-> x)
	~ return TURNS_SINCE(x) == 0

The section on functions outlines the syntax here a bit more clearly but the above allows you to say things like:
关于函数的部分更清楚地概述了此处的语法,但上面的内容允许您说以下内容:

* {came_from(->  nice_welcome)} 'I'm happy to be here!'
* {came_from(->  nasty_welcome)} 'Let's keep this quick.'

... and have the game react to content the player saw just now.
...并让游戏对玩家刚才看到的内容做出反应。

SEED_RANDOM()

For testing purposes, it's often useful to fix the random number generator so ink will produce the same outcomes every time you play. You can do this by "seeding" the random number system.
出于测试目的,修复随机数生成器通常很有用,这样每次玩时 Ink 都会产生相同的结果。您可以通过“播种”随机数系统来做到这一点。

~ SEED_RANDOM(235)

The number you pass to the seed function is arbitrary, but providing different seeds will result in different sequences of outcomes.
传递给种子函数的数字是任意的,但提供不同的种子将导致不同的结果序列。

Advanced: more queries 高级:更多查询

You can make your own external functions, though the syntax is a bit different: see the section on functions below.
您可以创建自己的外部函数,尽管语法有点不同:请参阅下面有关函数的部分。

Part 2: Weave 第 2 部分:编织

So far, we've been building branched stories in the simplest way, with "options" that link to "pages".
到目前为止,我们一直在以最简单的方式构建分支故事,其中“选项”链接到“页面”。

But this requires us to uniquely name every destination in the story, which can slow down writing and discourage minor branching.
但这要求我们对故事中的每个目的地进行唯一命名,这会减慢写作速度并阻止小分支的出现。

ink has a much more powerful syntax available, designed for simplifying story flows which have an always-forwards direction (as most stories do, and most computer programs don't).
ink 有更强大的语法,旨在简化具有始终向前方向的故事流程(就像大多数故事一样,而大多数计算机程序则不然)。

This format is called "weave", and its built out of the basic content/option syntax with two new features: the gather mark, -, and the nesting of choices and gathers.
这种格式称为“weave”,它是根据基本内容/选项语法构建的,具有两个新功能:聚集标记 - 以及选择和聚集的嵌套。

1) Gathers 1) 聚集

Gather points gather the flow back together
聚集点将流量聚集在一起

Let's go back to the first multi-choice example at the top of this document.
让我们回到本文档顶部的第一个多项选择示例。

"What's that?" my master asked.
	*	"I am somewhat tired[."]," I repeated.
		"Really," he responded. "How deleterious."
	*	"Nothing, Monsieur!"[] I replied.
	*  "I said, this journey is appalling[."] and I want no more of it."
		"Ah," he replied, not unkindly. "I see you are feeling frustrated. Tomorrow, things will improve."

In a real game, all three of these options might well lead to the same conclusion - Monsieur Fogg leaves the room. We can do this using a gather, without the need to create any new knots, or add any diverts.
在真实的游戏中,所有这三个选项很可能会得出相同的结论——福格先生离开房间。我们可以使用聚集来做到这一点,而不需要创建任何新的结,或添加任何转向。

"What's that?" my master asked.
	*	"I am somewhat tired[."]," I repeated.
		"Really," he responded. "How deleterious."
	*	"Nothing, Monsieur!"[] I replied.
		"Very good, then."
	*  "I said, this journey is appalling[."] and I want no more of it."
	"Ah," he replied, not unkindly. "I see you are feeling frustrated. Tomorrow, things will improve."

-	With that Monsieur Fogg left the room.

This produces the following playthrough:
这会产生以下游戏流程:

"What's that?" my master asked.

1: "I am somewhat tired."
2: "Nothing, Monsieur!"
3: "I said, this journey is appalling."

> 1
"I am somewhat tired," I repeated.
"Really," he responded. "How deleterious."
With that Monsieur Fogg left the room.

Options and gathers form chains of content
选项和集合形成内容链

We can string these gather-and-branch sections together to make branchy sequences that always run forwards.
我们可以将这些聚集和分支部分串在一起,以形成始终向前运行的分支序列。

=== escape ===
I ran through the forest, the dogs snapping at my heels.

	* 	I checked the jewels[] were still in my pocket, and the feel of them brought a spring to my step. <>

	*  I did not pause for breath[] but kept on running. <>

	*	I cheered with joy. <>

- 	The road could not be much further! Mackie would have the engine running, and then I'd be safe.

	*	I reached the road and looked about[]. And would you believe it?
	* 	I should interrupt to say Mackie is normally very reliable[]. He's never once let me down. Or rather, never once, previously to that night.

-	The road was empty. Mackie was nowhere to be seen.

This is the most basic kind of weave. The rest of this section details additional features that allow weaves to nest, contain side-tracks and diversions, divert within themselves, and above all, reference earlier choices to influence later ones.
这是最基本的编织方式。本节的其余部分详细介绍了允许组织嵌套、包含侧轨和转移、在自身内部转移的附加功能,最重要的是,参考早期的选择来影响后来的选择。

The weave philosophy 编织哲学

Weaves are more than just a convenient encapsulation of branching flow; they're also a way to author more robust content. The escape example above has already four possible routes through, and a more complex sequence might have lots and lots more. Using normal diverts, one has to check the links by chasing the diverts from point to point and it's easy for errors to creep in.
编织不仅仅是分支流的便捷封装;它们也是创作更强大内容的一种方式。上面的 escape 示例已经有四种可能的路线,更复杂的序列可能有很多很多。使用正常的转移,人们必须通过逐点追踪转移来检查链接,并且很容易出现错误。

With a weave, the flow is guaranteed to start at the top and "fall" to the bottom. Flow errors are impossible in a basic weave structure, and the output text can be easily skim read. That means there's no need to actually test all the branches in game to be sure they work as intended.
通过编织,可以保证流动从顶部开始并“落”到底部。在基本组织结构中不可能出现流量错误,并且可以轻松浏览输出文本。这意味着无需实际测试游戏中的所有分支以确保它们按预期工作。

Weaves also allow for easy redrafting of choice-points; in particular, it's easy to break a sentence up and insert additional choices for variety or pacing reasons, without having to re-engineer any flow.
编织还可以轻松地重新起草选择点;特别是,出于多样性或节奏原因,可以轻松地分解句子并插入其他选择,而无需重新设计任何流程。

2) Nested Flow 2) 嵌套流

The weaves shown above are quite simple, "flat" structures. Whatever the player does, they take the same number of turns to get from top to bottom. However, sometimes certain choices warrant a bit more depth or complexity.
上面显示的编织是非常简单的“扁平”结构。无论玩家做什么,他们都会花费相同的回合数从上到下。然而,有时某些选择需要更深入或更复杂。

For that, we allow weaves to nest.
为此,我们允许编织嵌套。

This section comes with a warning. Nested weaves are very powerful and very compact, but they can take a bit of getting used to!
本节附带一个警告。嵌套编织非常强大且非常紧凑,但可能需要一些时间来适应!

Options can be nested 选项可以嵌套

Consider the following scene:
考虑以下场景:

- 	"Well, Poirot? Murder or suicide?"
*	"Murder!"
* 	"Suicide!"
-	Ms. Christie lowered her manuscript a moment. The rest of the writing group sat, open-mouthed.

The first choice presented is "Murder!" or "Suicide!". If Poirot declares a suicide, there's no more to do, but in the case of murder, there's a follow-up question needed - who does he suspect?
第一个选择是“谋杀!”或“自杀!”。如果波洛宣布自杀,则无需再做任何事情,但如果是谋杀,则需要一个后续问题 - 他怀疑谁?

We can add new options via a set of nested sub-choices. We tell the script that these new choices are "part of" another choice by using two asterisks, instead of just one.
我们可以通过一组嵌套的子选项添加新选项。我们通过使用两个星号而不是一个星号来告诉脚本这些新选择是另一个选择的“一部分”。

- 	"Well, Poirot? Murder or suicide?"
	*	"Murder!"
	 	"And who did it?"
		* * 	"Detective-Inspector Japp!"
		* * 	"Captain Hastings!"
		* * 	"Myself!"
	* 	"Suicide!"
	-	Mrs. Christie lowered her manuscript a moment. The rest of the writing group sat, open-mouthed.

(Note that it's good style to also indent the lines to show the nesting, but the compiler doesn't mind.)
(请注意,缩进行以显示嵌套也是一种很好的风格,但编译器不介意。)

And should we want to add new sub-options to the other route, we do that in similar fashion.
如果我们想向其他路线添加新的子选项,我们可以以类似的方式进行。

- 	"Well, Poirot? Murder or suicide?"
	*	"Murder!"
	 	"And who did it?"
		* * 	"Detective-Inspector Japp!"
		* * 	"Captain Hastings!"
		* * 	"Myself!"
	* 	"Suicide!"
		"Really, Poirot? Are you quite sure?"
		* * 	"Quite sure."
		* *		"It is perfectly obvious."
	-	Mrs. Christie lowered her manuscript a moment. The rest of the writing group sat, open-mouthed.

Now, that initial choice of accusation will lead to specific follow-up questions - but either way, the flow will come back together at the gather point, for Mrs. Christie's cameo appearance.
现在,最初选择的指控将导致具体的后续问题 - 但无论哪种方式,流程都会在克里斯蒂夫人客串出现的集合点重新聚集在一起。

But what if we want a more extended sub-scene?
但是如果我们想要更扩展的子场景怎么办?

Gather points can be nested too
采集点也可以嵌套

Sometimes, it's not a question of expanding the number of options, but having more than one additional beat of story. We can do this by nesting gather points as well as options.
有时,问题不是扩大选择的数量,而是增加一个以上的故事节奏。我们可以通过嵌套聚集点和选项来做到这一点。

- 	"Well, Poirot? Murder or suicide?"
		*	"Murder!"
		 	"And who did it?"
			* * 	"Detective-Inspector Japp!"
			* * 	"Captain Hastings!"
			* * 	"Myself!"
			- - 	"You must be joking!"
			* * 	"Mon ami, I am deadly serious."
			* *		"If only..."
		* 	"Suicide!"
			"Really, Poirot? Are you quite sure?"
			* * 	"Quite sure."
			* *		"It is perfectly obvious."
		-	Mrs. Christie lowered her manuscript a moment. The rest of the writing group sat, open-mouthed.

If the player chooses the "murder" option, they'll have two choices in a row on their sub-branch - a whole flat weave, just for them.
如果玩家选择“谋杀”选项,他们在自己的分支上将有两个连续的选择——一个完整的平织,只为他们服务。

Advanced: What gathers do
高级:聚集做什么

Gathers are hopefully intuitive, but their behaviour is a little harder to put into words: in general, after an option has been taken, the story finds the next gather down that isn't on a lower level, and diverts to it.
集合希望是直观的,但它们的行为有点难以用语言表达:一般来说,在选择了一个选项后,故事会找到下一个不在较低级别的集合,并转向它。

The basic idea is this: options separate the paths of the story, and gathers bring them back together. (Hence the name, "weave"!)
基本思想是这样的:选项将故事的路径分开,而集合将它们重新组合在一起。 (因此得名“编织”!)

You can nest as many levels are you like
您可以嵌套任意级别

Above, we used two levels of nesting; the main flow, and the sub-flow. But there's no limit to how many levels deep you can go.
上面,我们使用了两层嵌套;主流程和子流程。但你可以进入的深度是没有限制的。

-	"Tell us a tale, Captain!"
	*	"Very well, you sea-dogs. Here's a tale..."
		* * 	"It was a dark and stormy night..."
				* * * 	"...and the crew were restless..."
						* * * *  "... and they said to their Captain..."
								* * * * *		"...Tell us a tale Captain!"
	*	"No, it's past your bed-time."
-	To a man, the crew began to yawn.

After a while, this sub-nesting gets hard to read and manipulate, so it's good style to divert away to a new stitch if a side-choice goes unwieldy.
一段时间后,这个子嵌套变得难以阅读和操作,因此,如果侧面选择变得笨拙,那么转移到新的针迹是一种很好的风格。

But, in theory at least, you could write your entire story as a single weave.
但是,至少从理论上来说,你可以将整个故事写成一个单一的编织。

Example: a conversation with nested nodes
示例:与嵌套节点的对话

Here's a longer example:
这是一个更长的例子:

- I looked at Monsieur Fogg
*	... and I could contain myself no longer.
	'What is the purpose of our journey, Monsieur?'
	'A wager,' he replied.
	* * 	'A wager!'[] I returned.
			He nodded.
			* * * 	'But surely that is foolishness!'
			* * *  'A most serious matter then!'
			- - - 	He nodded again.
			* * *	'But can we win?'
					'That is what we will endeavour to find out,' he answered.
			* * *	'A modest wager, I trust?'
					'Twenty thousand pounds,' he replied, quite flatly.
			* * * 	I asked nothing further of him then[.], and after a final, polite cough, he offered nothing more to me. <>
	* * 	'Ah[.'],' I replied, uncertain what I thought.
	- - 	After that, <>
*	... but I said nothing[] and <>
- we passed the day in silence.
- -> END

with a couple of possible playthroughs. A short one:
有几个可能的玩法。简短的一个:

I looked at Monsieur Fogg

1: ... and I could contain myself no longer.
2: ... but I said nothing

> 2
... but I said nothing and we passed the day in silence.

and a longer one:
和一个更长的:

I looked at Monsieur Fogg

1: ... and I could contain myself no longer.
2: ... but I said nothing

> 1
... and I could contain myself no longer.
'What is the purpose of our journey, Monsieur?'
'A wager,' he replied.

1: 'A wager!'
2: 'Ah.'

> 1
'A wager!' I returned.
He nodded.

1: 'But surely that is foolishness!'
2: 'A most serious matter then!'

> 2
'A most serious matter then!'
He nodded again.

1: 'But can we win?'
2: 'A modest wager, I trust?'
3: I asked nothing further of him then.

> 2
'A modest wager, I trust?'
'Twenty thousand pounds,' he replied, quite flatly.
After that, we passed the day in silence.

Hopefully, this demonstrates the philosophy laid out above: that weaves offer a compact way to offer a lot of branching, a lot of choices, but with the guarantee of getting from beginning to end!
希望这体现了上面阐述的理念:编织提供了一种紧凑的方式来提供大量分支、大量选择,但保证从头到尾!

3) Tracking a Weave 3) 跟踪组织

Sometimes, the weave structure is sufficient. But when it's not, we need a bit more control.
有时,编织结构就足够了。但如果不是这样,我们就需要更多的控制。

Weaves are largely unaddressed
编织基本上没有得到解决

By default, lines of content in a weave don't have an address or label, which means they can't be diverted to, and they can't be tested for. In the most basic weave structure, choices vary the path the player takes through the weave and what they see, but once the weave is finished those choices and that path are forgotten.
默认情况下,组织中的内容行没有地址或标签,这意味着它们无法被转移到,也无法进行测试。在最基本的编织结构中,选择会改变玩家通过编织所采取的路径以及他们所看到的内容,但是一旦编织完成,这些选择和路径就会被忘记。

But should we want to remember what the player has seen, we can - we add in labels where they're needed using the (label_name) syntax.
但是如果我们想记住玩家所看到的内容,我们可以使用 (label_name) 语法在需要的地方添加标签。

Gathers and options can be labelled
可以标记集合和选项

Gather points at any nested level can be labelled using brackets.
任何嵌套级别的收集点都可以使用括号进行标记。

-  (top)

Once labelled, gather points can be diverted to, or tested for in conditionals, just like knots and stitches. This means you can use previous decisions to alter later outcomes inside the weave, while still keeping all the advantages of a clear, reliable forward-flow.
一旦标记完毕,聚集点就可以转移到条件条件中或进行测试,就像结和缝线一样。这意味着您可以使用之前的决策来改变组织内的后续结果,同时仍然保留清晰、可靠的前向流程的所有优势。

Options can also be labelled, just like gather points, using brackets. Label brackets come before conditions in the line.
选项也可以使用括号来标记,就像聚集点一样。标签括号位于行中的条件之前。

These addresses can be used in conditional tests, which can be useful for creating options unlocked by other options.
这些地址可用于条件测试,这对于创建由其他选项解锁的选项非常有用。

=== meet_guard ===
The guard frowns at you.

* 	(greet) [Greet him]
	'Greetings.'
*	(get_out) 'Get out of my way[.'],' you tell the guard.

- 	'Hmm,' replies the guard.

*	{greet} 	'Having a nice day?' // only if you greeted him

* 	'Hmm?'[] you reply.

*	{get_out} [Shove him aside] 	 // only if you threatened him
	You shove him sharply. He stares in reply, and draws his sword!
	-> fight_guard 			// this route diverts out of the weave

-	'Mff,' the guard replies, and then offers you a paper bag. 'Toffee?'

Scope 范围

Inside the same block of weave, you can simply use the label name; from outside the block you need a path, either to a different stitch within the same knot:
在同一组织块内,您可以简单地使用标签名称;从块的外部,您需要一条路径,或者到同一结内的不同针迹:

=== knot ===
= stitch_one
	- (gatherpoint) Some content.
= stitch_two
	*	{stitch_one.gatherpoint} Option

or pointing into another knot:
或指向另一个结:

=== knot_one ===
-	(gather_one)
	* {knot_two.stitch_two.gather_two} Option

=== knot_two ===
= stitch_two
	- (gather_two)
		*	{knot_one.gather_one} Option

Advanced: all options can be labelled
高级:所有选项都可以标记

In truth, all content in ink is a weave, even if there are no gathers in sight. That means you can label any option in the game with a bracket label, and then reference it using the addressing syntax. In particular, this means you can test which option a player took to reach a particular outcome.
事实上,墨水中的所有内容都是编织的,即使看不到任何聚集。这意味着您可以使用括号标签标记游戏中的任何选项,然后使用寻址语法引用它。特别是,这意味着您可以测试玩家采取哪个选项来达到特定的结果。

=== fight_guard ===
...
= throw_something
*	(rock) [Throw rock at guard] -> throw
* 	(sand) [Throw sand at guard] -> throw

= throw
You hurl {throw_something.rock:a rock|a handful of sand} at the guard.

Advanced: Loops in a weave
高级:编织中的循环

Labelling allows us to create loops inside weaves. Here's a standard pattern for asking questions of an NPC.
标签允许我们在组织内部创建循环。以下是向 NPC 提问的标准模式。

- (opts)
	*	'Can I get a uniform from somewhere?'[] you ask the cheerful guard.
		'Sure. In the locker.' He grins. 'Don't think it'll fit you, though.'
	*	'Tell me about the security system.'
		'It's ancient,' the guard assures you. 'Old as coal.'
	*	'Are there dogs?'
		'Hundreds,' the guard answers, with a toothy grin. 'Hungry devils, too.'
	// We require the player to ask at least one question
	*	{loop} [Enough talking]
		-> done
- (loop)
	// loop a few times before the guard gets bored
	{ -> opts | -> opts | }
	He scratches his head.
	'Well, can't stand around talking all day,' he declares.
- (done)
	You thank the guard, and move away.

Advanced: diverting to options
高级:转向选项

Options can also be diverted to: but the divert goes to the output of having chosen that choice, as though the choice had been chosen. So the content printed will ignore square bracketed text, and if the option is once-only, it will be marked as used up.
选项也可以转移到:但是转移到选择该选项的输出,就好像该选择已被选择一样。因此打印的内容将忽略方括号文本,如果该选项是一次性的,它将被标记为已用完。

- (opts)
*	[Pull a face]
	You pull a face, and the soldier comes at you! -> shove

*	(shove) [Shove the guard aside] You shove the guard to one side, but he comes back swinging.

*	{shove} [Grapple and fight] -> fight_the_guard

- 	-> opts

produces: 产生:

1: Pull a face
2: Shove the guard aside

> 1
You pull a face, and the soldier comes at you! You shove the guard to one side, but he comes back swinging.

1: Grapple and fight

>

Advanced: Gathers directly after an option
高级:在选项后直接收集

The following is valid, and frequently useful.
以下内容是有效的,并且经常有用。

*	"Are you quite well, Monsieur?"[] I asked.
	- - (quitewell) "Quite well," he replied.
*	"How did you do at the crossword, Monsieur?"[] I asked.
	-> quitewell
*	I said nothing[] and neither did my Master.
-	We feel into companionable silence once more.

Note the level 2 gather point directly below the first option: there's nothing to gather here, really, but it gives us a handy place to divert the second option to.
请注意第一个选项正下方的 2 级收集点:这里确实没有什么可收集的,但它为我们提供了一个方便的地方来转移第二个选项。

Part 3: Variables and Logic
第 3 部分:变量和逻辑

So far we've made conditional text, and conditional choices, using tests based on what content the player has seen so far.
到目前为止,我们已经根据玩家目前看到的内容进行了测试,制作了条件文本和条件选择。

ink also supports variables, both temporary and global, storing numerical and content data, or even story flow commands. It is fully-featured in terms of logic, and contains a few additional structures to help keep the often complex logic of a branching story better organised.
ink 还支持临时变量和全局变量,存储数字和内容数据,甚至故事流命令。它在逻辑方面功能齐全,并包含一些附加结构,以帮助更好地组织分支故事中通常复杂的逻辑。

1) Global Variables 1) 全局变量

The most powerful kind of variable, and arguably the most useful for a story, is a variable to store some unique property about the state of the game - anything from the amount of money in the protagonist's pocket, to a value representing the protagonist's state of mind.
最强大的变量,可以说是对故事最有用的变量,是用于存储有关游戏状态的一些独特属性的变量 - 从主角口袋里的钱数到代表主角状态的值。头脑。

This kind of variable is called "global" because it can be accessed from anywhere in the story - both set, and read from. (Traditionally, programming tries to avoid this kind of thing, as it allows one part of a program to mess with another, unrelated part. But a story is a story, and stories are all about consequences: what happens in Vegas rarely stays there.)
这种变量称为“全局”变量,因为可以从故事中的任何位置访问它 - 设置和读取。 (传统上,编程试图避免这种事情,因为它允许程序的一个部分与另一个不相关的部分混淆。但故事就是故事,故事都是关于后果的:在维加斯发生的事情很少停留在那里。 )

Defining Global Variables
定义全局变量

Global variables can be defined anywhere, via a VAR statement. They should be given an initial value, which defines what type of variable they are - integer, floating point (decimal), content, or a story address.
全局变量可以通过 VAR 语句在任何地方定义。应该给它们一个初始值,该值定义了它们的变量类型 - 整数、浮点(十进制)、内容或故事地址。

VAR knowledge_of_the_cure = false
VAR players_name = "Emilia"
VAR number_of_infected_people = 521
VAR current_epilogue = -> they_all_die_of_the_plague

Using Global Variables 使用全局变量

We can test global variables to control options, and provide conditional text, in a similar way to what we have previously seen.
我们可以测试全局变量来控制选项,并提供条件文本,与我们之前看到的类似。

=== the_train ===
	The train jolted and rattled. { mood > 0:I was feeling positive enough, however, and did not mind the odd bump|It was more than I could bear}.
	*	{ not knows_about_wager } 'But, Monsieur, why are we travelling?'[] I asked.
	* 	{ knows_about_wager} I contemplated our strange adventure[]. Would it be possible?

Advanced: storing diverts as variables
高级:将转移存储为变量

A "divert" statement is actually a type of value in itself, and can be stored, altered, and diverted to.
“转移”语句本身实际上是一种值,可以存储、更改和转移。

VAR 	current_epilogue = -> everybody_dies

=== continue_or_quit ===
Give up now, or keep trying to save your Kingdom?
*  [Keep trying!] 	-> more_hopeless_introspection
*  [Give up] 		-> current_epilogue

Advanced: Global variables are externally visible
高级:全局变量外部可见

Global variables can be accessed, and altered, from the runtime as well from the story, so provide a good way to communicate between the wider game and the story.
全局变量可以从运行时以及故事中访问和更改,因此提供了一种在更广泛的游戏和故事之间进行通信的好方法。

The ink layer is often be a good place to store gameplay-variables; there's no save/load issues to consider, and the story itself can react to the current values.
ink 层通常是存储游戏变量的好地方;无需考虑保存/加载问题,故事本身可以对当前值做出反应。

Printing variables 打印变量

The value of a variable can be printed as content using an inline syntax similar to sequences, and conditional text:
可以使用类似于序列和条件文本的内联语法将变量的值打印为内容:

VAR friendly_name_of_player = "Jackie"
VAR age = 23

My name is Jean Passepartout, but my friends call me {friendly_name_of_player}. I'm {age} years old.

This can be useful in debugging. For more complex printing based on logic and variables, see the section on functions.
这在调试时很有用。对于基于逻辑和变量的更复杂的打印,请参阅函数部分。

Evaluating strings 评估字符串

It might be noticed that above we refered to variables as being able to contain "content", rather than "strings". That was deliberate, because a string defined in ink can contain ink - although it will always evaluate to a string. (Yikes!)
可能会注意到,上面我们将变量称为能够包含“内容”,而不是“字符串”。这是故意的,因为 ink 中定义的字符串可以包含 ink - 尽管它总是计算为字符串。 (哎呀!)

VAR a_colour = ""

~ a_colour = "{~red|blue|green|yellow}"

{a_colour}

... produces one of red, blue, green or yellow.
...产生红色、蓝色、绿色或黄色之一。

Note that once a piece of content like this is evaluated, its value is "sticky". (The quantum state collapses.) So the following:
请注意,一旦对这样的内容进行评估,它的值就是“粘性”。 (量子态崩溃。)所以如下:

The goon hits you, and sparks fly before you eyes, {a_colour} and {a_colour}.

... won't produce a very interesting effect. (If you really want this to work, use a text function to print the colour!)
...不会产生非常有趣的效果。 (如果您确实希望此功能有效,请使用文本函数来打印颜色!)

This is also why
这也是为什么

VAR a_colour = "{~red|blue|green|yellow}"

is explicitly disallowed; it would be evaluated on the construction of the story, which probably isn't what you want.
被明确禁止;它将根据故事的结构进行评估,这可能不是你想要的。

2) Logic 2)逻辑

Obviously, our global variables are not intended to be constants, so we need a syntax for altering them.
显然,我们的全局变量并不打算成为常量,因此我们需要一种语法来更改它们。

Since by default, any text in an ink script is printed out directly to the screen, we use a markup symbol to indicate that a line of content is intended meant to be doing some numerical work, we use the ~ mark.
由于默认情况下,墨迹脚本中的任何文本都会直接打印到屏幕上,因此我们使用标记符号来指示一行内容旨在执行一些数字工作,我们使用 ~ 标记。

The following statements all assign values to variables:
以下语句都为变量赋值:

=== set_some_variables ===
	~ knows_about_wager = true
	~ x = (x * x) - (y * y) + c
	~ y = 2 * x * y

and the following will test conditions:
以下将测试条件:

{ x == 1.2 }
{ x / 2 > 4 }
{ y - 1 <= x * x }

Mathematics 数学

ink supports the four basic mathematical operations (+, -, * and /), as well as % (or mod), which returns the remainder after integer division. There's also POW for to-the-power-of:
ink 支持四种基本数学运算( +-*/ )以及 % (或 mod ),返回整数除法后的余数。还有 POW 来表示权力:

{POW(3, 2)} is 9.
{POW(16, 0.5)} is 4.

If more complex operations are required, one can write functions (using recursion if necessary), or call out to external, game-code functions (for anything more advanced).
如果需要更复杂的操作,可以编写函数(如果需要,使用递归),或调用外部游戏代码函数(用于更高级的操作)。

RANDOM(min, max) 随机(最小,最大)

Ink can generate random integers if required using the RANDOM function. RANDOM is authored to be like a dice (yes, pendants, we said a dice), so the min and max values are both inclusive.
如果需要,Ink 可以使用 RANDOM 函数生成随机整数。 RANDOM 的设计就像一个骰子(是的,吊坠,我们说骰子),因此最小值和最大值都包含在内。

~ temp dice_roll = RANDOM(1, 6)

~ temp lazy_grading_for_test_paper = RANDOM(30, 75)

~ temp number_of_heads_the_serpent_has = RANDOM(3, 8)

The random number generator can be seeded for testing purposes, see the section of Game Queries and Functions section above.
可以为测试目的播种随机数生成器,请参阅上面的游戏查询和函数部分。

Advanced: numerical types are implicit
高级:数字类型是隐式的

Results of operations - in particular, for division - are typed based on the type of the input. So integer division returns integer, but floating point division returns floating point results.
运算结果(特别是除法运算结果)根据输入的类型进行分类。因此整数除法返回整数,但浮点除法返回浮点结果。

~ x = 2 / 3
~ y = 7 / 3
~ z = 1.2 / 0.5

assigns x to be 0, y to be 2 and z to be 2.4.
x 指定为 0, y 指定为 2, z 指定为 2.4。

Advanced: INT(), FLOOR() and FLOAT()
高级:INT()、FLOOR() 和 FLOAT()

In cases where you don't want implicit types, or you want to round off a variable, you can cast it directly.
如果您不需要隐式类型,或者想要对变量进行舍入,则可以直接对其进行强制转换。

{INT(3.2)} is 3.
{FLOOR(4.8)} is 4.
{INT(-4.8)} is -4.
{FLOOR(-4.8)} is -5.

{FLOAT(4)} is, um, still 4.

String queries 字符串查询

Oddly for a text-engine, ink doesn't have much in the way of string-handling: it's assumed that any string conversion you need to do will be handled by the game code (and perhaps by external functions.) But we support three basic queries - equality, inequality, and substring (which we call ? for reasons that will become clear in a later chapter).
奇怪的是,对于文本引擎来说,ink 没有太多字符串处理方式:假设您需要执行的任何字符串转换都将由游戏代码(也许还有外部函数)处理。但我们支持三种基本查询 - 相等、不等式和子字符串(我们称之为?,其原因将在后面的章节中变得清楚)。

The following all return true:
以下全部返回 true:

{ "Yes, please." == "Yes, please." }
{ "No, thank you." != "Yes, please." }
{ "Yes, please" ? "ease" }

3) Conditional blocks (if/else)
3)条件块(if/else)

We've seen conditionals used to control options and story content; ink also provides an equivalent of the normal if/else-if/else structure.
我们已经看到了用于控制选项和故事内容的条件; ink 还提供了与正常 if/else-if/else 结构等效的功能。

A simple 'if' 一个简单的“如果”

The if syntax takes its cue from the other conditionals used so far, with the {...} syntax indicating that something is being tested.
if 语法从迄今为止使用的其他条件中获取线索,其中 { ... } 语法指示正在测试某些内容。

{ x > 0:
	~ y = x - 1
}

Else conditions can be provided:
还可以提供其他条件:

{ x > 0:
	~ y = x - 1
- else:
	~ y = x + 1
}

Extended if/else if/else blocks
扩展 if/else if/else 块

The above syntax is actually a specific case of a more general structure, something like a "switch" statement of another language:
上面的语法实际上是更通用结构的特定情况,类似于另一种语言的“switch”语句:

{
	- x > 0:
		~ y = x - 1
	- else:
		~ y = x + 1
}

And using this form we can include 'else-if' conditions:
使用这种形式,我们可以包含“else-if”条件:

{
	- x == 0:
		~ y = 0
	- x > 0:
		~ y = x - 1
	- else:
		~ y = x + 1
}

(Note, as with everything else, the white-space is purely for readability and has no syntactic meaning.)
(请注意,与其他所有内容一样,空格纯粹是为了可读性,没有语法意义。)

Switch blocks 开关块

And there's also an actual switch statement:
还有一个实际的 switch 语句:

{ x:
- 0: 	zero
- 1: 	one
- 2: 	two
- else: lots
}

Example: context-relevant content
示例:上下文相关内容

Note these tests don't have to be variable-based and can use read-counts, just as other conditionals can, and the following construction is quite frequent, as a way of saying "do some content which is relevant to the current game state":
请注意,这些测试不必是基于变量的,并且可以使用读取计数,就像其他条件一样,并且以下构造非常频繁,作为一种表达“执行一些与当前游戏状态相关的内容”的方式”:

=== dream ===
	{
		- visited_snakes && not dream_about_snakes:
			~ fear++
			-> dream_about_snakes

		- visited_poland && not dream_about_polish_beer:
			~ fear--
			-> dream_about_polish_beer

		- else:
			// breakfast-based dreams have no effect
			-> dream_about_marmalade
	}

The syntax has the advantage of being easy to extend, and prioritise.
该语法的优点是易于扩展和区分优先级。

Conditional blocks are not limited to logic
条件块不限于逻辑

Conditional blocks can be used to control story content as well as logic:
条件块可用于控制故事内容和逻辑:

I stared at Monsieur Fogg.
{ know_about_wager:
	<> "But surely you are not serious?" I demanded.
- else:
	<> "But there must be a reason for this trip," I observed.
}
He said nothing in reply, merely considering his newspaper with as much thoroughness as entomologist considering his latest pinned addition.

You can even put options inside conditional blocks:
您甚至可以将选项放入条件块内:

{ door_open:
	* 	I strode out of the compartment[] and I fancied I heard my master quietly tutting to himself. 			-> go_outside
- else:
	*	I asked permission to leave[] and Monsieur Fogg looked surprised. 	-> open_door
	* 	I stood and went to open the door[]. Monsieur Fogg seemed untroubled by this small rebellion. -> open_door
}

...but note that the lack of weave-syntax and nesting in the above example isn't accidental: to avoid confusing the various kinds of nesting at work, you aren't allowed to include gather points inside conditional blocks.
...但请注意,上面示例中缺少编织语法和嵌套并非偶然:为了避免混淆工作中的各种嵌套,不允许您在条件块内包含聚集点。

Multiline blocks 多行块

There's one other class of multiline block, which expands on the alternatives system from above. The following are all valid and do what you might expect:
还有另一类多行块,它扩展了上面的替代系统。以下内容均有效,并且符合您的预期:

// Sequence: go through the alternatives, and stick on last
{ stopping:
	-	I entered the casino.
	-  I entered the casino again.
	-  Once more, I went inside.
}

// Shuffle: show one at random
At the table, I drew a card. <>
{ shuffle:
	- 	Ace of Hearts.
	- 	King of Spades.
	- 	2 of Diamonds.
		'You lose this time!' crowed the croupier.
}

// Cycle: show each in turn, and then cycle
{ cycle:
	- I held my breath.
	- I waited impatiently.
	- I paused.
}

// Once: show each, once, in turn, until all have been shown
{ once:
	- Would my luck hold?
	- Could I win the hand?
}

Advanced: modified shuffles
高级:修改随机播放

The shuffle block above is really a "shuffled cycle"; in that it'll shuffle the content, play through it, then reshuffle and go again.
上面的 shuffle 块实际上是一个“洗牌循环”;因为它会打乱内容,播放它,然后重新打乱并再次播放。

There are two other versions of shuffle:
shuffle 还有另外两个版本:

shuffle once which will shuffle the content, play through it, and then do nothing.
shuffle once 它将随机播放内容,播放它,然后什么都不做。

{ shuffle once:
-	The sun was hot.
- 	It was a hot day.
}

shuffle stopping will shuffle all the content (except the last entry), and once its been played, it'll stick on the last entry.
shuffle stopping 将随机播放所有内容(最后一个条目除外),一旦播放完毕,它就会停留在最后一个条目上。

{ shuffle stopping:
- 	A silver BMW roars past.
-	A bright yellow Mustang takes the turn.
- 	There are like, cars, here.
}

4) Temporary Variables 4)临时变量

Temporary variables are for scratch calculations
临时变量用于临时计算

Sometimes, a global variable is unwieldy. ink provides temporary variables for quick calculations of things.
有时,全局变量很笨重。 ink 提供了用于快速计算事物的临时变量。

=== near_north_pole ===
	~ temp number_of_warm_things = 0
	{ blanket:
		~ number_of_warm_things++
	}
	{ ear_muffs:
		~ number_of_warm_things++
	}
	{ gloves:
		~ number_of_warm_things++
	}
	{ number_of_warm_things > 2:
		Despite the snow, I felt incorrigibly snug.
	- else:
		That night I was colder than I have ever been.
	}

The value in a temporary variable is thrown away after the story leaves the stitch in which it was defined.
故事离开定义它的针迹后,临时变量中的值将被丢弃。

Knots and stitches can take parameters
结和针迹可以带参数

A particularly useful form of temporary variable is a parameter. Any knot or stitch can be given a value as a parameter.
临时变量的一种特别有用的形式是参数。任何结或针迹都可以被赋予一个值作为参数。

*	[Accuse Hasting]
		-> accuse("Hastings")
*	[Accuse Mrs Black]
		-> accuse("Claudia")
*	[Accuse myself]
		-> accuse("myself")

=== accuse(who) ===
	"I accuse {who}!" Poirot declared.
	"Really?" Japp replied. "{who == "myself":You did it?|{who}?}"
	"And why not?" Poirot shot back.

... and you'll need to use parameters if you want to pass a temporary value from one stitch to another!
...如果您想将临时值从一针传递到另一针,则需要使用参数!

Example: a recursive knot definition
示例:递归结定义

Temporary variables are safe to use in recursion (unlike globals), so the following will work.
临时变量可以安全地在递归中使用(与全局变量不同),因此以下内容将起作用。

-> add_one_to_one_hundred(0, 1)

=== add_one_to_one_hundred(total, x) ===
	~ total = total + x
	{ x == 100:
		-> finished(total)
	- else:
		-> add_one_to_one_hundred(total, x + 1)
	}

=== finished(total) ===
	"The result is {total}!" you announce.
	Gauss stares at you in horror.
	-> END

(In fact, this kind of definition is useful enough that ink provides a special kind of knot, called, imaginatively enough, a function, which comes with certain restrictions and can return a value. See the section below.)
(事实上​​,这种定义非常有用,以至于 ink 提供了一种特殊的结,足够想象力地称为 function ,它带有一定的限制并且可以返回一个值。请参阅下面的部分。 )

Advanced: sending divert targets as parameters
高级:将转移目标作为参数发送

Knot/stitch addresses are a type of value, indicated by a -> character, and can be stored and passed around. The following is therefore legal, and often useful:
结/针地址是一种值,由 -> 字符表示,可以存储和传递。因此,以下内容是合法的,并且通常很有用:

=== sleeping_in_hut ===
	You lie down and close your eyes.
	-> generic_sleep (-> waking_in_the_hut)

===	 generic_sleep (-> waking)
	You sleep perchance to dream etc. etc.
	-> waking

=== waking_in_the_hut
	You get back to your feet, ready to continue your journey.

...but note the -> in the generic_sleep definition: that's the one case in ink where a parameter needs to be typed: because it's too easy to otherwise accidentally do the following:
...但请注意 generic_sleep 定义中的 -> :这是 ink 中需要键入参数的一种情况:因为否则很容易意外执行以下操作:

=== sleeping_in_hut ===
	You lie down and close your eyes.
	-> generic_sleep (waking_in_the_hut)

... which sends the read count of waking_in_the_hut into the sleeping knot, and then attempts to divert to it.
...它将 waking_in_the_hut 的读取计数发送到休眠结,然后尝试转移到它。

5) Functions 5) 功能

The use of parameters on knots means they are almost functions in the usual sense, but they lack one key concept - that of the call stack, and the use of return values.
在结上使用参数意味着它们几乎是通常意义上的函数,但它们缺少一个关键概念 - 调用堆栈和返回值的使用。

ink includes functions: they are knots, with the following limitations and features:
ink 包含功能:它们是结,具有以下限制和特征:

A function: 一个函数:

  • cannot contain stitches 不能包含针迹
  • cannot use diverts or offer choices
    不能使用转移或提供选择
  • can call other functions
    可以调用其他函数
  • can include printed content
    可以包含印刷内容
  • can return a value of any type
    可以返回任何类型的值
  • can recurse safely 可以安全地递归

(Some of these may seem quite limiting, but for more story-oriented call-stack-style features, see the section on Tunnels.)
(其中一些可能看起来相当有限,但对于更多面向故事的调用堆栈样式功能,请参阅有关隧道的部分。)

Return values are provided via the ~ return statement.
返回值通过 ~ return 语句提供。

Defining and calling functions
定义和调用函数

To define a function, simply declare a knot to be one:
要定义一个函数,只需将一个结声明为 1:

=== function say_yes_to_everything ===
	~ return true

=== function lerp(a, b, k) ===
	~ return ((b - a) * k) + a

Functions are called by name, and with brackets, even if they have no parameters:
函数通过名称和方括号调用,即使它们没有参数:

~ x = lerp(2, 8, 0.3)

*	{say_yes_to_everything()} 'Yes.'

As in any other language, a function, once done, returns the flow to wherever it was called from - and despite not being allowed to divert the flow, functions can still call other functions.
与任何其他语言一样,函数一旦完成,就会将流返回到调用它的地方 - 尽管不允许转移流,函数仍然可以调用其他函数。

=== function say_no_to_nothing ===
	~ return say_yes_to_everything()

Functions don't have to return anything
函数不必返回任何内容

A function does not need to have a return value, and can simply do something that is worth packaging up:
函数不需要有返回值,并且可以简单地做一些值得打包的事情:

=== function harm(x) ===
	{ stamina < x:
		~ stamina = 0
	- else:
		~ stamina = stamina - x
	}

...though remember a function cannot divert, so while the above prevents a negative Stamina value, it won't kill a player who hits zero.
...尽管记住函数不能转移,所以虽然上面的方法可以防止负耐力值,但它不会杀死击中零的玩家。

Functions can be called inline
函数可以内联调用

Functions can be called on ~ content lines, but can also be called during a piece of content. In this context, the return value, if there is one, is printed (as well as anything else the function wants to print.) If there is no return value, nothing is printed.
可以在 ~ 内容行上调用函数,但也可以在一段内容期间调用函数。在此上下文中,如果有返回值,则会打印返回值(以及函数想要打印的其他任何内容)。如果没有返回值,则不会打印任何内容。

Content is, by default, 'glued in', so the following:
默认情况下,内容是“粘贴”的,因此如下:

Monsieur Fogg was looking {describe_health(health)}.

=== function describe_health(x) ===
{
- x == 100:
	~ return "spritely"
- x > 75:
	~ return "chipper"
- x > 45:
	~ return "somewhat flagging"
- else:
	~ return "despondent"
}

produces: 产生:

Monsieur Fogg was looking despondent.

Examples 例子

For instance, you might include:
例如,您可能包括:

=== function max(a,b) ===
	{ a < b:
		~ return b
	- else:
		~ return a
	}

=== function exp(x, e) ===
	// returns x to the power e where e is an integer
	{ e <= 0:
		~ return 1
	- else:
		~ return x * exp(x, e - 1)
	}

Then: 然后:

The maximum of 2^5 and 3^3 is {max(exp(2,5), exp(3,3))}.

produces: 产生:

The maximum of 2^5 and 3^3 is 32.

Example: turning numbers into words
示例:将数字转换为单词

The following example is long, but appears in pretty much every inkle game to date. (Recall that a hyphenated line inside multiline curly braces indicates either "a condition to test" or, if the curly brace began with a variable, "a value to compare against".)
下面的例子很长,但几乎出现在迄今为止的所有 inkle 游戏中。 (回想一下,多行大括号内的连字符表示“要测试的条件”,或者如果大括号以变量开头,则表示“要比较的值”。)

=== function print_num(x) ===
{
    - x >= 1000:
        {print_num(x / 1000)} thousand { x mod 1000 > 0:{print_num(x mod 1000)}}
    - x >= 100:
        {print_num(x / 100)} hundred { x mod 100 > 0:and {print_num(x mod 100)}}
    - x == 0:
        zero
    - else:
        { x >= 20:
            { x / 10:
                - 2: twenty
                - 3: thirty
                - 4: forty
                - 5: fifty
                - 6: sixty
                - 7: seventy
                - 8: eighty
                - 9: ninety
            }
            { x mod 10 > 0:<>-<>}
        }
        { x < 10 || x > 20:
            { x mod 10:
                - 1: one
                - 2: two
                - 3: three
                - 4: four
                - 5: five
                - 6: six
                - 7: seven
                - 8: eight
                - 9: nine
            }
        - else:
            { x:
                - 10: ten
                - 11: eleven
                - 12: twelve
                - 13: thirteen
                - 14: fourteen
                - 15: fifteen
                - 16: sixteen
                - 17: seventeen
                - 18: eighteen
                - 19: nineteen
            }
        }
}

which enables us to write things like:
这使我们能够编写如下内容:

~ price = 15

I pulled out {print_num(price)} coins from my pocket and slowly counted them.
"Oh, never mind," the trader replied. "I'll take half." And she took {print_num(price / 2)}, and pushed the rest back over to me.

Parameters can be passed by reference
参数可以通过引用传递

Function parameters can also be passed 'by reference', meaning that the function can actually alter the the variable being passed in, instead of creating a temporary variable with that value.
函数参数也可以“通过引用”传递,这意味着函数实际上可以更改传入的变量,而不是使用该值创建临时变量。

For instance, most inkle stories include the following:
例如,大多数 Inkle 故事包括以下内容:

=== function alter(ref x, k) ===
	~ x = x + k

Lines such as: 行例如:

~ gold = gold + 7
~ health = health - 4

then become: 然后变成:

~ alter(gold, 7)
~ alter(health, -4)

which are slightly easier to read, and (more usefully) can be done inline for maximum compactness.
它们更容易阅读,并且(更有用)可以内联完成以获得最大的紧凑性。

*	I ate a biscuit[] and felt refreshed. {alter(health, 2)}
* 	I gave a biscuit to Monsieur Fogg[] and he wolfed it down most undecorously. {alter(foggs_health, 1)}
-	<> Then we continued on our way.

Wrapping up simple operations in function can also provide a simple place to put debugging information, if required.
如果需要,在函数中封装简单的操作还可以提供一个放置调试信息的简单位置。

6) Constants 6) 常数

Global Constants 全局常数

Interactive stories often rely on state machines, tracking what stage some higher level process has reached. There are lots of ways to do this, but the most conveninent is to use constants.
交互式故事通常依赖于状态机,跟踪某些更高级别的过程已达到的阶段。有很多方法可以做到这一点,但最方便的是使用常量。

Sometimes, it's convenient to define constants to be strings, so you can print them out, for gameplay or debugging purposes.
有时,将常量定义为字符串很方便,因此您可以将它们打印出来,以用于游戏或调试目的。

CONST HASTINGS = "Hastings"
CONST POIROT = "Poirot"
CONST JAPP = "Japp"

VAR current_chief_suspect = HASTINGS

=== review_evidence ===
	{ found_japps_bloodied_glove:
		~ current_chief_suspect = POIROT
	}
	Current Suspect: {current_chief_suspect}

Sometimes giving them values is useful:
有时给他们价值观是有用的:

CONST PI = 3.14
CONST VALUE_OF_TEN_POUND_NOTE = 10

And sometimes the numbers are useful in other ways:
有时这些数字还有其他用途:

CONST LOBBY = 1
CONST STAIRCASE = 2
CONST HALLWAY = 3

CONST HELD_BY_AGENT = -1

VAR secret_agent_location = LOBBY
VAR suitcase_location = HALLWAY

=== report_progress ===
{
    -  secret_agent_location == suitcase_location:
	The secret agent grabs the suitcase!
	~ suitcase_location = HELD_BY_AGENT

-  secret_agent_location < suitcase_location:
	The secret agent moves forward.
	~ secret_agent_location++
}

Constants are simply a way to allow you to give story states easy-to-understand names.
常量只是一种允许您为故事状态提供易于理解的名称的方法。

7) Advanced: Game-side logic
7)进阶:游戏端逻辑

There are two core ways to provide game hooks in the ink engine. External function declarations in ink allow you to directly call C# functions in the game, and variable observers are callbacks that are fired in the game when ink variables are modified. Both of these are described in Running your ink.
Ink引擎中提供游戏Hook的核心方式有两种。 ink 中的外部函数声明允许您直接在游戏中调用 C# 函数,而变量观察者是修改 ink 变量时在游戏中触发的回调。这两个内容都在“运行墨水”中进行了描述。

Part 4: Advanced Flow Control
第 4 部分:高级流量控制

1) Tunnels 1) 隧道

The default structure for ink stories is a "flat" tree of choices, branching and joining back together, perhaps looping, but with the story always being "at a certain place".
水墨故事的默认结构是一棵“扁平”的选择树,分支和连接在一起,也许是循环的,但故事总是“在某个地方”。

But this flat structure makes certain things difficult: for example, imagine a game in which the following interaction can happen:
但这种扁平结构使某些事情变得困难:例如,想象一个游戏,其中可能发生以下交互:

=== crossing_the_date_line ===
*	"Monsieur!"[] I declared with sudden horror. "I have just realised. We have crossed the international date line!"
-	Monsieur Fogg barely lifted an eyebrow. "I have adjusted for it."
*	I mopped the sweat from my brow[]. A relief!
* 	I nodded, becalmed[]. Of course he had!
*  I cursed, under my breath[]. Once again, I had been belittled!

...but it can happen at several different places in the story. We don't want to have to write copies of the content for each different place, but when the content is finished it needs to know where to return to. We can do this using parameters:
...但它可能发生在故事的几个不同的地方。我们不想为每个不同的地方编写内容副本,但当内容完成后,它需要知道返回到哪里。我们可以使用参数来做到这一点:

=== crossing_the_date_line(-> return_to) ===
...
-	-> return_to

...

=== outside_honolulu ===
We arrived at the large island of Honolulu.
- (postscript)
	-> crossing_the_date_line(-> done)
- (done)
	-> END

...

=== outside_pitcairn_island ===
The boat sailed along the water towards the tiny island.
- (postscript)
	-> crossing_the_date_line(-> done)
- (done)
	-> END

Both of these locations now call and execute the same segment of storyflow, but once finished they return to where they need to go next.
这两个位置现在都调用并执行故事流的同一片段,但一旦完成,它们就会返回到下一步需要去的地方。

But what if the section of story being called is more complex - what if it spreads across several knots? Using the above, we'd have to keep passing the 'return-to' parameter from knot to knot, to ensure we always knew where to return.
但是,如果被调用的故事部分更复杂怎么办——如果它分布在几个结点上怎么办?使用上面的方法,我们必须不断地将“return-to”参数从一个结传递到另一个结,以确保我们始终知道返回到哪里。

So instead, ink integrates this into the language with a new kind of divert, that functions rather like a subroutine, and is called a 'tunnel'.
因此,ink 通过一种新的转移将其集成到语言中,其功能类似于子例程,被称为“隧道”。

Tunnels run sub-stories 隧道运行子故事

The tunnel syntax looks like a divert, with another divert on the end:
隧道语法看起来像一个转向,最后还有另一个转向:

-> crossing_the_date_line ->

This means "do the crossing_the_date_line story, then continue from here".
这意味着“完成穿越日期线的故事,然后从这里继续”。

Inside the tunnel itself, the syntax is simplified from the parameterised example: all we do is end the tunnel using the ->-> statement which means, essentially, "go on".
在隧道内部,语法从参数化示例中进行了简化:我们所做的就是使用 ->-> 语句结束隧道,这本质上意味着“继续”。

=== crossing_the_date_line ===
// this is a tunnel!
...
- 	->->

Note that tunnel knots aren't declared as such, so the compiler won't check that tunnels really do end in ->-> statements, except at run-time. So you will need to write carefully to ensure that all the flows into a tunnel really do come out again.
请注意,隧道结没有这样声明,因此编译器不会检查隧道是否确实以 ->-> 语句结束,除非在运行时。因此,您需要仔细编写,以确保所有进入隧道的流量确实会再次流出。

Tunnels can also be chained together, or finish on a normal divert:
隧道也可以链接在一起,或者以正常的改道结束:

...
// this runs the tunnel, then diverts to 'done'
-> crossing_the_date_line -> done
...

...
//this runs one tunnel, then another, then diverts to 'done'
-> crossing_the_date_line -> check_foggs_health -> done
...

Tunnels can be nested, so the following is valid:
隧道可以嵌套,因此以下内容有效:

=== plains ===
= night_time
	The dark grass is soft under your feet.
	+	[Sleep]
		-> sleep_here -> wake_here -> day_time
= day_time
	It is time to move on.

=== wake_here ===
	You wake as the sun rises.
	+	[Eat something]
		-> eat_something ->
	+	[Make a move]
	-	->->

=== sleep_here ===
	You lie down and try to close your eyes.
	-> monster_attacks ->
	Then it is time to sleep.
	-> dream ->
	->->

... and so on.
... 等等。

Advanced: Tunnels can return elsewhere
高级:隧道可以返回其他地方

Sometimes, in a story, things happen. So sometimes a tunnel can't guarantee that it will always want to go back to where it came from. ink supplies a syntax to allow you to "returning from a tunnel but actually go somewhere else" but it should be used with caution as the possibility of getting very confused is very high indeed.
有时,在故事中,事情会发生。所以有时隧道不能保证它总是想回到它原来的地方。 ink 提供了一种语法,允许您“从隧道返回,但实际上去其他地方”,但应谨慎使用,因为确实非常容易感到困惑。

Still, there are cases where it's indispensable:
尽管如此,在某些情况下它是必不可少的:

=== fall_down_cliff 
-> hurt(5) -> 
You're still alive! You pick yourself up and walk on.

=== hurt(x)
	~ stamina -= x 
	{ stamina <= 0:
		->-> youre_dead
	}

=== youre_dead
Suddenly, there is a white light all around you. Fingers lift an eyepiece from your forehead. 'You lost, buddy. Out of the chair.'

And even in less drastic situations, we might want to break up the structure:
即使在不太激烈的情况下,我们也可能想要打破结构:

-> talk_to_jim ->

 === talk_to_jim
 - (opts) 	
	*	[ Ask about the warp lacelles ] 
		-> warp_lacells ->
	*	[ Ask about the shield generators ] 
		-> shield_generators ->	
	* 	[ Stop talking ]
		->->
 - -> opts 

 = warp_lacells
	{ shield_generators : ->-> argue }
	"Don't worry about the warp lacelles. They're fine."
	->->

 = shield_generators
	{ warp_lacells : ->-> argue }
	"Forget about the shield generators. They're good."
	->->
 
 = argue 
 	"What's with all these questions?" Jim demands, suddenly. 
 	...
 	->->

Advanced: Tunnels use a call-stack
高级:隧道使用调用堆栈

Tunnels are on a call-stack, so can safely recurse.
隧道位于调用堆栈上,因此可以安全地递归。

2) Threads 2) 线程

So far, everything in ink has been entirely linear, despite all the branching and diverting. But it's actually possible for a writer to 'fork' a story into different sub-sections, to cover more possible player actions.
到目前为止,尽管有很多分支和转移,但墨水中的一切都是完全线性的。但实际上,编剧可以将故事“分叉”成不同的小部分,以涵盖更多可能的玩家行为。

We call this 'threading', though it's not really threading in the sense that computer scientists mean it: it's more like stitching in new content from various places.
我们称之为“线程”,尽管它并不是计算机科学家所说的真正的线程:它更像是从不同地方缝合新内容。

Note that this is definitely an advanced feature: the engineering stories becomes somewhat more complex once threads are involved!
请注意,这绝对是一项高级功能:一旦涉及线程,工程故事就会变得更加复杂!

Threads join multiple sections together
线程将多个部分连接在一起

Threads allow you to compose sections of content from multiple sources in one go. For example:
线程允许您一次性编写来自多个来源的内容部分。例如:

== thread_example ==
I had a headache; threading is hard to get your head around.
<- conversation
<- walking


== conversation ==
It was a tense moment for Monty and me.
 * "What did you have for lunch today?"[] I asked.
    "Spam and eggs," he replied.
 * "Nice weather, we're having,"[] I said.
    "I've seen better," he replied.
 - -> house

== walking ==
We continued to walk down the dusty road.
 * [Continue walking]
    -> house

== house ==
Before long, we arrived at his house.
-> END

It allows multiple sections of story to combined together into a single section:
它允许故事的多个部分组合成一个部分:

I had a headache; threading is hard to get your head around.
It was a tense moment for Monty and me.
We continued to walk down the dusty road.
1: "What did you have for lunch today?"
2: "Nice weather, we're having,"
3: Continue walking

On encountering a thread statement such as <- conversation, the compiler will fork the story flow. The first fork considered will run the content at conversation, collecting up any options it finds. Once it has run out of flow here it'll then run the other fork.
当遇到诸如 <- conversation 这样的线程语句时,编译器将分叉故事流。考虑的第一个分叉将运行 conversation 处的内容,收集它找到的所有选项。一旦这里的流量用完,它就会运行另一个分叉。

All the content is collected and shown to the player. But when a choice is chosen, the engine will move to that fork of the story and collapse and discard the others.
所有内容都会被收集并显示给玩家。但当做出选择时,引擎将移动到故事的那个分叉点,并崩溃并丢弃其他选项。

Note that global variables are not forked, including the read counts of knots and stitches.
请注意,全局变量不会分叉,包括结和针的读取计数。

Uses of threads 线程的用途

In a normal story, threads might never be needed.
在正常的故事中,可能永远不需要线程。

But for games with lots of independent moving parts, threads quickly become essential. Imagine a game in which characters move independently around a map: the main story hub for a room might look like the following:
但对于具有大量独立移动部件的游戏来说,线程很快就变得至关重要。想象一个游戏,其中角色在地图上独立移动:房间的主要故事中心可能如下所示:

CONST HALLWAY = 1
CONST OFFICE = 2

VAR player_location = HALLWAY
VAR generals_location = HALLWAY
VAR doctors_location = OFFICE

== run_player_location
	{
		- player_location == HALLWAY: -> hallway
	}

== hallway ==
	<- characters_present(HALLWAY)
	*	[Drawers]	-> examine_drawers
	* 	[Wardrobe] -> examine_wardrobe
	*  [Go to Office] 	-> go_office
	-	-> run_player_location
= examine_drawers
	// etc...

// Here's the thread, which mixes in dialogue for characters you share the room with at the moment.

== characters_present(room)
	{ generals_location == room:
		<- general_conversation
	}
	{ doctors_location == room:
		<- doctor_conversation
	}
	-> DONE

== general_conversation
	*	[Ask the General about the bloodied knife]
		"It's a bad business, I can tell you."
	-	-> run_player_location

== doctor_conversation
	*	[Ask the Doctor about the bloodied knife]
		"There's nothing strange about blood, is there?"
	-	-> run_player_location

Note in particular, that we need an explicit way to return the player who has gone down a side-thread to return to the main flow. In most cases, threads will either need a parameter telling them where to return to, or they'll need to end the current story section.
特别注意,我们需要一种显式的方法来让已经进入侧线程的玩家返回到主流程。在大多数情况下,线程要么需要一个参数来告诉它们返回到哪里,要么需要结束当前的故事部分。

When does a side-thread end?
侧线什么时候结束?

Side-threads end when they run out of flow to process: and note, they collect up options to display later (unlike tunnels, which collect options, display them and follow them until they hit an explicit return, possibly several moves later).
当侧线程耗尽处理流程时就会结束:请注意,它们收集选项以供稍后显示(与隧道不同,隧道收集选项、显示它们并跟踪它们,直到它们达到显式返回,可能会在几次移动之后)。

Sometimes a thread has no content to offer - perhaps there is no conversation to have with a character after all, or perhaps we have simply not written it yet. In that case, we must mark the end of the thread explicitly.
有时,一条线索没有内容可提供——也许根本没有与角色进行对话,或者也许我们只是还没有写出来。在这种情况下,我们必须明确标记线程的结束。

If we didn't, the end of content might be a story-bug or a hanging story thread, and we want the compiler to tell us about those.
如果我们不这样做,内容的结尾可能是一个故事错误或一个悬而未决的故事线程,我们希望编译器告诉我们这些。

Using -> DONE 使用 -> DONE

In cases where we want to mark the end of a thread, we use -> DONE: meaning "the flow intentionally ends here". If we don't, we might end up with a warning message - we can still play the game, but it's a reminder that we have unfinished business.
如果我们想要标记线程的结束,我们使用 -> DONE :意思是“流程故意在此处结束”。如果不这样做,我们可能会收到一条警告消息 - 我们仍然可以玩游戏,但它提醒我们还有未完成的事情。

The example at the start of this section will generate a warning; it can be fixed as follows:
本节开头的示例将生成警告;可以按如下方式修复:

== thread_example ==
I had a headache; threading is hard to get your head around.
<- conversation
<- walking
-> DONE

The extra DONE tells ink that the flow here has ended and it should rely on the threads for the next part of the story.
额外的 DONE 告诉 ink 这里的流程已经结束,它应该依赖于故事下一部分的线程。

Note that we don't need a -> DONE if the flow ends with options that fail their conditions. The engine treats this as a valid, intentional, end of flow state.
请注意,如果流程以不符合其条件的选项结束,我们不需要 -> DONE 。引擎将此视为有效的、有意的、流程结束状态。

You do not need a -> DONE after an option has been chosen. Once an option is chosen, a thread is no longer a thread - it is simply the normal story flow once more.
选择选项后,您不需要 -> DONE 。一旦选择了一个选项,一条线索就不再是一条线索——它只是再次成为正常的故事流程。

Using -> END in this case will not end the thread, but the whole story flow. (And this is the real reason for having two different ways to end flow.)
在这种情况下使用 -> END 不会结束线程,而是整个故事的流程。 (这就是采用两种不同方式结束流程的真正原因。)

Example: adding the same choice to several places
示例:将相同的选择添加到多个位置

Threads can be used to add the same choice into lots of different places. When using them this way, it's normal to pass a divert as a parameter, to tell the story where to go after the choice is done.
线程可用于将相同的选择添加到许多不同的地方。当以这种方式使用它们时,通常会传递一个转移作为参数,以告诉故事在选择完成后要去哪里。

=== outside_the_house
The front step. The house smells. Of murder. And lavender.
- (top)
	<- review_case_notes(-> top)
	*	[Go through the front door]
		I stepped inside the house.
		-> the_hallway
	* 	[Sniff the air]
		I hate lavender. It makes me think of soap, and soap makes me think about my marriage.
		-> top

=== the_hallway
The hallway. Front door open to the street. Little bureau.
- (top)
	<- review_case_notes(-> top)
	*	[Go through the front door]
		I stepped out into the cool sunshine.
		-> outside_the_house
	* 	[Open the bureau]
		Keys. More keys. Even more keys. How many locks do these people need?
		-> top

=== review_case_notes(-> go_back_to)
+	{not done || TURNS_SINCE(-> done) > 10}
	[Review my case notes]
	// the conditional ensures you don't get the option to check repeatedly
 	{I|Once again, I} flicked through the notes I'd made so far. Still not obvious suspects.
- 	(done) -> go_back_to

Note this is different than a tunnel, which runs the same block of content but doesn't give a player a choice. So a layout like:
请注意,这与隧道不同,隧道运行相同的内容块,但不给玩家选择。所以布局如下:

<- childhood_memories(-> next)
*	[Look out of the window]
 	I daydreamed as we rolled along...
 - (next) Then the whistle blew...

might do exactly the same thing as:
可能会做与以下完全相同的事情:

*	[Remember my childhood]
	-> think_back ->
*	[Look out of the window]
	I daydreamed as we rolled along...
- 	(next) Then the whistle blew...

but as soon as the option being threaded in includes multiple choices, or conditional logic on choices (or any text content, of course!), the thread version becomes more practical.
但是,一旦线程中的选项包含多个选择或选项的条件逻辑(当然,或任何文本内容!),线程版本就变得更加实用。

Example: organisation of wide choice points
示例:广泛选择点的组织

A game which uses ink as a script rather than a literal output might often generate very large numbers of parallel choices, intended to be filtered by the player via some other in-game interaction - such as walking around an environment. Threads can be useful in these cases simply to divide up choices.
使用墨迹作为脚本而不是文字输出的游戏通常可能会生成大量并行选择,旨在由玩家通过其他一些游戏内交互(例如在环境中走动)进行过滤。在这些情况下,线程可以用来简单地划分选择。

=== the_kitchen
- (top)
	<- drawers(-> top)
	<- cupboards(-> top)
	<- room_exits
= drawers (-> goback)
	// choices about the drawers...
	...
= cupboards(-> goback)
	// choices about cupboards
	...
= room_exits
	// exits; doesn't need a "return point" as if you leave, you go elsewhere
	...

Part 5: Advanced State Tracking
第 5 部分:高级状态跟踪

Games with lots of interaction can get very complex, very quickly and the writer's job is often as much about maintaining continuity as it is about content.
具有大量交互的游戏可能会变得非常复杂、非常快,而作家的工作通常与保持内容的连续性一样重要。

This becomes particularly important if the game text is intended to model anything - whether it's a game of cards, the player's knowledge of the gameworld so far, or the state of the various light-switches in a house.
如果游戏文本旨在模拟任何事物(无论是纸牌游戏、玩家迄今为止对游戏世界的了解,还是房屋中各种电灯开关的状态),这一点就变得尤为重要。

ink does not provide a full world-modelling system in the manner of a classic parser IF authoring language - there are no "objects", no concepts of "containment" or being "open" or "locked". However, it does provide a simple yet powerful system for tracking state-changes in a very flexible way, to enable writers to approximate world models where necessary.
ink 没有以经典解析器 IF 创作语言的方式提供完整的世界建模系统 - 没有“对象”,没有“包含”或“开放”或“锁定”的概念。然而,它确实提供了一个简单而强大的系统,以非常灵活的方式跟踪状态变化,使编写者能够在必要时近似世界模型。

Note: New feature alert! 注意:新功能提醒​​!

This feature is very new to the language. That means we haven't begun to discover all the ways it might be used - but we're pretty sure it's going to be useful! So if you think of a clever usage we'd love to know!
这个功能对于该语言来说是非常新的。这意味着我们还没有开始发现它的所有使用方式 - 但我们非常确定它会很有用!因此,如果您想到一个巧妙的用法,我们很想知道!

1) Basic Lists 1) 基本清单

The basic unit of state-tracking is a list of states, defined using the LIST keyword. Note that a list is really nothing like a C# list (which is an array).
状态跟踪的基本单位是状态列表,使用 LIST 关键字定义。请注意,列表实际上与 C# 列表(它是一个数组)完全不同。

For instance, we might have:
例如,我们可能有:

LIST kettleState = cold, boiling, recently_boiled

This line defines two things: firstly three new values - cold, boiling and recently_boiled - and secondly, a variable, called kettleState, to hold these states.
该行定义了两件事:首先是三个新值 - coldboilingrecently_boiled - 其次是一个名为 kettleState 的变量,保持这些状态。

We can tell the list what value to take:
我们可以告诉列表取什么值:

~ kettleState = cold

We can change the value:
我们可以改变这个值:

*	[Turn on kettle]
	The kettle begins to bubble and boil.
	~ kettleState = boiling

We can query the value:
我们可以查询该值:

*	[Touch the kettle]
	{ kettleState == cold:
		The kettle is cool to the touch.
	- else:
	 	The outside of the kettle is very warm!
	}

For convenience, we can give a list a value when it's defined using a bracket:
为了方便起见,我们可以在使用括号定义列表时为其赋予一个值:

LIST kettleState = cold, (boiling), recently_boiled
// at the start of the game, this kettle is switched on. Edgy, huh?

...and if the notation for that looks a bit redundant, there's a reason for that coming up in a few subsections time.
...如果这个符号看起来有点多余,那么在几个小节的时间内就会出现这种情况是有原因的。

2) Reusing Lists 2)重用列表

The above example is fine for the kettle, but what if we have a pot on the stove as well? We can then define a list of states, but put them into variables - and as many variables as we want.
上面的例子对于水壶来说很好,但是如果我们在炉子上也有一个锅怎么办?然后,我们可以定义一个状态列表,但将它们放入变量中 - 以及我们想要的任意多个变量。

LIST daysOfTheWeek = Monday, Tuesday, Wednesday, Thursday, Friday
VAR today = Monday
VAR tomorrow = Tuesday

States can be used repeatedly
状态可以重复使用

This allows us to use the same state machine in multiple places.
这允许我们在多个地方使用相同的状态机。

LIST heatedWaterStates = cold, boiling, recently_boiled
VAR kettleState = cold
VAR potState = cold

*	{kettleState == cold} [Turn on kettle]
	The kettle begins to boil and bubble.
	~ kettleState = boiling
*	{potState == cold} [Light stove]
 	The water in the pot begins to boil and bubble.
 	~ potState = boiling

But what if we add a microwave as well? We might want start generalising our functionality a bit:
但如果我们再加一个微波炉呢?我们可能想开始概括一下我们的功能:

LIST heatedWaterStates = cold, boiling, recently_boiled
VAR kettleState = cold
VAR potState = cold
VAR microwaveState = cold

=== function boilSomething(ref thingToBoil, nameOfThing)
	The {nameOfThing} begins to heat up.
	~ thingToBoil = boiling

=== do_cooking
*	{kettleState == cold} [Turn on kettle]
	{boilSomething(kettleState, "kettle")}
*	{potState == cold} [Light stove]
	{boilSomething(potState, "pot")}
*	{microwaveState == cold} [Turn on microwave]
	{boilSomething(microwaveState, "microwave")}

or even... 甚至...

LIST heatedWaterStates = cold, boiling, recently_boiled
VAR kettleState = cold
VAR potState = cold
VAR microwaveState = cold

=== cook_with(nameOfThing, ref thingToBoil)
+ 	{thingToBoil == cold} [Turn on {nameOfThing}]
  	The {nameOfThing} begins to heat up.
	~ thingToBoil = boiling
	-> do_cooking.done

=== do_cooking
<- cook_with("kettle", kettleState)
<- cook_with("pot", potState)
<- cook_with("microwave", microwaveState)
- (done)

Note that the "heatedWaterStates" list is still available as well, and can still be tested, and take a value.
请注意,“heatedWaterStates”列表仍然可用,并且仍然可以进行测试并取值。

List values can share names
列表值可以共享名称

Reusing lists brings with it ambiguity. If we have:
重用列表会带来歧义。如果我们有:

LIST colours = red, green, blue, purple
LIST moods = mad, happy, blue

VAR status = blue

... how can the compiler know which blue you meant?
...编译器怎么知道你指的是哪种蓝色?

We resolve these using a . syntax similar to that used for knots and stitches.
我们使用类似于结和缝线的 . 语法来解决这些问题。

VAR status = colours.blue

...and the compiler will issue an error until you specify.
...并且编译器将发出错误,直到您指定为止。

Note the "family name" of the state, and the variable containing a state, are totally separate. So
请注意,状态的“家族名称”和包含状态的变量是完全独立的。所以

{ statesOfGrace == statesOfGrace.fallen:
	// is the current state "fallen"
}

... is correct. ... 是正确的。

Advanced: a LIST is actually a variable
高级:LIST 实际上是一个变量

One surprising feature is the statement
一个令人惊讶的特点是声明

LIST statesOfGrace = ambiguous, saintly, fallen

actually does two things simultaneously: it creates three values, ambiguous, saintly and fallen, and gives them the name-parent statesOfGrace if needed; and it creates a variable called statesOfGrace.
实际上同时做了两件事:它创建三个值 ambiguoussaintlyfallen ,并为它们提供名称父级 statesOfGrace 如果需要;它创建一个名为 statesOfGrace 的变量。

And that variable can be used like a normal variable. So the following is valid, if horribly confusing and a bad idea:
该变量可以像普通变量一样使用。因此,以下内容是有效的,尽管非常令人困惑并且是一个坏主意:

LIST statesOfGrace = ambiguous, saintly, fallen

~ statesOfGrace = 3.1415 // set the variable to a number not a list value

...and it wouldn't preclude the following from being fine:
...并且它不会排除以下情况:

~ temp anotherStateOfGrace = statesOfGrace.saintly

3) List Values 3) 列出值

When a list is defined, the values are listed in an order, and that order is considered to be significant. In fact, we can treat these values as if they were numbers. (That is to say, they are enums.)
定义列表时,值按顺序列出,并且该顺序被认为是重要的。事实上,我们可以将这些值视为数字。 (也就是说,它们是枚举。)

LIST volumeLevel = off, quiet, medium, loud, deafening
VAR lecturersVolume = quiet
VAR murmurersVolume = quiet

{ lecturersVolume < deafening:
	~ lecturersVolume++

	{ lecturersVolume > murmurersVolume:
		~ murmurersVolume++
		The murmuring gets louder.
	}
}

The values themselves can be printed using the usual {...} syntax, but this will print their name.
值本身可以使用通常的 {...} 语法打印,但这将打印它们的名称。

The lecturer's voice becomes {lecturersVolume}.

Converting values to numbers
将值转换为数字

The numerical value, if needed, can be got explicitly using the LIST_VALUE function. Note the first value in a list has the value 1, and not the value 0.
如果需要,可以使用 LIST_VALUE 函数显式获取数值。请注意,列表中的第一个值是 1,而不是 0。

The lecturer has {LIST_VALUE(deafening) - LIST_VALUE(lecturersVolume)} notches still available to him.

Converting numbers to values
将数字转换为值

You can go the other way by using the list's name as a function:
您可以通过使用列表的名称作为函数来采用另一种方式:

LIST Numbers = one, two, three
VAR score = one
~ score = Numbers(2) // score will be "two"

Advanced: defining your own numerical values
高级:定义您自己的数值

By default, the values in a list start at 1 and go up by one each time, but you can specify your own values if you need to.
默认情况下,列表中的值从 1 开始,每次增加 1,但如果需要,您可以指定自己的值。

LIST primeNumbers = two = 2, three = 3, five = 5

If you specify a value, but not the next value, ink will assume an increment of 1. So the following is the same:
如果您指定一个值,但未指定下一个值,则 ink 将假定增量为 1。因此以下内容是相同的:

LIST primeNumbers = two = 2, three, five = 5

4) Multivalued Lists 4) 多值列表

The following examples have all included one deliberate untruth, which we'll now remove. Lists - and variables containing list values - do not have to contain only one value.
以下示例都包含一个故意的谎言,我们现在将其删除。列表 - 以及包含列表值的变量 - 不必只包含一个值。

Lists are boolean sets 列表是布尔集

A list variable is not a variable containing a number. Rather, a list is like the in/out nameboard in an accommodation block. It contains a list of names, each of which has a room-number associated with it, and a slider to say "in" or "out".
列表变量不是包含数字的变量。相反,列表就像住宿区的进/出名牌。它包含一个名称列表,每个名称都有一个与之关联的房间号,以及一个表示“进”或“出”的滑块。

Maybe no one is in:
也许没有人在:

LIST DoctorsInSurgery = Adams, Bernard, Cartwright, Denver, Eamonn

Maybe everyone is: 也许大家都是:

LIST DoctorsInSurgery = (Adams), (Bernard), (Cartwright), (Denver), (Eamonn)

Or maybe some are and some aren't:
或者也许有些是,有些不是:

LIST DoctorsInSurgery = (Adams), Bernard, (Cartwright), Denver, Eamonn

Names in brackets are included in the initial state of the list.
括号中的名称包含在列表的初始状态中。

Note that if you're defining your own values, you can place the brackets around the whole term or just the name:
请注意,如果您定义自己的值,则可以将整个术语或仅名称放在括号中:

LIST primeNumbers = (two = 2), (three) = 3, (five = 5)

Assiging multiple values 分配多个值

We can assign all the values of the list at once as follows:
我们可以一次分配列表的所有值,如下所示:

~ DoctorsInSurgery = (Adams, Bernard)
~ DoctorsInSurgery = (Adams, Bernard, Eamonn)

We can assign the empty list to clear a list out:
我们可以分配空列表来清除列表:

~ DoctorsInSurgery = ()

Adding and removing entries
添加和删​​除条目

List entries can be added and removed, singly or collectively.
可以单独或集体添加和删除列表条目。

~ DoctorsInSurgery = DoctorsInSurgery + Adams
~ DoctorsInSurgery += Adams  // this is the same as the above
~ DoctorsInSurgery -= Eamonn
~ DoctorsInSurgery += (Eamonn, Denver)
~ DoctorsInSurgery -= (Adams, Eamonn, Denver)

Trying to add an entry that's already in the list does nothing. Trying to remove an entry that's not there also does nothing. Neither produces an error, and a list can never contain duplicate entries.
尝试添加列表中已有的条目不会执行任何操作。尝试删除不存在的条目也没有任何作用。两者都不会产生错误,并且列表永远不能包含重复的条目。

Basic Queries 基本查询

We have a few basic ways of getting information about what's in a list:
我们有一些基本方法来获取有关列表中内容的信息:

LIST DoctorsInSurgery = (Adams), Bernard, (Cartwright), Denver, Eamonn

{LIST_COUNT(DoctorsInSurgery)} 	//  "2"
{LIST_MIN(DoctorsInSurgery)} 		//  "Adams"
{LIST_MAX(DoctorsInSurgery)} 		//  "Cartwright"
{LIST_RANDOM(DoctorsInSurgery)} 	//  "Adams" or "Cartwright"

Testing for emptiness 测试是否空虚

Like most values in ink, a list can be tested "as it is", and will return true, unless it's empty.
与 ink 中的大多数值一样,列表可以“按原样”进行测试,并且将返回 true,除非它为空。

{ DoctorsInSurgery: The surgery is open today. | Everyone has gone home. }

Testing for exact equality
测试完全相等

Testing multi-valued lists is slightly more complex than single-valued ones. Equality (==) now means 'set equality' - that is, all entries are identical.
测试多值列表比单值列表稍微复杂一些。平等 ( == ) 现在意味着“设置平等” - 即所有条目都是相同的。

So one might say:
所以有人可能会说:

{ DoctorsInSurgery == (Adams, Bernard):
	Dr Adams and Dr Bernard are having a loud argument in one corner.
}

If Dr Eamonn is in as well, the two won't argue, as the lists being compared won't be equal - DoctorsInSurgery will have an Eamonn that the list (Adams, Bernard) doesn't have.
如果 Eamonn 博士也在其中,那么两人就不会争论,因为所比较的列表不会相等 - DoctorsInSurgery 将有一个列表(亚当斯、伯纳德)没有的 Eamonn。

Not equals works as expected:
不等于按预期工作:

{ DoctorsInSurgery != (Adams, Bernard):
	At least Adams and Bernard aren't arguing.
}

Testing for containment 遏制测试

What if we just want to simply ask if Adams and Bernard are present? For that we use a new operator, has, otherwise known as ?.
如果我们只想简单地询问 Adams 和 Bernard 是否在场怎么办?为此,我们使用一个新的运算符 has ,也称为 ?

{ DoctorsInSurgery ? (Adams, Bernard):
	Dr Adams and Dr Bernard are having a hushed argument in one corner.
}

And ? can apply to single values too:
? 也可以应用于单个值:

{ DoctorsInSurgery has Eamonn:
	Dr Eamonn is polishing his glasses.
}

We can also negate it, with hasnt or !? (not ?). Note this starts to get a little complicated as
我们也可以使用 hasnt!? (不是 ? )来否定它。请注意,这开始变得有点复杂,因为

DoctorsInSurgery !? (Adams, Bernard)

does not mean neither Adams nor Bernard is present, only that they are not both present (and arguing).
并不意味着亚当斯和伯纳德都不在场,只是他们没有同时在场(并且正在争论)。

Warning: no lists contain the empty list
警告:没有列表包含空列表

Note that the test
请注意,测试

SomeList ? ()

will always return false, regardless of whether SomeList itself is empty. In practice this is the most useful default, as you'll often want to do tests like:
无论 SomeList 本身是否为空,都将始终返回 false。实际上,这是最有用的默认值,因为您经常需要进行如下测试:

SilverWeapons ? best_weapon_to_use 

to fail if the player is empty-handed.
如果玩家两手空空,就会失败。

Example: basic knowledge tracking
示例:基础知识追踪

The simplest use of a multi-valued list is for tracking "game flags" tidily.
多值列表最简单的用途是整齐地跟踪“游戏标志”。

LIST Facts = (Fogg_is_fairly_odd), 	first_name_phileas, (Fogg_is_English)

{Facts ? Fogg_is_fairly_odd:I smiled politely.|I frowned. Was he a lunatic?}
'{Facts ? first_name_phileas:Phileas|Monsieur}, really!' I cried.

In particular, it allows us to test for multiple game flags in a single line.
特别是,它允许我们在一行中测试多个游戏标志。

{ Facts ? (Fogg_is_English, Fogg_is_fairly_odd):
	<> 'I know Englishmen are strange, but this is *incredible*!'
}

Example: a doctor's surgery
示例:医生的手术

We're overdue a fuller example, so here's one.
我们早就该有一个更完整的例子了,所以这是一个。

LIST DoctorsInSurgery = (Adams), Bernard, Cartwright, (Denver), Eamonn

-> waiting_room

=== function whos_in_today()
	In the surgery today are {DoctorsInSurgery}.

=== function doctorEnters(who)
	{ DoctorsInSurgery !? who:
		~ DoctorsInSurgery += who
		Dr {who} arrives in a fluster.
	}

=== function doctorLeaves(who)
	{ DoctorsInSurgery ? who:
		~ DoctorsInSurgery -= who
		Dr {who} leaves for lunch.
	}

=== waiting_room
	{whos_in_today()}
	*	[Time passes...]
		{doctorLeaves(Adams)} {doctorEnters(Cartwright)} {doctorEnters(Eamonn)}
		{whos_in_today()}

This produces: 这会产生:

In the surgery today are Adams, Denver.

> Time passes...

Dr Adams leaves for lunch. Dr Cartwright arrives in a fluster. Dr Eamonn arrives in a fluster.

In the surgery today are Cartwright, Denver, Eamonn.

Advanced: nicer list printing
高级:更好的列表打印

The basic list print is not especially attractive for use in-game. The following is better:
基本的列表打印对于在游戏中使用来说并不是特别有吸引力。下面的比较好:

=== function listWithCommas(list, if_empty)
    {LIST_COUNT(list):
    - 2:
        	{LIST_MIN(list)} and {listWithCommas(list - LIST_MIN(list), if_empty)}
    - 1:
        	{list}
    - 0:
			{if_empty}
    - else:
      		{LIST_MIN(list)}, {listWithCommas(list - LIST_MIN(list), if_empty)}
    }

LIST favouriteDinosaurs = (stegosaurs), brachiosaur, (anklyosaurus), (pleiosaur)

My favourite dinosaurs are {listWithCommas(favouriteDinosaurs, "all extinct")}.

It's probably also useful to have an is/are function to hand:
手头有一个 is/are 函数可能也很有用:

=== function isAre(list)
	{LIST_COUNT(list) == 1:is|are}

My favourite dinosaurs {isAre(favouriteDinosaurs)} {listWithCommas(favouriteDinosaurs, "all extinct")}.

And to be pendantic:
并且要悬垂:

My favourite dinosaur{LIST_COUNT(favouriteDinosaurs) != 1:s} {isAre(favouriteDinosaurs)} {listWithCommas(favouriteDinosaurs, "all extinct")}.

Lists don't need to have multiple entries
列表不需要有多个条目

Lists don't have to contain multiple values. If you want to use a list as a state-machine, the examples above will all work - set values using =, ++ and --; test them using ==, <, <=, > and >=. These will all work as expected.
列表不必包含多个值。如果您想使用列表作为状态机,上面的示例都可以使用 =++-- 设置值;使用 ==<<=>>= 测试它们。这些都将按预期工作。

The "full" list “完整”清单

Note that LIST_COUNT, LIST_MIN and LIST_MAX are refering to who's in/out of the list, not the full set of possible doctors. We can access that using
请注意, LIST_COUNTLIST_MINLIST_MAX 指的是列表中/之外的人员,而不是完整的可能医生集。我们可以使用它来访问它

LIST_ALL(element of list)

or 或者

LIST_ALL(list containing elements of a list)

{LIST_ALL(DoctorsInSurgery)} // Adams, Bernard, Cartwright, Denver, Eamonn
{LIST_COUNT(LIST_ALL(DoctorsInSurgery))} // "5"
{LIST_MIN(LIST_ALL(Eamonn))} 				// "Adams"

Note that printing a list using {...} produces a bare-bones representation of the list; the values as words, delimited by commas.
请注意,使用 {...} 打印列表会生成列表的基本表示;值作为单词,以逗号分隔。

Advanced: "refreshing" a list's type
高级:“刷新”列表类型

If you really need to, you can make an empty list that knows what type of list it is.
如果确实需要,您可以创建一个空列表,知道它是什么类型的列表。

LIST ValueList = first_value, second_value, third_value
VAR myList = ()

~ myList = ValueList()

You'll then be able to do:
然后您将能够执行以下操作:

{ LIST_ALL(myList) }

Advanced: a portion of the "full" list
高级:“完整”列表的一部分

You can also retrieve just a "slice" of the full list, using the LIST_RANGE function. There are two formulations, both valid:
您还可以使用 LIST_RANGE 函数仅检索完整列表的“片段”。有两种表述,均有效:

LIST_RANGE(list_name, min_integer_value, max_integer_value)

and 

LIST_RANGE(list_name, min_value, max_value)

Min and max values here are inclusive. If the game can’t find the values, it’ll get as close as it can, but never go outside the range. So for example:
此处包含最小值和最大值。如果游戏找不到这些值,它会尽可能接近,但永远不会超出范围。例如:

{LIST_RANGE(LIST_ALL(primeNumbers), 10, 20)} 

will produce 将产生

11, 13, 17, 19

Example: Tower of Hanoi 示例:河内塔

To demonstrate a few of these ideas, here's a functional Tower of Hanoi example, written so no one else has to write it.
为了演示其中一些想法,这里有一个功能性的河内塔示例,编写后无需其他人编写。

LIST Discs = one, two, three, four, five, six, seven
VAR post1 = ()
VAR post2 = ()
VAR post3 = ()

~ post1 = LIST_ALL(Discs)

-> gameloop

=== function can_move(from_list, to_list) ===
    {
    -   LIST_COUNT(from_list) == 0:
        // no discs to move
        ~ return false
    -   LIST_COUNT(to_list) > 0 && LIST_MIN(from_list) > LIST_MIN(to_list):
        // the moving disc is bigger than the smallest of the discs on the new tower
        ~ return false
    -   else:
    	 // nothing stands in your way!
        ~ return true

    }

=== function move_ring( ref from, ref to ) ===
    ~ temp whichRingToMove = LIST_MIN(from)
    ~ from -= whichRingToMove
    ~ to += whichRingToMove

== function getListForTower(towerNum)
    { towerNum:
        - 1:    ~ return post1
        - 2:    ~ return post2
        - 3:    ~ return post3
    }

=== function name(postNum)
    the {postToPlace(postNum)} temple

=== function Name(postNum)
    The {postToPlace(postNum)} temple

=== function postToPlace(postNum)
    { postNum:
        - 1: first
        - 2: second
        - 3: third
    }

=== function describe_pillar(listNum) ==
    ~ temp list = getListForTower(listNum)
    {
    - LIST_COUNT(list) == 0:
        {Name(listNum)} is empty.
    - LIST_COUNT(list) == 1:
        The {list} ring lies on {name(listNum)}.
    - else:
        On {name(listNum)}, are the discs numbered {list}.
    }


=== gameloop
    Staring down from the heavens you see your followers finishing construction of the last of the great temples, ready to begin the work.
- (top)
    +  [ Regard the temples]
        You regard each of the temples in turn. On each is stacked the rings of stone. {describe_pillar(1)} {describe_pillar(2)} {describe_pillar(3)}
    <- move_post(1, 2, post1, post2)
    <- move_post(2, 1, post2, post1)
    <- move_post(1, 3, post1, post3)
    <- move_post(3, 1, post3, post1)
    <- move_post(3, 2, post3, post2)
    <- move_post(2, 3, post2, post3)
    -> DONE

= move_post(from_post_num, to_post_num, ref from_post_list, ref to_post_list)
    +   { can_move(from_post_list, to_post_list) }
        [ Move a ring from {name(from_post_num)} to {name(to_post_num)} ]
        { move_ring(from_post_list, to_post_list) }
        { stopping:
        -   The priests far below construct a great harness, and after many years of work, the great stone ring is lifted up into the air, and swung over to the next of the temples.
            The ropes are slashed, and in the blink of an eye it falls once more.
        -   Your next decree is met with a great feast and many sacrifices. After the funeary smoke has cleared, work to shift the great stone ring begins in earnest. A generation grows and falls, and the ring falls into its ordained place.
        -   {cycle:
            - Years pass as the ring is slowly moved.
            - The priests below fight a war over what colour robes to wear, but while they fall and die, the work is still completed.
            }
        }
    -> top

5) Advanced List Operations
5) 高级列表操作

The above section covers basic comparisons. There are a few more powerful features as well, but - as anyone familiar with mathematical sets will know - things begin to get a bit fiddly. So this section comes with an 'advanced' warning.
上面的部分涵盖了基本的比较。还有一些更强大的功能,但是 - 任何熟悉数学集的人都会知道 - 事情开始变得有点棘手。因此,本节带有“高级”警告。

A lot of the features in this section won't be necessary for most games.
本节中的许多功能对于大多数游戏来说并不是必需的。

Comparing lists 比较列表

We can compare lists less than exactly using >, <, >= and <=. Be warned! The definitions we use are not exactly standard fare. They are based on comparing the numerical value of the elements in the lists being tested.
我们可以使用 ><>=<= 来比较列表。被警告!我们使用的定义并不完全是标准票价。它们基于比较正在测试的列表中元素的数值。

"Distinctly bigger than" “明显大于”

LIST_A > LIST_B means "the smallest value in A is bigger than the largest values in B": in other words, if put on a number line, the entirety of A is to the right of the entirety of B. < does the same in reverse.
LIST_A > LIST_B 表示“A 中的最小值大于 B 中的最大值”:换句话说,如果放在数轴上,A 的整体位于 B 的整体的右侧。 < b1> 反过来做同样的事情。

"Definitely never smaller than"
“绝对不会小于”

LIST_A >= LIST_B means - take a deep breath now - "the smallest value in A is at least the smallest value in B, and the largest value in A is at least the largest value in B". That is, if drawn on a number line, the entirety of A is either above B or overlaps with it, but B does not extend higher than A.
LIST_A >= LIST_B 的意思是 - 现在深呼吸 - “A 中的最小值至少是 B 中的最小值,并且 A 中的最大值至少是 B 中的最大值”。也就是说,如果在数轴上绘制,则 A 的整体要么在 B 之上,要么与其重叠,但 B 不会延伸到高于 A 的位置。

Note that LIST_A > LIST_B implies LIST_A != LIST_B, and LIST_A >= LIST_B allows LIST_A == LIST_B but precludes LIST_A < LIST_B, as you might hope.
请注意, LIST_A > LIST_B 暗示 LIST_A != LIST_B ,而 LIST_A >= LIST_B 允许 LIST_A == LIST_B 但排除 LIST_A < LIST_B ,正如您所希望的那样。

Health warning! 健康警告!

LIST_A >= LIST_B is not the same as LIST_A > LIST_B or LIST_A == LIST_B.
LIST_A >= LIST_BLIST_A > LIST_B or LIST_A == LIST_B 不同。

The moral is, don't use these unless you have a clear picture in your mind.
其寓意是,除非您心中有清晰的认识,否则不要使用这些。

Inverting lists 反转列表

A list can be "inverted", which is the equivalent of going through the accommodation in/out name-board and flipping every switch to the opposite of what it was before.
列表可以“反转”,这相当于浏览住宿进/出名牌并将每个开关翻转到与之前相反的位置。

LIST GuardsOnDuty = (Smith), (Jones), Carter, Braithwaite

=== function changingOfTheGuard
	~ GuardsOnDuty = LIST_INVERT(GuardsOnDuty)

Note that LIST_INVERT on an empty list will return a null value, if the game doesn't have enough context to know what invert. If you need to handle that case, it's safest to do it by hand:
请注意,如果游戏没有足够的上下文来了解什么是反转,则空列表上的 LIST_INVERT 将返回空值。如果您需要处理这种情况,手动执行是最安全的:

=== function changingOfTheGuard
	{!GuardsOnDuty: // "is GuardsOnDuty empty right now?"
		~ GuardsOnDuty = LIST_ALL(Smith)
	- else:
		~ GuardsOnDuty = LIST_INVERT(GuardsOnDuty)
	}

Footnote 脚注

The syntax for inversion was originally ~ list but we changed it because otherwise the line
反转的语法最初是 ~ list 但我们更改了它,因为否则该行

~ list = ~ list

was not only functional, but actually caused list to invert itself, which seemed excessively perverse.
不仅是功能性的,而且实际上导致了 list 自身的反转,这看起来太反常了。

Intersecting lists 相交列表

The has or ? operator is, somewhat more formally, the "are you a subset of me" operator, ⊇, which includes the sets being equal, but which doesn't include if the larger set doesn't entirely contain the smaller set.
更正式地说, has? 运算符是“你是我的子集吗”运算符 bas,其中包括相等的集合,但不包括 if较大的集合并不完全包含较小的集合。

To test for "some overlap" between lists, we use the overlap operator, ^, to get the intersection.
为了测试列表之间的“某些重叠”,我们使用重叠运算符 ^ 来获取交集。

LIST CoreValues = strength, courage, compassion, greed, nepotism, self_belief, delusions_of_godhood
VAR desiredValues = (strength, courage, compassion, self_belief )
VAR actualValues =  ( greed, nepotism, self_belief, delusions_of_godhood )

{desiredValues ^ actualValues} // prints "self_belief"

The result is a new list, so you can test it:
结果是一个新列表,因此您可以测试它:

{desiredValues ^ actualValues: The new president has at least one desirable quality.}

{LIST_COUNT(desiredValues ^ actualValues) == 1: Correction, the new president has only one desirable quality. {desiredValues ^ actualValues == self_belief: It's the scary one.}}

6) Multi-list Lists 6) 多列表列表

So far, all of our examples have included one large simplification, again - that the values in a list variable have to all be from the same list family. But they don't.
到目前为止,我们所有的示例都再次进行了一项重大简化 - 列表变量中的值必须全部来自同一列表系列。但他们没有。

This allows us to use lists - which have so far played the role of state-machines and flag-trackers - to also act as general properties, which is useful for world modelling.
这允许我们使用列表(到目前为止,列表扮演着状态机和标志跟踪器的角色)也可以充当通用属性,这对于世界建模很有用。

This is our inception moment. The results are powerful, but also more like "real code" than anything that's come before.
这是我们的起步时刻。结果很强大,但也比以前的任何东西都更像“真实代码”。

Lists to track objects 跟踪对象的列表

For instance, we might define:
例如,我们可以定义:

LIST Characters = Alfred, Batman, Robin
LIST Props = champagne_glass, newspaper

VAR BallroomContents = (Alfred, Batman, newspaper)
VAR HallwayContents = (Robin, champagne_glass)

We could then describe the contents of any room by testing its state:
然后我们可以通过测试房间的状态来描述任何房间的内容:

=== function describe_room(roomState)
	{ roomState ? Alfred: Alfred is here, standing quietly in a corner. } { roomState ? Batman: Batman's presence dominates all. } { roomState ? Robin: Robin is all but forgotten. }
	<> { roomState ? champagne_glass: A champagne glass lies discarded on the floor. } { roomState ? newspaper: On one table, a headline blares out WHO IS THE BATMAN? AND *WHO* IS HIS BARELY-REMEMBERED ASSISTANT? }

So then: 那么:

{ describe_room(BallroomContents) }

produces: 产生:

Alfred is here, standing quietly in a corner. Batman's presence dominates all.

On one table, a headline blares out WHO IS THE BATMAN? AND *WHO* IS HIS BARELY-REMEMBERED ASSISTANT?

While: 尽管:

{ describe_room(HallwayContents) }

gives: 给出:

Robin is all but forgotten.

A champagne glass lies discarded on the floor.

And we could have options based on combinations of things:
我们可以根据事物的组合进行选择:

*	{ currentRoomState ? (Batman, Alfred) } [Talk to Alfred and Batman]
	'Say, do you two know each other?'

Lists to track multiple states
跟踪多个状态的列表

We can model devices with multiple states. Back to the kettle again...
我们可以对具有多种状态的设备进行建模。又回到水壶了……

LIST OnOff = on, off
LIST HotCold = cold, warm, hot

VAR kettleState = off, cold

=== function turnOnKettle() ===
{ kettleState ? hot:
	You turn on the kettle, but it immediately flips off again.
- else:
	The water in the kettle begins to heat up.
	~ kettleState -= off
	~ kettleState += on
	// note we avoid "=" as it'll remove all existing states
}

=== function can_make_tea() ===
	~ return kettleState ? (hot, off)

These mixed states can make changing state a bit trickier, as the off/on above demonstrates, so the following helper function can be useful.
这些混合状态可能会使更改状态变得有点棘手,如上面的关闭/打开所示,因此以下辅助函数可能很有用。

=== function changeStateTo(ref stateVariable, stateToReach)
	// remove all states of this type
	~ stateVariable -= LIST_ALL(stateToReach)
	// put back the state we want
	~ stateVariable += stateToReach

which enables code like:
这使得代码如下:

~ changeState(kettleState, on)
~ changeState(kettleState, warm)

How does this affect queries?
这对查询有何影响?

The queries given above mostly generalise nicely to multi-valued lists
上面给出的查询大多可以很好地推广到多值列表

LIST Letters = a,b,c
LIST Numbers = one, two, three

VAR mixedList = (a, three, c)

{LIST_ALL(mixedList)}   // a, one, b, two, c, three
{LIST_COUNT(mixedList)} // 3
{LIST_MIN(mixedList)}   // a
{LIST_MAX(mixedList)}   // three or c, albeit unpredictably

{mixedList ? (a,b) }        // false
{mixedList ^ LIST_ALL(a)}   // a, c

{ mixedList >= (one, a) }   // true
{ mixedList < (three) }     // false

{ LIST_INVERT(mixedList) }            // one, b, two

7) Long example: crime scene
7) 长例子:犯罪现场

Finally, here's a long example, demonstrating a lot of ideas from this section in action. You might want to try playing it before reading through to better understand the various moving parts.
最后,这是一个很长的示例,演示了本节中的许多想法的实际应用。您可能想在通读之前尝试玩一下,以更好地理解各个活动部分。

-> murder_scene

// Helper function: popping elements from lists
=== function pop(ref list)
   ~ temp x = LIST_MIN(list) 
   ~ list -= x 
   ~ return x

//
//  System: items can have various states
//  Some are general, some specific to particular items
//


LIST OffOn = off, on
LIST SeenUnseen = unseen, seen

LIST GlassState = (none), steamed, steam_gone
LIST BedState = (made_up), covers_shifted, covers_off, bloodstain_visible

//
// System: inventory
//

LIST Inventory = (none), cane, knife

=== function get(x)
    ~ Inventory += x

//
// System: positioning things
// Items can be put in and on places
//

LIST Supporters = on_desk, on_floor, on_bed, under_bed, held, with_joe

=== function move_to_supporter(ref item_state, new_supporter) ===
    ~ item_state -= LIST_ALL(Supporters)
    ~ item_state += new_supporter


// System: Incremental knowledge.
// Each list is a chain of facts. Each fact supersedes the fact before 
//

VAR knowledgeState = ()

=== function reached (x) 
   ~ return knowledgeState ? x 

=== function between(x, y) 
   ~ return knowledgeState? x && not (knowledgeState ^ y)

=== function reach(statesToSet) 
   ~ temp x = pop(statesToSet)
   {
   - not x: 
      ~ return false 

   - not reached(x):
      ~ temp chain = LIST_ALL(x)
      ~ temp statesGained = LIST_RANGE(chain, LIST_MIN(chain), x)
      ~ knowledgeState += statesGained
      ~ reach (statesToSet) 	// set any other states left to set
      ~ return true  	       // and we set this state, so true
 
    - else:
      ~ return false || reach(statesToSet) 
    }	

//
// Set up the game
//

VAR bedroomLightState = (off, on_desk)

VAR knifeState = (under_bed)


//
// Knowledge chains
//


LIST BedKnowledge = neatly_made, crumpled_duvet, hastily_remade, body_on_bed, murdered_in_bed, murdered_while_asleep

LIST KnifeKnowledge = prints_on_knife, joe_seen_prints_on_knife,joe_wants_better_prints, joe_got_better_prints

LIST WindowKnowledge = steam_on_glass, fingerprints_on_glass, fingerprints_on_glass_match_knife


//
// Content
//

=== murder_scene ===
    The bedroom. This is where it happened. Now to look for clues.
- (top)
    { bedroomLightState ? seen:     <- seen_light  }
    <- compare_prints(-> top)

*   (dobed) [The bed...]
    The bed was low to the ground, but not so low something might not roll underneath. It was still neatly made.
    ~ reach (neatly_made)
    - - (bedhub)
    * *     [Lift the bedcover]
            I lifted back the bedcover. The duvet underneath was crumpled.
            ~ reach (crumpled_duvet)
            ~ BedState = covers_shifted
    * *     (uncover) {reached(crumpled_duvet)}
            [Remove the cover]
            Careful not to disturb anything beneath, I removed the cover entirely. The duvet below was rumpled.
            Not the work of the maid, who was conscientious to a point. Clearly this had been thrown on in a hurry.
            ~ reach (hastily_remade)
            ~ BedState = covers_off
    * *     (duvet) {BedState == covers_off} [ Pull back the duvet ]
            I pulled back the duvet. Beneath it was a sheet, sticky with blood.
            ~ BedState = bloodstain_visible
            ~ reach (body_on_bed)
            Either the body had been moved here before being dragged to the floor - or this is was where the murder had taken place.
    * *     {BedState !? made_up} [ Remake the bed ]
            Carefully, I pulled the bedsheets back into place, trying to make it seem undisturbed.
            ~ BedState = made_up
    * *     [Test the bed]
            I pushed the bed with spread fingers. It creaked a little, but not so much as to be obnoxious.
    * *     (darkunder) [Look under the bed]
            Lying down, I peered under the bed, but could make nothing out.

    * *     {TURNS_SINCE(-> dobed) > 1} [Something else?]
            I took a step back from the bed and looked around.
            -> top
    - -     -> bedhub

*   {darkunder && bedroomLightState ? on_floor && bedroomLightState ? on}
    [ Look under the bed ]
    I peered under the bed. Something glinted back at me.
    - - (reaching)
    * *     [ Reach for it ]
            I fished with one arm under the bed, but whatever it was, it had been kicked far enough back that I couldn't get my fingers on it.
            -> reaching
    * *     {Inventory ? cane} [Knock it with the cane]
            -> knock_with_cane

    * *     {reaching > 1 } [ Stand up ]
            I stood up once more, and brushed my coat down.
            -> top

*   (knock_with_cane) {reaching && TURNS_SINCE(-> reaching) >= 4 &&  Inventory ? cane } [Use the cane to reach under the bed ]
    Positioning the cane above the carpet, I gave the glinting thing a sharp tap. It slid out from the under the foot of the bed.
    ~ move_to_supporter( knifeState, on_floor )
    * *     (standup) [Stand up]
            Satisfied, I stood up, and saw I had knocked free a bloodied knife.
            -> top

    * *     [Look under the bed once more]
            Moving the cane aside, I looked under the bed once more, but there was nothing more there.
            -> standup

*   {knifeState ? on_floor} [Pick up the knife]
    Careful not to touch the handle, I lifted the blade from the carpet.
    ~ get(knife)

*   {Inventory ? knife} [Look at the knife]
    The blood was dry enough. Dry enough to show up partial prints on the hilt!
    ~ reach (prints_on_knife)

*   [   The desk... ]
    I turned my attention to the desk. A lamp sat in one corner, a neat, empty in-tray in the other. There was nothing else out.
    Leaning against the desk was a wooden cane.
    ~ bedroomLightState += seen

    - - (deskstate)
    * *     (pickup_cane) {Inventory !? cane}  [Pick up the cane ]
            ~ get(cane)
          I picked up the wooden cane. It was heavy, and unmarked.

    * *    { bedroomLightState !? on } [Turn on the lamp]
            -> operate_lamp ->

    * *     [Look at the in-tray ]
            I regarded the in-tray, but there was nothing to be seen. Either the victim's papers were taken, or his line of work had seriously dried up. Or the in-tray was all for show.

    + +     (open)  {open < 3} [Open a drawer]
            I tried {a drawer at random|another drawer|a third drawer}. {Locked|Also locked|Unsurprisingly, locked as well}.

    * *     {deskstate >= 2} [Something else?]
            I took a step away from the desk once more.
            -> top

    - -     -> deskstate

*     {(Inventory ? cane) && TURNS_SINCE(-> deskstate) <= 2} [Swoosh the cane]
    I was still holding the cane: I gave it an experimental swoosh. It was heavy indeed, though not heavy enough to be used as a bludgeon.
    But it might have been useful in self-defence. Why hadn't the victim reached for it? Knocked it over?

*   [The window...]
    I went over to the window and peered out. A dismal view of the little brook that ran down beside the house.

    - - (window_opts)
    <- compare_prints(-> window_opts)
    * *     (downy) [Look down at the brook]
            { GlassState ? steamed:
                Through the steamed glass I couldn't see the brook. -> see_prints_on_glass -> window_opts
            }
            I watched the little stream rush past for a while. The house probably had damp but otherwise, it told me nothing.
    * *     (greasy) [Look at the glass]
            { GlassState ? steamed: -> downy }
            The glass in the window was greasy. No one had cleaned it in a while, inside or out.
    * *     { GlassState ? steamed && not see_prints_on_glass && downy && greasy }
            [ Look at the steam ]
            A cold day outside. Natural my breath should steam. -> see_prints_on_glass ->
    + +     {GlassState ? steam_gone} [ Breathe on the glass ]
            I breathed gently on the glass once more. { reached (fingerprints_on_glass): The fingerprints reappeared. }
            ~ GlassState = steamed

    + +     [Something else?]
            { window_opts < 2 || reached (fingerprints_on_glass) || GlassState ? steamed:
                I looked away from the dreary glass.
                {GlassState ? steamed:
                    ~ GlassState = steam_gone
                    <> The steam from my breath faded.
                }
                -> top
            }
            I leant back from the glass. My breath had steamed up the pane a little.
           ~ GlassState = steamed

    - -     -> window_opts

*   {top >= 5} [Leave the room]
    I'd seen enough. I {bedroomLightState ? on:switched off the lamp, then} turned and left the room.
    -> joe_in_hall

-   -> top


= operate_lamp
    I flicked the light switch.
    { bedroomLightState ? on:
        <> The bulb fell dark.
        ~ bedroomLightState += off
        ~ bedroomLightState -= on
    - else:
        { bedroomLightState ? on_floor: <> A little light spilled under the bed.} { bedroomLightState ? on_desk : <> The light gleamed on the polished tabletop. }
        ~ bedroomLightState -= off
        ~ bedroomLightState += on
    }
    ->->


= compare_prints (-> backto)
    *   { between ((fingerprints_on_glass, prints_on_knife),     fingerprints_on_glass_match_knife) } 
[Compare the prints on the knife and the window ]
        Holding the bloodied knife near the window, I breathed to bring out the prints once more, and compared them as best I could.
        Hardly scientific, but they seemed very similar - very similiar indeed.
        ~ reach (fingerprints_on_glass_match_knife)
        -> backto

= see_prints_on_glass
    ~ reach (fingerprints_on_glass)
    {But I could see a few fingerprints, as though someone hadpressed their palm against it.|The fingerprints were quite clear and well-formed.} They faded as I watched.
    ~ GlassState = steam_gone
    ->->

= seen_light
    *   {bedroomLightState !? on} [ Turn on lamp ]
        -> operate_lamp ->

    *   { bedroomLightState !? on_bed  && BedState ? bloodstain_visible }
        [ Move the light to the bed ]
        ~ move_to_supporter(bedroomLightState, on_bed)

        I moved the light over to the bloodstain and peered closely at it. It had soaked deeply into the fibres of the cotton sheet.
        There was no doubt about it. This was where the blow had been struck.
        ~ reach (murdered_in_bed)

    *   { bedroomLightState !? on_desk } {TURNS_SINCE(-> floorit) >= 2 }
        [ Move the light back to the desk ]
        ~ move_to_supporter(bedroomLightState, on_desk)
        I moved the light back to the desk, setting it down where it had originally been.
    *   (floorit) { bedroomLightState !? on_floor && darkunder }
        [Move the light to the floor ]
        ~ move_to_supporter(bedroomLightState, on_floor)
        I picked the light up and set it down on the floor.
    -   -> top

=== joe_in_hall
    My police contact, Joe, was waiting in the hall. 'So?' he demanded. 'Did you find anything interesting?'
- (found)
    *   {found == 1} 'Nothing.'
        He shrugged. 'Shame.'
        -> done
    *   { Inventory ? knife } 'I found the murder weapon.'
        'Good going!' Joe replied with a grin. 'We thought the murderer had gotten rid of it. I'll bag that for you now.'
        ~ move_to_supporter(knifeState, with_joe)

    *   {reached(prints_on_knife)} { knifeState ? with_joe }
        'There are prints on the blade[.'],' I told him.
        He regarded them carefully.
        'Hrm. Not very complete. It'll be hard to get a match from these.'
        ~ reach (joe_seen_prints_on_knife)
    *   { reached((fingerprints_on_glass_match_knife, joe_seen_prints_on_knife)) }
        'They match a set of prints on the window, too.'
        'Anyone could have touched the window,' Joe replied thoughtfully. 'But if they're more complete, they should help us get a decent match!'
        ~ reach (joe_wants_better_prints)
    *   { between(body_on_bed, murdered_in_bed)}
        'The body was moved to the bed at some point[.'],' I told him. 'And then moved back to the floor.'
        'Why?'
        * *     'I don't know.'
                Joe nods. 'All right.'
        * *     'Perhaps to get something from the floor?'
                'You wouldn't move a whole body for that.'
        * *     'Perhaps he was killed in bed.'
                'It's just speculation at this point,' Joe remarks.
    *   { reached(murdered_in_bed) }
        'The victim was murdered in bed, and then the body was moved to the floor.'
        'Why?'
        * *     'I don't know.'
                Joe nods. 'All right, then.'
        * *     'Perhaps the murderer wanted to mislead us.'
                'How so?'
            * * *   'They wanted us to think the victim was awake[.'], I replied thoughtfully. 'That they were meeting their attacker, rather than being stabbed in their sleep.'
            * * *   'They wanted us to think there was some kind of struggle[.'],' I replied. 'That the victim wasn't simply stabbed in their sleep.'
            - - -   'But if they were killed in bed, that's most likely what happened. Stabbed, while sleeping.'
                    ~ reach (murdered_while_asleep)
        * *     'Perhaps the murderer hoped to clean up the scene.'
                'But they were disturbed? It's possible.'

    *   { found > 1} 'That's it.'
        'All right. It's a start,' Joe replied.
        -> done
    -   -> found
-   (done)
    {
    - between(joe_wants_better_prints, joe_got_better_prints):
        ~ reach (joe_got_better_prints)
        <> 'I'll get those prints from the window now.'
    - reached(joe_seen_prints_on_knife):
        <> 'I'll run those prints as best I can.'
    - else:
        <> 'Not much to go on.'
    }
    -> END

8) Summary 8)总结

To summarise a difficult section, ink's list construction provides:
总结一下困难的部分,ink 的列表结构提供了:

Flags 旗帜

  • Each list entry is an event
    每个列表条目都是一个事件
  • Use += to mark an event as having occurred
    使用 += 将事件标记为已发生
  • Test using ? and !?
    使用 ?!? 进行测试

Example: 例子:

LIST GameEvents = foundSword, openedCasket, metGorgon
{ GameEvents ? openedCasket }
{ GameEvents ? (foundSword, metGorgon) }
~ GameEvents += metGorgon

State machines 状态机

  • Each list entry is a state
    每个列表条目都是一个状态
  • Use = to set the state; ++ and -- to step forward or backward
    使用 = 设置状态; ++-- 前进或后退
  • Test using ==, > etc
    使用 ==> 等进行测试

Example: 例子:

LIST PancakeState = ingredients_gathered, batter_mix, pan_hot, pancakes_tossed, ready_to_eat
{ PancakeState == batter_mix }
{ PancakeState < ready_to_eat }
~ PancakeState++

Properties 特性

  • Each list is a different property, with values for the states that property can take (on or off, lit or unlit, etc)
    每个列表都是一个不同的属性,具有属性可以采取的状态的值(打开或关闭、点亮或熄灭等)
  • Change state by removing the old state, then adding in the new
    通过删除旧状态然后添加新状态来更改状态
  • Test using ? and !?
    使用 ?!? 进行测试

Example: 例子:

LIST OnOffState = on, off
LIST ChargeState = uncharged, charging, charged

VAR PhoneState = (off, uncharged)

*	{PhoneState !? uncharged } [Plug in phone]
	~ PhoneState -= LIST_ALL(ChargeState)
	~ PhoneState += charging
	You plug the phone into charge.
*	{ PhoneState ? (on, charged) } [ Call my mother ]

Part 6: International character support in identifiers
第 6 部分:标识符中的国际字符支持

By default, ink has no limitations on the use of non-ASCII characters inside the story content. However, a limitation currently exsits on the characters that can be used for names of constants, variables, stictches, diverts and other named flow elements (a.k.a. identifiers).
默认情况下,ink 对故事内容中非 ASCII 字符的使用没有限制。然而,目前可用于常量、变量、缝合、转向和其他命名流元素(也称为标识符)的名称的字符存在限制。

Sometimes it is inconvenient for a writer using a non-ASCII language to write a story because they have to constantly switch to naming identifiers in ASCII and then switching back to whatever language they are using for the story. In addition, naming identifiers in the author's own language could improve the overal readibility of the raw story format.
有时,使用非 ASCII 语言编写故事的作者很不方便,因为他们必须不断切换到 ASCII 中的命名标识符,然后再切换回他们在故事中使用的任何语言。此外,用作者自己的语言命名标识符可以提高原始故事格式的整体可读性。

In an effort to assist in the above scenario, ink automatically supports a list of pre-defined non-ASCII character ranges that can be used as identifiers. In general, those ranges have been selected to include the alpha-numeric subset of the official unicode character range, which would suffice for naming identifiers. The below section gives more detailed information on the non-ASCII characters that ink automatically supports.
为了帮助解决上述情况,ink 自动支持可用作标识符的预定义非 ASCII 字符范围列表。一般来说,这些范围已被选择为包括官方 unicode 字符范围的字母数字子集,这足以命名标识符。以下部分提供了有关 ink 自动支持的非 ASCII 字符的更多详细信息。

Supported Identifier Characters
支持的标识符字符

The support for the additional character ranges in ink is currently limited to a predefined set of character ranges.
目前对墨迹中附加字符范围的支持仅限于一组预定义的字符范围。

Below is a listing of the currently supported identifier ranges.
以下是当前支持的标识符范围的列表。

  • Arabic 阿拉伯

    Enables characters for languages of the Arabic family and is a subset of the official Arabic unicode range \u0600-\u06FF.
    启用阿拉伯语系语言的字符,是官方阿拉伯语 unicode 范围 \u0600 - \u06FF 的子集。

  • Armenian 亚美尼亚语

    Enables characters for the Armenian language and is a subset of the official Armenian unicode range \u0530-\u058F.
    启用亚美尼亚语言的字符,并且是官方亚美尼亚 unicode 范围 \u0530 - \u058F 的子集。

  • Cyrillic 西里尔

    Enables characters for languages using the Cyrillic alphabet and is a subset of the official Cyrillic unicode range \u0400-\u04FF.
    启用使用西里尔字母的语言的字符,并且是官方西里尔 unicode 范围 \u0400 - \u04FF 的子集。

  • Greek 希腊语

    Enables characters for languages using the Greek alphabet and is a subset of the official Greek and Coptic unicode range \u0370-\u03FF.
    启用使用希腊字母的语言的字符,并且是官方希腊语和科普特语 unicode 范围 \u0370 - \u03FF 的子集。

  • Hebrew 希伯来语

    Enables characters in Hebrew using the Hebrew alphabet and is a subset of the official Hebrew unicode range \u0590-\u05FF.
    使用希伯来字母启用希伯来语字符,并且是官方希伯来语 unicode 范围 \u0590 - \u05FF 的子集。

  • Latin Extended A 拉丁语扩展 A

    Enables an extended character range subset of the Latin alphabet - completely represented by the official Latin Extended-A unicode range \u0100-\u017F.
    启用拉丁字母表的扩展字符范围子集 - 完全由官方拉丁扩展 A unicode 范围 \u0100 - \u017F 表示。

  • Latin Extended B 拉丁语扩展B

    Enables an extended character range subset of the Latin alphabet - completely represented by the official Latin Extended-B unicode range \u0180-\u024F.
    启用拉丁字母表的扩展字符范围子集 - 完全由官方拉丁扩展 B unicode 范围 \u0180 - \u024F 表示。

  • Latin 1 Supplement 拉丁语 1 补充

    Enables an extended character range subset of the Latin alphabet - completely represented by the official Latin 1 Supplement unicode range \u0080 - \u00FF.
    启用拉丁字母表的扩展字符范围子集 - 完全由官方 Latin 1 Suplement unicode 范围 \u0080 - \u00FF 表示。

NOTE! ink files should be saved in UTF-8 format, which ensures that the above character ranges are supported.
笔记! ink 文件应以 UTF-8 格式保存,以确保支持上述字符范围。

If a particular character range that you would like to use within identifiers isn't supported, feel free to open an issue or pull request on the main ink repo.
如果不支持您想要在标识符中使用的特定字符范围,请随时在主 Ink 存储库上提出问题或拉取请求。

ink/Documentation/WritingWithInk.md at master · inkle/ink