Multireplace

Choicescript has another handy feature called multireplace; it can be used to avoid long sections of *if for moments that would only contain different text. It can also keep it all in one line, improving code readability. ChoiceScript 还有一个方便的功能叫做多重替换;它可以用于避免在仅包含不同文本的时刻使用冗长的 *if 部分。它还可以将所有内容保持在一行中,提高代码的可读性。

Basic Usage

Multireplace is a function that lets you use a combination of the @{ | } characters to display different bits of flavor text linked to either Boolean (true/false) variables or numeric variables. These bits of flavor text are separated using a pipe character: | . 多重替换是一种函数,允许您使用 @{ | } 字符的组合来显示与布尔(真/假)变量或数值变量相关联的不同描述性文本片段。这些描述性文本片段使用竖线字符进行分隔:|。

Boolean example: @{variable Text if true|Text if false}

Numeric example: @{variable Text if 1|Text if 2|Text if 3|Text if 4}

Numeric example: @{(variable+1) Text if 0|Text if 1|Text if 2|Text if 3}

Multireplace can also be used to check any expression that that evaluates to true/false or natural numbers. See "Checking Expressions as Booleans" and "Advanced Multireplace" below for examples. 多重替换也可用于检查任何评估结果为真/假或自然数的表达式。有关示例,请参阅下文的“将表达式作为布尔值检查”和“高级多重替换”。

Multireplace is most useful when you want: 当您需要以下功能时,多重替换最为有用:

  • short, varying flavor text 简短的、变化多样的风味文本

  • with no para breaks 无段落分隔

  • without setting any variables 不设置任何变量

  • linked to a variable with a finite, discrete, and ideally small number of possible states. 链接到一个具有有限、离散且理想情况下状态数量较少的变量。

If your variable always returns an identical range of text options, e.g. if "dragon_color" can only be "red," black," "gold," "green," or "purple," then it would be most efficient to set "dragon_color" as a string variable and display it as ${dragon_color}, rather than setting it as a numeric variable and displaying the text using multireplace. 如果你的变量总是返回相同的文本选项范围,例如如果“dragon_color”只能是“红色”、“黑色”、“金色”、“绿色”或“紫色”,那么将“dragon_color”设置为字符串变量并显示为${dragon_color},会比将其设置为数值变量并使用多重替换显示文本更为高效。

Multireplace is better suited for when you want to use different text in different contexts. For example, if the variable "dragon_attitude" could be set to one of four numbers (1-4) by the main character's choices in the game, you could then use it in several multireplace contexts: 多重替换更适用于需要在不同语境中使用不同文本的情况。例如,如果变量“dragon_attitude”可以通过玩家在游戏中的选择设置为四个数字(1-4)之一,那么你可以在多个多重替换语境中使用它:

The dragon Nzak regards you with @{dragon_attitude hate|scorn|indifference|affection}.

"We have to work together," says Nzak. @{dragon_attitude "But don't think I'll ever forget that you slew my mate."|"What a pathetic joke!"|It gives the draconic equivalent of a shrug.|"But I want you to know that I'd choose to, even if circumstances hadn't forced us together."}

"You're wounded," Nzak observes @{dragon_attitude with relish|sneeringly|blandly|with concern}.

Uses like these are much more code- and time-efficient than typing out "*if dragon_attitude = 1, *if dragon_attitude = 2" etc. every time you want to have varying flavor text. 这类用法比每次需要变化描述文本时都输入“*if dragon_attitude = 1, *if dragon_attitude = 2”等代码要节省大量编码时间和精力。

Multireplace can contain other variables: 多重替换可以包含其他变量:

A multireplace can even contain line breaks, using the inline [n/] characterarrow-up-right. But multireplace can't contain paragraph breaks (since using double-[n/] for para breaks is bad for screen-readers), can't contain commands or *set other variables, and can't include a second multireplace nested inside it. For those, you'll still need *if. 多重替换甚至可以使用内联的[n/]字符来包含换行。但多重替换不能包含段落分隔(因为使用双[n/]进行段落分隔对屏幕阅读器不友好),不能包含命令或设置其他变量,也不能嵌套包含第二个多重替换。对于这些情况,你仍然需要使用if。

