Since everyone has read my summary of Twine's built-in macros, you'll notice that many other JavaScript operators can be used in the <<set>>, <<remember>>, <<if>>
and <<print>>
macros. Here is an incomplete list of a few that you may find useful. Many of these are not as "tidy" as the documented operators, and some of them are counterintuitive in their meaning. You can still use them, though, if you so wish.
No greater-than signs
Before we begin, a quick reminder: the greater-than sign can't be used inside a valid macro tag - the macro will not be recognised. That's why the "gt" and "gte" operators are provided to you instead.
Setting multiple variables in one <<set>>
You can set multiple variables in a <<set>> macro if you separate their equations with a semicolon:
<<set $green = "Rice" ; $gold = "Cheese" ; $ammo = 14; $score = 0 >>
Modifying a number variable: +=, -=, *=, /=
A commonly known shorthand for various arithmetic operations is available. Instead of writing <<set $hp = $hp - 1>>
you can just write
<<set $hp -= 1>>
This sets $hp to itself, minus 1. The other operators +=, *=, /=
also work for their respective arithmetic. These can only be used in situations where the = sign would normally be used.
The ternary operator ( ? : )
The ternary operator can be used as a condensed, specific version of <<if>> <<else>> <<endif>>
. Whenever a statement calls for a value, you could instead put ( [an if-macro condition] ? [a value if the condition is true] : [a value if the condition is false] ).
<<print ($ammo gt 4 ? "Ammo OK" : "Ammo low" )>>
The above line prints "Ammo OK" if $ammo is greater than 4, and "Ammo low" otherwise. If you want to make things much less readable and comprehensible, you can nest ternaries in each other:
<<print ($ammo gt 4 ? ($ammo gt 16 ? "Ammo oversupplied" : "Ammo OK") : "Ammo low" )>>
If $ammo exceeds 4, then the value of the inner ternary is printed. You can also rewrite it like this, which is slightly more readable:
<<print ($ammo gt 16? : "Ammo oversupplied" : ($ammo gt 4 ? "Ammo OK" : "Ammo low" )>>
Reducing strings and numbers to "true" and "false"
In JavaScript, using strings or numbers in place of "true", "false", or conditional statements such as "x > y" means that the computer will try and convert the strings or numbers into either "true" or "false". The rule for those two types of values is: the number 0 and a string with zero characters in it, "", are converted to "false" - everything else is converted to "true". (0 and "" are called "falsy" values because they're equivalent to "false" - other values are "truthy").
(If you're familiar with Game Maker, you of course know that Game Maker considers all negative numbers to be falsy as well - but not so in JavaScript!)
This is not really that useful, except for explaining the behaviour of later examples, but it does mean that you could shorten certain <<if>>
comparisons:
<<if $hp neq 0>>
can be shortened to
<<if $hp>>
...though it may be confusing and appear like a mistake if you do write it like that.
You can also use Not to shorten the opposite.
<<if $hp eq 0>>
can be "shortened" to
<<if not $hp>>
...even though it's basically the same length.
false is equivalent to 0, true is equivalent to 1
With regards to numbers, the process of true/false coercion also works symmetrically - the value false, when used in arithmetic, is converted to 0. <<set $red = 3 + false>>
is valid, but useless. Nevertheless, this understanding is necessary for the next part.
As a corollary, the raw value true, when used in arithmetic, is converted to 1. This means that <<set $dead = $dead + not $hp>>
will add 1 to $dead if $hp is 0.
And and Or
The way And and Or is implemented in JavaScript is interesting: "x and y" means "if x is false, the value of this statement is x (i.e false), otherwise the value of this statement is y". And, as discussed previously, since such statements are usually used for true/false comparisons, then y is reduced to its true/false value - as one expects.
However, what if you used it in a situation where a plain value was expected? Essentially, it can be used in a manner similar to the ternary operator:
<<set $liveammo = ($gun and $bullets)>>
This line, given the rule above, sets $liveammo to equal $bullets, but only if $gun is true - if not, it's set to false, i.e. 0 (or whatever falsy value $gun has). It may look counterintuitive, especially since it relies on the aforementioned understanding of true/false reduction, but it's equivalent to <<set $liveammo = ($gun ? 0 : $bullets)>>
Or is implemented in JavaScript this way: "x or y" means "if x is true, the value of this statement is x (i.e true), otherwise the value of this statement is y". This means you could use it like this:
<<set $samusenergy = ($energytank or $reservetank)>>
That line only sets $samusenergy to be $reservetank if the $energytank variable is 0. It's equivalent to <<set $samusenergy = ($energytank ? $energytank : $reservetank)>>
It can also be used, in a way, to specify a "default" value if the first value is 0 or an empty string:
<<set $name = ($name or prompt(“What’s your name?”))>>
This line of code will only prompt the player if $name isn't an empty string (i.e. a name was already given).
Show all the game's variables for debug purposes
All the story variables are accessible in the JavaScript object "state.history[0].variables".
This line of code will print the variables that have currently been set:
<<print JSON.stringify(state.history[0].variables)>>
This will print something like the following:
{"green":"Rice","gold":"Cheese","ammo":14,"score":0}
As you can see, it features pairs of values - the variable name, followed by a colon, followed by the value of the variable.
Alternative to <<display>>
Since <<display>>
doesn't interpret its parameter as code, you can't, for instance, specify a passage name inside a variable. Hopefully this will be fixed at some point, but until then, here's an alternative you can use right now:
<<print tale.get($variable).text>>
$variable can be any statement or valid series of operations:
<<print tale.get($hp eq 0 ? "DeathEnd" : $race + "Ending").text>>