Sponsored By

Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs.

In the third and final part of this series, I cover using conditional statements and demonstrate macros in Twine.

Game Developer, Staff

February 18, 2013

9 Min Read

[Before proceeding too far into this post, I recommend checking out Part 1 and Part 2 first. From beginning at the Start passage to changing styles using CSS, I have covered a large amount of material before getting to this part. I highly encourage you not to jump into this post without reading the other two or having a good familiarity with how Twine works.]

So far, passages have been the results of static text that was written and then read afterwards. Any presentation of dynamic results came from limited use of variables to display values after they were "set" previously. Everything appeared the same to both developer and reader.

However, Twine has the ability to understand conditional statements. These are commands that evaluate an expression and take action depending on if it is true or not. For the first case, the code immediately underneath it will be run, otherwise another section of code will be run instead or nothing else will happen. These statements allow passages to conditionally display certain text.

Note: Twine has its own special set of conditional operators.

JavaScript

Operator in Twine

Explanation

<

lt

less than

>

gt

greater than

=

eq

equal

!=

neq

not equal

<=

lte

less than or equal to

>=

gte

greater than or equal to

&&

and

logical and

||

or

logical or

!

not

negation

Like other commands and keywords, conditional statements are set off from normal text with two less-than and two greater-than signs. The block of code also starts with "if" and ends with an "endif". While it can have an "else" after the initial "if", it is not required.

Note: Conditional statements written within passages can only evaluate variables defined using the '$' token. It cannot access any value from a JavaScript-only passage defined using "var". All variables, therefore, must be "set" prior to being used within a conditional statement in Twine.

Because each passage is unique, looping them is often not possible without the use of special code. Twine expects for passages to connect to each other without the need to create circles or self-referential links within the map area. Therefore, in order to create the perception of using the same link title again, a special character, '|', can be used to describe a link as different from its title.

Variables, once they have been initially "set", can be accessed in passages that follow by invoking the command "set" again to change their values. This means, for example, in order to increase or decrease the value of a variable, its current value must be part of the operation to assign its new value.

While passages cannot loop, their contents can. Using the "display" command, the content of one passage can be displayed in another. Like all other commands, it too is set off from normal text using the standard set of less-than and greater-than signs.

Once a variable has an initial value, and all currently necessary changes have been made, it can be evaluated in a conditional statement. Depending on the current value, a decision is made as to which section of text and code is run next in order. This execution happens as the passage is rendered and only the result is shown to the reader.

In this extended example story, the variable $numShout is set to a value of one and, with each progressive link into the story, it is incremented by one each time. After this change, the content of the passage "Lassi" is displayed and, within that passage itself, the value of $numShout is evaluated to see if it is greater-than-or-equal to three. If it is, the link "Tell Lassi to get help|Get help" is shown, else the link "Shout" is shown.

Using the '|' character to change the appearance of links, the reader is presented with a series of links that all appear to have the character shouting or crying for help. After the reader has clicked through at least three of these, however, the link to "Get help" becomes an option. Inevitably, as the reader takes either initial choice during the "Start" passage, the outcome will be the same. After making noise across three passages, Timmy gets Lassi's attention and instructs him to acquire farm equipment and gear.




Caution: While macros can often be very helpful for adding JavaScript to stories, they come with some limitations. Traditional looping structures of "for", "while", and "do-while" cannot be used within them. While a "var" can be defined, Twine will not be able to access its value directly.

Additionally, the order of execution of passages using the "script" tag is not guaranteed. Macro definitions, along with any other JavaScript, is added during the run-time of the page itself. As Twine parses each passage, macros are added and the browser executes the JavaScript at that moment.

While there are ways to mitigate some of these problems, I have decided not to cover them because they require more advanced knowledge of JavaScript and could easily consume several pages of explanation. However, I will demonstrate how a macro works in practice and, at the bottom of this post, point you to some additional resources. Many others have devised clever ways to solve portions of these problems.

 

Like adding CSS, JavaScript can also be placed in passages. In order for Twine to avoid color-coding this text, the tag of "script" is used. This labels the passage as containing JavaScript and should not be considered normal text.

Twine comes with many macros already. These are the commands that have been in use to "display" a passage within another or "set" a value. However, more can be added by a developer. By creating an associative array structure with a specified handler function, Twine will add macros that are parsed correctly. During the loading of the page, these macros are added at run-time and can be accessed from any passage afterwards.

Note: If there is a problem with the code within a macro, Twine might throw an error or could ignore a definition completely without warning the developer beforehand. Care should be taken to verify that all code is written correctly as Twine will not check this for the developer.

Macros have two primary parts: its name and the handler function. In order to be referenced in other passages as a command, its name is used. However, it is the handler function itself that does the actual work. This is where the actual code of the macro belongs.

Note: Whatever code is placed within a macro is executed in the context of the passage that calls it. Any dependencies, such as a variable needed for a calculation existing, should be solved before invoking it.

The handler function itself can receive multiple parameters. These include the passage from which it was called, the name of the macro it is a part of, any values passed to it from its invocation, and the parser to be used. However, in practice, it is common to either use the first three or none at all.

Because a passage with the tag "script" denotes the use of JavaScript, code within it does not need to use Twine's special conditional operators. It can also reference any built-in JavaScript function. Other than looping and some other special cases, nearly any JavaScript can be used within a passage with a "script" tag.

Note: Because an individual passage only exists after the user has clicked on a link to it and it has been loaded by Twine, there is no simple way to access the contents of a passage using JavaScript. However, the Twee documentation covers several functions for adding elements or even more text to a passage from within a macro, such as "insertText()" which takes the two parameters of the passage and the text to add.

Within any passage, HTML code can be added using an opening and closing tag. This allows developers to include other items like images, specially styled selections of text, and even embed audio or video. However, it comes with the limitation that no links can be specified from within the tagged text.

Using JavaScript defined within a macro, the value of something like a textarea can be accessed through the global "document.getElementById('id-of-element')" JavaScript function or, using Twine's own alias of that, "$('id-of-element')". Like variables, to access whatever may have been typed into a textarea, it must first have a value and then be accessed afterwards.

By combining JavaScript, CSS, and the features of Twine, stories can become as much about the reader herself as the developer who created it. Through using macros and conditional statements, passages can branch and change according to different rules or even a random number. With even a small amount of code, stories can become highly dynamic.



Additional Resources:

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like