To pass ChoiceScript's automatic testers, multireplace needs to cover every value of a variable that is achievable in your game. That's easy for Booleans (which should have one pipe between the brackets, distinguishing true|false), but can be a little trickier with numeric variables. For example, let's say your game has a variable, "stan_trust," which can be set to 1 (you trust Stan), 2 (you distrust Stan without any reason), or 3 (Stan has betrayed you and you'll never trust them again). Even before Stan betrays you, i.e. before it's possible for the variable to actually be set to 3, any use of "stan_trust" in a multireplace needs to have a third term, or it will fail Quicktest. 为了让游戏通过ChoiceScript的自动测试,多重替换必须涵盖游戏中变量所有可能出现的值。这对于布尔变量来说很简单(括号内应该用一条竖线区分true|false),但对于数值变量可能会有些棘手。例如,假设你的游戏中有一个变量"stan_trust",它可以被设置为1(你信任斯坦)、2(你毫无理由地不信任斯坦)或3(斯坦背叛了你,你再也不会信任他)。即使在斯坦背叛你之前,即在该变量实际上可能被设置为3之前,任何在多重替换中使用"stan_trust"的地方都必须包含第三个选项,否则快速测试就会失败。

So you might write: 所以你可能会这样写:

or just 或者直接写:

or even 甚至

--you don't need to fill all the spaces around/between pipes with text, but if you don't include at least an empty option on one side of a pipe corresponding to every possible value of the variable, your game won't pass Quicktest. --你无需在竖线周围/之间填满所有空格,但若未为变量的每个可能值在竖线对应侧至少包含一个空选项,你的游戏将无法通过快速测试。

Because multireplace needs to include a potential response for every game-achievable value of a variable, it is poorly suited to many uses of numeric variables, e.g. numeric variables that change incrementally (where you'd want the flavor text to correspond to a range of values rather than being different for each value), that have gaps or jumps, or that just go on for too long. Don't expect to use multireplace and fairmath on the same variable! 由于多重替换需要包含变量在游戏中可达成的每个可能值的对应回应,因此它不太适合许多数值变量的应用场景,例如那些逐步递增的数值变量(此时你希望描述文本对应一个数值范围,而非每个值都不同)、存在间隔或跳跃的数值变量,或者数值范围过长的变量。切勿对同一变量同时使用多重替换和公平数学!

(Though see "Advanced Multireplace" below for the use of moduloarrow-up-right to create expressions using fairmath variables that multireplace can parse.) (但可参阅下文“高级多重替换”部分,了解如何利用模运算创建多重替换能够解析的、使用公平数学变量的表达式。)

Advantages of Multireplace

Code using multireplace is generally more condensed than the alternatives. Compare: 使用多重替换的代码通常比替代方案更简洁。对比:

or: 或:

Having all your flavor text variation in one line can make the code more readable, especially with relatively simple Booleans. 将所有风味文本变体放在同一行可以使代码更具可读性,尤其是在处理相对简单的布尔值时。

However, as the number of terms in a numeric variable increases, the readability of a multireplace arguably decreases, and the odds of a mistake definitely increase. 然而,随着数值变量中术语数量的增加,多重替换的可读性可能会降低,而出错的几率肯定会增加。

While this is much shorter than writing out a list of all sixteen potential Choice of Rebels traumas with *if checks, it's unlikely that you or anyone checking your code will catch the mistake if you happened to confuse trauma number 12 with trauma number 13. 虽然这比列出《Choice of Rebels》中所有十六种潜在创伤并搭配*if条件检查要简短得多,但若你不慎混淆了12号创伤与13号创伤,你或任何审查代码的人都很难发现这个错误。

Multireplace is great with Booleans. If a key character is present or absent, it's easy to use a Boolean like "ayla_here" to give them a dialogue tag or a reaction that would otherwise go to someone else: 多重替换在处理布尔值时非常出色。若某个关键角色在场或缺席,可以轻松利用如"ayla_here"这样的布尔变量,将原本属于他人的对话标签或反应分配给他们:

If you have Booleans for whether a character is in love with you, or injured, or whether a particular event happened, you can use multireplace for simple in-line variations on the game text to take those differences into account. 如果设置了角色是否爱慕你、是否受伤,或特定事件是否发生的布尔变量,就能运用多重替换功能,在游戏文本中进行简单的行内调整,以体现这些差异。

Multireplace also makes it easier to vary text inside #options. It lets you replace e.g. 多重替换也让在#options内部变化文本变得更加容易。它允许你替换例如:

with: 配合使用:

Always watch your punctuation when using multireplace, though - it's easy to end up missing a comma, full stop, or quotation marks, or to have them double up, if you're not consistent in using them either inside or outside the curly brackets. 不过,在使用多重替换时,请务必注意标点符号——如果不坚持将标点放在花括号内部或外部,很容易遗漏逗号、句号或引号,或者导致它们重复出现。

Managing Verbs for Singular They

In April 2016, ChoiceScript author Lynnea Glasser wrote herearrow-up-right about the challenge of coding for a character who might prefer either a traditional singular pronoun or the pronoun "they." The differences in verb conjugations can be tricky to manage with a typical ${variable} code; compare "he flies," with "they fly"; "she is," with "they are"; "xe doesn't," with "they don't." In some cases, managing it took multiple lines of code: 2016年4月,ChoiceScript作者Lynnea Glasser在此撰文探讨了为可能偏好传统单数代词或代词"they"的角色编写代码所面临的挑战。动词变形的差异用典型的${variable}代码处理起来可能很棘手;比较一下"he flies"与"they fly";"she is"与"they are";"xe doesn't"与"they don't"。在某些情况下,处理这个问题需要多行代码:

The multireplace function, introduced a year or so after Glasser's blog post, makes it possible to code this more efficiently. 多重替换函数,大约在格拉瑟博客文章发布一年后引入,使得更高效地编写此类代码成为可能。

Here's one way to do it. In startup.txt, create a string variable for the character's pronoun (which may later be set to e.g. "he," "she," "they," or a neopronounarrow-up-right) as well as a Boolean variable for whether the pronoun is plural. 一种实现方法如下。在 startup.txt 中,为角色的代词创建一个字符串变量(后续可设置为例如“他”、“她”、“他们”或新代词),同时创建一个布尔变量来标识该代词是否为复数形式。

When the character's gender is set, if the character uses the pronoun "they," (or for that matter any other plural pronoun--maybe they prefer the royal wearrow-up-right?) the variable plural should be set to true. 当角色性别设定后,如果角色使用代词“they”(或任何其他复数代词——或许他们偏好使用皇家“we”?),变量plural应设为true。

From that point on, multireplace can be used to select the right verb conjugation: 此后,便可使用多重替换功能来选取正确的动词变位:

This could display as "They don't want to go." or "She doesn't want to go." depending on the pronoun. 根据代词不同,这可以显示为“他们不想去。”或“她不想去。”。

Checking Expressions as Booleans

An expression that refers to the value of a stat, e.g. (health < 50), is equivalent to a Boolean variable -- it's true if health is below 50, and otherwise false. You can use multireplace on this kind of expression as if it's a Boolean, which can replace many cases of paired *if - *else statements. 一种引用属性值的表达式,例如(health < 50),等同于布尔变量——当健康值低于50时为真,否则为假。您可以对此类表达式使用多重替换,就像处理布尔值一样,这可以替代许多成对的*if-*else语句情况。

So instead of writing out this: 因此,无需写出这样的内容:

you could just have: 你可以直接这样写:

This can make code simpler, as it requires fewer *goto and *label statements than the equivalent *if/*else statement. It also allows you to use multireplace on variables that have a much wider range of values, e.g. a 1-100 fairmath percentage variable. But it only works in cases of flavor text -- if you want any variables to change, or the story to branch, you'll still need to use *if . And for a case where you want to include more than two options (e.g. displaying one text snippet if health < 50, a different one if it's 50-89, and yet another one if its >=90) then you still need *if/*elseif/*else. 这可以使代码更简洁,因为它比等效的if/else语句需要更少的goto和label语句。它还允许您对取值范围更广的变量使用多替换,例如一个1-100的公平数学百分比变量。但它仅适用于描述性文本的情况——如果您希望任何变量发生变化,或者故事出现分支,您仍然需要使用if。对于需要包含两个以上选项的情况(例如,如果生命值<50显示一段文本,50-89显示另一段,>=90时又显示另一段),您仍然需要if/*elseif/*else。

(Unless the ranges you want to check are evenly spaced, in which case you can use modulo to check the expression as a series of natural numbers -- see "Advanced Multireplace" below.) (除非您要检查的范围是均匀间隔的,在这种情况下,您可以使用模运算将表达式作为一系列自然数来检查——请参阅下面的“高级多替换”。)

It's worth noting that using multireplace to check expressions can make randomtest slightly less useful in catching certain bugs. In the example above, if at this point in an actual playthrough it's impossible for the MC's health to have fallen below 50, randomtest might help the author realize that, by showing that the line "Coughing, you stagger weakly toward the door." is reached 0 times in 10,000 random playthroughs. That won't happen when the (health < 50) conditional text is tucked inside an otherwise reachable line of code using multireplace. 值得注意的是,使用多重替换来检查表达式可能会略微降低随机测试在发现某些错误方面的效用。在上面的例子中,如果在实际游戏流程的这一点上,主角的生命值不可能降到50以下,随机测试或许能通过显示"你咳嗽着,虚弱地踉跄走向门口。"这句台词在10,000次随机游戏流程中被触发了0次,来帮助作者意识到这一点。但当(生命值 < 50)的条件文本被隐藏在通过多重替换可达的代码行内部时,这种情况就不会发生。

Advanced Multireplace

You can also use multireplace on any expression that evaluates to a discrete range of natural numbers (1, 2, 3, 4, etc.). We've already used a simple example above, to show how to parse a variable whose range starts at 0 and can go as high as 3: 你也可以对任何能得出离散自然数范围(1、2、3、4等)的表达式使用多重替换。上文已经展示了一个简单示例,演示如何解析一个取值范围从0开始、最高可达3的变量:

This same approach can be usedarrow-up-right for e.g. displaying a life counter on the stats screen for a game where both max_lives and lives can vary between 0 and 5: 同样的方法也可用于在状态屏幕上显示生命计数器,适用于最大生命值和当前生命值均在0到5之间变化的游戏场景:

In the example where lives = 1 and max_lives = 3, this code would display: Lives: ❤️🪦🪦 在示例中,当生命值 = 1 且最大生命值 = 3 时,此代码将显示:生命:❤️🪦🪦

Moduloarrow-up-right (the arithmetic operator that displays remainders) opens up even more possibilities for converting numeric variables into ranges that multireplace can handle. 模运算(显示余数的算术运算符)为将数值变量转换为多重替换可以处理的范围开辟了更多可能性。

Infinitely Cycling Text

Usually, multireplace can't parse numeric variables with an open-ended upper bound, e.g. where you might repeat the same action an unlimited number of times with a variable set +1 for each additional action. But an expression using modulo can break that down into a smaller range of numbers. Take the example of a repeatable action that you expect to repeatedly fail, and where you want some variation in the "failure text" seen by the player: 通常,多重替换无法解析具有开放式上限的数值变量,例如,您可能重复执行同一动作无限次,且每次额外执行时变量会 +1。但使用模运算的表达式可以将其分解为更小的数字范围。以一个可重复动作为例,您预期该动作会反复失败,并且希望玩家看到的“失败文本”有一些变化:

As long as the variable you're checking can only be a positive whole integer (never a negative number, a decimal, or irrational), "variable modulo 4" -- the remainder you get when you divide the variable by 4 -- will always be between 0 and 3. That's a range we know multireplace can work with. 只要您检查的变量只能是正整数(从不为负数、小数或无理数),"变量模4"——即该变量除以4所得的余数——将始终介于0到3之间。这是我们已知多重替换能够处理的范围。

So in the example code, until some other choice sets the variable "letter" true, after your first time looking in the drawer, you'll see "You don’t find anything." When you look a fourth time, "times_looked_in_drawer modulo 4" will become 0, and the _**multireplace**_ will display "Nothing in this drawer, just like the last 3 times you checked." It will then cycle through the other responses, until (if you look an eighth time), you cycle back to `"Nothing in this drawer, just like the last 7 times you checked," and so on. 因此在示例代码中,除非其他选项将变量"letter"设为真,否则第一次查看抽屉后,您会看到"你什么都没找到"。当您第四次查看时,"times_looked_in_drawer模4"将变为0,此时多重替换会显示"抽屉里空无一物,就像你前三次检查时一样"。随后它将循环显示其他回应,直到(如果您第八次查看)循环回到"抽屉里空无一物,就像你前七次检查时一样",依此类推。

Using a fairmath percentile in multireplace

Modulo can also be used to split a fairmath range (1-99, in theory) into manageable chunks for displaying flavor text. 模运算还可用于将公平数学范围(理论上为1-99)分割成便于显示特色文本的可管理区块。

In this example, dividing a fairmath variable by 20 splits it into five possible ranges (1-19, 20-39, 40-59, 60-79, and 80-99). If your rel_larry relationship score is e.g. at 37, then rel_larry modulo 20 is 17, the remainder when you divide 37 by 20. Feed those into the expression in the example code above, and the final result is 2, displaying that Larry is "not your biggest fan." 在这个例子中,将一个公平数学变量除以20会将其划分为五个可能的范围(1-19、20-39、40-59、60-79和80-99)。例如,如果你的rel_larry关系分数是37,那么rel_larry模20的结果是17,即37除以20的余数。将这些值代入上述示例代码的表达式中,最终结果是2,显示拉里"不是你最大的粉丝"。

This approach works when you want to split the variable into equal-sized ranges, with flavor text to suit. If however you wanted the range for Larry being one of your "closest companions" to start at 70 rather than 80 (since reaching 80 in fairmath can be quite the achievement), and "amiable companion" to run from 60-70, then *if/*elseif/*else may work better. 当你希望将变量划分为等大小的范围,并配以相应的描述文本时,这种方法很有效。然而,如果你希望拉里成为你"最亲密的伙伴"这一范围从70开始,而不是80(因为在公平数学中达到80分可能是一项相当了不起的成就),并且"友善的伙伴"范围设定在60-70之间,那么使用*if/*elseif/*else结构可能会更合适。

The "number cruncher" works well with arrays. Instead of writing a subroutine or having this long thing: "数字处理机"与数组配合良好。与其编写子程序或使用冗长的代码:

you can condense it down to 你可以将其简化为