☀️🌙
<==> Click on ▶ on code samples in this documentation to preview the resulting Twine passage here!

Introduction

Report bugs and suggest features

If you think you've found a bug to report in Harlowe, want to make a feature suggestion, or wish to see what future features are already planned, simply visit the project's issues page.

I'd appreciate it if you could adhere to the following guidelines when reporting bugs or proposals:

The issues page has recently moved!

As of July 2020, Harlowe's issues page and hosting is now at foss.heptapod.net. Big thanks to Octobus SAS for providing this free open-source software hosting service, and Clever Cloud SAS for that service's hosting.

Welcome to the world of 3.2.0

Dear fellow author of hypertext fiction: this version of Harlowe is most unlike others before it - despite a commitment to backwards-compatibility letting it be numbered a "minor" release, its wealth of added features is truly incredible. Let me explain in brief how this came to be. In April 2020, as the COVID-19 pandemic death toll (and the even greater permanent disability toll) was steadily rising worldwide, I came to something of a quiet realisation... that it was very possible I would not live to the end of this year, and, furthermore, it was probable that many Harlowe users would not live to the end of this year. I thought about Harlowe, and about how so much functionality I'd always envisioned for it - custom macros, string patterns, fullscreen support - was still unimplemented, and came to a decision that I absolutely had to finally get all of it in, this year.

So, I worked diligently on Harlowe for 9 months. In retrospect, it feels like my life lost all meaning except for Harlowe. Honestly, it was perhaps less like 9 months of productivity and more like a 9-month-long panic attack. (This is what most "COVID-19 productivity" stories are actually like, deep down. Please, don't admire me. Love yourself.)

But at the end, Harlowe finally became, for the first time, a hypertext fiction coding language I could be proud of. Even now, there's still some extra features I wish I'd been able to fit in, but this version feels like the first version where Harlowe is "complete", as my original vision for the story format in 2014 had resembled.

And now you get to enjoy all of this, and more.

And, most importantly, a new toolbar has been implemented for the Twine 2 editor that allows common Harlowe code idioms to be quickly created.

For a complete list of changes and outlines of how to use the above features, consult the change log section.

What Harlowe does best

Harlowe is one of a small handful of story formats offered in Twine. Each has its own specific focus and audience. Harlowe is designed for the following kind of author and work.

No HTML, Javascript or CSS experience needed

At its core, Harlowe's language is designed to assist authors with no familiarity with HTML, Javascript or CSS. Rather than requiring knowledge of all three languages, Harlowe provides a single language that fulfills the basic needs of all three and whose parts seamlessly integrate.

Use layout syntax, such as columns and aligners, to provide structure and composition to your prose. Harlowe contains special values that represent one or more CSS styles bundled together for convenience. Attach them to single runs of prose to provide the equivalent of inline styles, or use variables or the (change:) or (enchant:) macros to globally affect certain labeled structures. Harlowe's coding language, inside macro calls, smooths over the pitfalls and discrepancies of Javascript - better type-checking and clearer syntax prevents obvious datatype bugs like 1 + "1", 1 == "1" or if (a = 1), more plentiful error messages replace silent failures and junk values like NaN or undefined, different data structures each use the same setting, getting and checking syntax, and the styling datatypes are easily handled alongside other data. Styles can be combined by simply adding them together, for instance, and computed based on the current game state.

Though, if you already have HTML, Javascript or CSS experience, and would prefer to leverage that experience as you create, you may wish to use SugarCube instead, which provides more direct access to the page's HTML elements and to the Javascript language.

Dynamic hypertext as a focus

I have a deep admiration for the storytelling potential of early and recent hypertext mediums, such as HyperCard, Shockwave, Flash, and early HTML fiction, and I have kept their versatility in mind when creating Harlowe. Harlowe heavily encourages you to think of a page as a dynamic interactive space, not just a sequence of prose followed by choices. Harlowe encourages you to place links and interactive elements in the midst of prose, not just at the end, and to use them to change the prose in surprising and unusual ways - inserting or removing text in a previously-read paragraph, changing the styling of words, changing just the link itself, and other such effects to reveal new meaning in the text and communicate your story in a manner unique to hypertext. If you would like to explore the storytelling potential of hypertext, it is my dear hope that you will find Harlowe satisfying.

Though, if you would prefer a more traditional, branching style of interactive prose writing, you may wish to use Chapbook or a non-Twine language like Ink instead.

Programming depth available when you need it

Don't let the preceding sections lead you to believe Harlowe is narrowly limited as a programming environment. Despite crafting Harlowe as a language apart from Javascript, I've nonetheless equipped it with tools and utilities to handle dense data manipulation. A wide collection of conversion macros and syntactic structures exist to convert and manipulate arrays, strings, and maps, including a lambda syntax, and the (macro:) macro lets you sculpt a personal sub-language within Harlowe. In addition, the Debug Mode gives you a live view of variables, styles, and game state as the story progresses. While these are not meant to be immediately useful to the first-time author, one who has grown more ambitious in their time with Harlowe may call upon them to make more computationally complicated stories, such as basic role-playing games. As you grow in programming confidence, Harlowe can follow you.

No specific simulation elements

Interactive fiction is commonly associated with text adventure games with a high degree of spatial simulation and procedurally generated text, where you control a player-character and manipulate objects and navigate rooms. Harlowe (and the other Twine story formats) is intended for a much wider variety of stories with a much lighter amount of interaction with the story's inner world, and as such it does not contain pre-built programming constructs for rooms, objects, inventories, manipulation verbs, and other common design affordances of text adventures. If you would prefer to write a story with a higher degree of simulation and interaction, you may wish to use a non-Twine language like Inform instead.

Three fundamentals of Harlowe

Harlowe is a markup language that you can apply to your Twine prose to make the prose interactive, non-linear, and game-like. This language's full contents of features may appear daunting, but all of its features revolve around the following three simple concepts, which, when understood, unlock understanding the rest of the language:

1. Macros produce either commands, or data values

A macro is the basic unit of code. It is either a command that changes the game's state or the prose in some way, such as the (set:) macro that saves data to a variable, or the (link-goto:) macro that puts a link inside the prose, or it produces a data value, such as a number or a styling instruction. All of Harlowe's macros are either one or the other.

(set: $companion to "Gallifrey") This is a command.
(print: $companion) This is a command.
(lowercase: "SCREECH") This is a value (a string).
(a: 3,4,5) This is a value (an array).
(for: each _num, 1,2,3) This is a value (a changer).

2. Attach values to hooks of text to change the text

To use macros to change your story's prose, you must place that prose between brackets, which make a hook. You can then attach one or more values to the front of the hook to change the prose. This syntactic structure is used for everything from text styling with (text-style:), to the narrative-branching of the (if:) macro, to more complex commands like (event:) or (for:).

(if: $ringStolen)[The ring, as expected, is gone.]

In the above example, an "if" statement is created by attaching the (if:) changer value to the square-bracketed hook.

3. You can use anything that produces a value as if it was that value

(set: $text to "VALID")
(set: $slide to (transition:"slide-right"))

(lowercase: "VALID") This is valid.
(lowercase: "VAL" + "ID") This is valid.
(lowercase: "VAL" + (uppercase: "id")) This is valid.
(lowercase: $text) This is valid.

(transition:"slide-right")[VALID] This is valid.
$slide[VALID] This is valid.

It is important, when you read example code in this documentation, not to assume that you're restricted to very specific ways of writing macros and expressions. Just because an example uses a variable, doesn't mean you can't instead use a plain value, or a macro that produces that value, in that exact same place. Variables and macros are interchangeable with the values they contain or produce – a variable containing a number can be used anywhere that a number could be used, as can a macro that produces a number. This means you have a wide range of expressiveness in writing your story's code - you can save values into variables simply to save having to write them in full repeatedly, and you can use data-choosing macros like (either:), (cond:), (nth:) and so forth nearly anywhere you want. Use whatever form is most suited to making your prose readable!

Passage markup

Hyperlinks are the player's means of moving between passages and affecting the story. They consist of link text, which the player clicks on, and a passage name to send the player to.

Inside matching non-nesting pairs of [[ and ]], place the link text and the passage name, separated by either -> or <-, with the arrow pointing to the passage name.

You can also write a shorthand form, where there is no <- or -> separator. The entire content is treated as a passage name, and its evaluation is treated as the link text.

Example usage:

[[Go to the cellar->Cellar]] is a link that goes to a passage named "Cellar".
[[Parachuting<-Jump]] is a link that goes to a passage named "Parachuting".
[[Down the hatch]] is a link that goes to a passage named "Down the hatch".

Details:

The interior of a link (the text between [[ and ]]) may contain any character except ]. If additional ->s or <-s appear, the rightmost right arrow or leftmost left arrow is regarded as the canonical separator.

If the passage name of a link does not exactly match that of an existing passage, but it does if you render the markup in or around it, then Harlowe will use that name. So, you can put markup inside the link, as well as variables or value macros like (either:).

However, you can't put commands or changers in the passage name. [[Really, now.->(print:$explain)]] will cause an error.

Links can be customised by attaching changer macros, like (transition-depart:) or (text-style:). Just place one in front of the link, like so: (t8n-depart:"dissolve")[[Recall that day]] - or attach a variable containing one: $memory[[Recall that day]]. You can also customise every link in the passage using (change:) or (enchant:), and ?Link.

This syntax is not the only way to create links – there are many link macros, such as (link:), which can be used to make more versatile hyperlinks in your story.

Style markup

It's expected that you'd want to apply styles to your text – to italicise a word in dialogue, for example. You can do this with simple formatting codes that are similar to the double brackets of a link. Here is what's available to you:

Styling Markup code Result HTML produced
Italics //text// text <i>text</i>
Boldface ''text'' text <b>text</b>
Strikethrough text ~~text~~ text <s>text</s>
Emphasis *text* text <em>text</em>
Strong emphasis **text** text <strong>text</strong>
Superscript meters/second^^2^^ meters/second2 meters/second<sup>2</sup>

Example usage:

You //can't// be serious! I have to go through the ''whole game''
again? ^^Jeez, louise!^^

Details:

You can nest these codes - ''//text//'' will produce bold italics - but they must nest symmetrically. ''//text''// will not work.

A larger variety of text styles can be produced by using the (text-style:) macro, attaching it to a text hook you'd like to style. And, furthermore, you can use HTML tags like <mark> as an additional styling option.

Macro markup

A macro is a piece of code that is inserted into passage text. Macros are used to accomplish many effects, such as altering the game's state, displaying different text depending on the game's state, and altering the manner in which text is displayed.

Built in macros:

There are many built-in macros in Harlowe. To use one, you must call upon it in your passage by writing the name, a colon, and some data values to provide it, all in parentheses. For instance, you call the (print:) macro like so: (print: 54). In this example, print is the macro's name, and 54 is the value.

The name of the macro is case-insensitive, dash-insensitive and underscore-insensitive. This means that almost any combination of case, dashes and underscores in the name will be ignored. You can, for instance, write (go-to:) as (goto:), (Goto:), (GOTO:), (GoTo:), (Go_To:), (Got--o:), (-_-_g-o-t-o:), or almost any other combination or variation. There is, however, ONE exception: the name cannot start with an underscore _, because that would make it a temp variable.

Custom macros:

In addition to built-in macros, it is also possible to write your own macros, using the (macro:) macro. You need to save these macros inside a variable or temp variable using the (set:) macro. Once you've done so, you can call it much like it was a built-in macro, except by replacing the name with the variable: ($someCustomMacro:) is how you would call a custom macro stored in the variable $someCustomMacro, and (_anotherCustomMacro:) is how you would call a custom macro stored in the temp variable _anotherCustomMacro. Note that you can't use dataname access to use macros that are inside arrays or datamaps: ($array's 1st:) is, unfortunately, not a valid custom macro call.

Passing data:

You can provide any type of data values to a macro call - numbers, strings, booleans, and so forth. These can be in any form, as well - "Red" + "belly" is an expression that produces a single string, "Redbelly", and can be used anywhere that the joined string can be used. Variables, too, can be used with macros, if their contents matches what the macro expects. So, if $var contains the string "Redbelly", then (print: $var), (print: "Redbelly") and (print: "Red" + "belly") are exactly the same.

Furthermore, each macro call produces a value itself - (num:), for instance, produces a number, (a:) an array - so they too can be nested inside other macro calls. (if: (num:"5") > 2) nests the (num:) macro inside the (if:) macro.

If a macro can or should be given multiple values, separate them with commas. You can give the (a:) macro three numbers like so: (a: 2, 3, 4). The final value may have a comma after it, or it may not - (a: 2, 3, 4,) is equally valid. Also, if you have a data value that's an array, string or dataset, you can "spread out" all of its values into the macro call by using the ... operator: (either: ...$array) will act as if every value in $array was placed in the (either:) macro call separately

Historical note:

You might notice that the majority of Harlowe macros are not, strictly speaking, macros in the computer-science sense, but are more like functions. This is purely due to historical circumstance - the original Twine 1 story format, Jonah, was based on TiddlyWiki's engine, which features parameterised transclusions called "macros". These are closer to computer-science macros in that they actually transclude markup directly into the tiddler (TiddlyWiki's term for "passage"). Thus, only Harlowe command macros like (display:) can really be considered "proper" macros.

Variable markup

As described in the documentation for the (set:) macro, variables are used to remember data values in your game, keep track of the player's status, and so forth. They start with $ (for normal variables) or _ (for temp variables, which only exist inside a single passage, hook or lambda).

Due to this syntax potentially conflicting with dollar values (such as $1.50) in your story text, variables cannot begin with a numeral.

You can print the contents of variables, or any further items within them, using the (print:) and (for:) macros. Or, if you only want to print a single variable, you can just enter the variable's name directly in your passage's prose.

(set: $plushieName to "Whispy", _heldItem to "briefcase")
Your beloved plushie, $plushieName, awaits you after a long work day.
You put your _heldItem down and lift it for a snuggle.

Furthermore, if the variable contains a changer command, such as that created by (text-style:) and such, then the variable can be attached to a hook to apply the changer to the hook:

(set: $robotText to (font:"Courier New"), _assistantText to (size:0.8))
$robotText[Good golly! Your flesh... it's so soft!]
_assistantText[Don't touch me, please! I'm ticklish.]

Note: While you can normally display the contents of variables by simply placing their names directly in passage prose, such as $ship or $crew, you have to use another macro, such as (print:), to display the contents of arrays, datamaps, or other structures, such as (print: $ship's mast) or (print: $crew's 1st).

Hook markup

A hook is a means of indicating that a specific span of passage prose is special in some way. It essentially consists of text between single [ and ] marks. Prose inside a hook can be modified, styled, controlled and analysed in a variety of ways using macros.

A hook by itself, such as [some text], is not very interesting. However, if you attach a macro or a variable to the front, the attached value is used to change the hook in some way, such as hiding it based on the game state, altering the styling of its text, moving its text to elsewhere in the passage.

(font: "Courier New")[This is a hook.

As you can see, this has a macro instance in front of it.]
This text is outside the hook.

The (font:) macro is one of several macros which produces a special styling changer, instead of a basic data type like a number or a string. In this case, the changer changes the attached hook's font to Courier New, without modifying the other text.

You can save this changer to a variable, and then use it repeatedly, like so.

(set: $x to (font: "Tahoma"))
$x[This text is in Tahoma.]
$x[As is this text.]

The basic (if:) macro is used by attaching it to a hook, too.

(if: $x is 2)[This text is only displayed if $x is 2.]

For more information about changer macros, consult the descriptions for each of them in turn.

Named hook markup

For a general introduction to hooks, see their respective markup description. Named hooks are a less common type of hook that offer unique benefits. To produce one, instead of attaching a macro, attach a "nametag" to the front or back:

[This hook is named 'opener']<opener|

|s2>[This hook is named 's2']

(Hook nametags are supposed to resemble triangular gift box nametags.)

A macro can refer to and alter the text content of a named hook by referring to the hook as if it were a variable. To do this, write the hook's name as if it were a variable, but use the ? symbol in place of the $ symbol:

[Fie and fuggaboo!]<shout|

(click: ?shout)[ (replace: ?shout)["Blast and damnation!"] ]

The above (click:) and (replace:) macros can remotely refer to and alter the hook using its name. This lets you, for instance, write a section of text full of tiny hooks, and then attach behaviour to them further in the passage:

Your [ballroom gown]<c1| is [bright red]<c2| with [silver streaks]<c3|,
and covered in [moonstones]<c4|.

[]<c5|
(click: ?c1)[(replace:?c5)[A hand-me-down from your great aunt.]]
(click: ?c2)[(replace:?c5)[A garish shade, to your reckoning.]]
(click: ?c3)[(replace:?c5)[Only their faint shine keeps them from being seen as grey.]]
(click: ?c4)[(replace:?c5)[Dreadfully heavy, they weigh you down and make dancing arduous.]]

As you can see, the top sentence remains mostly readable despite the fact that several words have (click:) behaviours assigned to them.

Built in names:

There are four special built-in hook names, ?Page, ?Passage, ?Sidebar, and ?Link, which, in addition to selecting named hooks, also affect parts of the page that you can't normally style with macros. They can be styled using the (change:) or (enchant:) macros.

(Note that, as mentioned above, if you use these names for your own hooks, such as by creating a named hook like |passage>[], then they will, of course, be included in the selections of these names.)

Hidden hook markup

Hidden hooks are an advanced kind of named hook that can be shown using macros like (show:). For a general introduction to named hooks, see their respective markup description.

There may be hooks whose contained prose you don't want to be visible as soon as the passage appears - a time delay, or the click of a link should be used to show them. You can set a hook to be hidden by altering the hook tag syntax - replace the > or < mark with a parenthesis.

|visible>[This hook is visible when the passage loads.]
|cloaked)[This hook is hidden when the passage loads, and needs a macro like `(show:?cloaked)` to reveal it.]

[My commanding officer - a war hero, and a charismatic face for the military.]<sight|
[Privately, I despise the man. His vacuous boosterism makes a mockery of my sacrifices.](thoughts|

(You can think of this as being visually similar to the pointed tails of comic speech balloons vs. round, enclosed thought balloons.)

In order to be useful, hidden hooks must have a name, which macros like (show:) can use to show them. Hence, there's no way to make a hidden unnamed hook - at least, without using a conditional macro like (if:).

Unclosed hook markup

This is a special version of the hook markup - an open bracket [, followed by any number of = marks, that has no matching closing bracket. When it is placed in a passage, it indicates that all the prose that follows, until the end of the hook that contains it or the end of the passage, is part of a single hook.

Its main purpose is to let you easily deploy hook changers that apply to the remaining text of the passage, without having to place and keep track of closing brackets at the end. For instance, the (click:) macro can be used with the ?page hook name to prompt the reader to click anywhere on the page to reveal the rest of the passage. The unclosed hook markup lets you use it as many times as you want, without needing to balance a number of closing brackets at the end of the passage.

(click: ?page)[==
This text won't appear until the page is clicked once.
(click: ?page)[==
This text won't appear until the page is clicked twice.
(click: ?page)[==
This text won't appear until the page is clicked three times.

Other changer macros, such as (link:), (more:), (event:), and (transition:), also work well with this markup.

Also, unclosed hooks can be named, and marked as hidden, just like other hooks.

|1>[=
The rest of this passage is in a hook named "1".
|2)[=
This part is also in a hidden hook named "2".

HTML markup

If you are familiar with them, HTML tags (like <img>) and HTML escapes (like &sect;) can be inserted straight into your passage text. They are treated very naively - they essentially pass through Harlowe's markup-to-HTML conversion process untouched.

Example usage:

<mark>This is marked text.

&para; So is this.

And this.</mark>

Details:

HTML elements included in this manner are given a data-raw attribute by Harlowe, to distinguish them from elements created via markup.

You can include a <script> tag in your passage to run Javascript code. The code will run as soon as the containing passage code is rendered.

You can also include a <style> tag containing CSS code. The CSS should affect the entire page until the element is removed from the DOM. You could use this in a "header" tagged passage, inside an (if:) hook, to make the CSS apply to every passage where the (if:) condition is fulfilled.

Finally, you can also include HTML comments <!-- Comment --> in your code, if you wish to leave reminder messages or explanations about the passage's code to yourself.

Verbatim markup

As plenty of symbols have special uses in Harlowe, you may wonder how you can use them normally, as mere symbols, without invoking their special functionality. You can do this by placing them between a pair of ` marks.

If you want to escape a section of text which already contains single ` marks, simply increase the number of ` marks used to enclose them.

Example usage:

There's no hard limit to the amount of graves you can use to enclose the text.

If you want to make an entire hook to be displayed verbatim, without its markup being rendered, you can attach the (verbatim:) changer.

Bulleted list markup

You can create bullet-point lists in your text by beginning lines with an asterisk *, followed by whitespace, followed by the list item text. The asterisk will be replaced with an indented bullet-point. Consecutive lines of bullet-point items will be joined into a single list, with appropriate vertical spacing.

Remember that there must be whitespace between the asterisk and the list item text! Otherwise, this markup will conflict with the emphasis markup.

If you use multiple asterisks (**, *** etc.) for the bullet, you will make a nested list, which is indented deeper than a normal list. Use nested lists for "children" of normal list items.

Example usage:

 * Bulleted item
    *    Bulleted item 2
  ** Indented bulleted item

Numbered list markup

You can create numbered lists in your text, which are similar to bulleted lists, but feature numbers in place of bullets. Simply begin single lines with 0., followed by whitespace, followed by the list item text. Consecutive items will be joined into a single list, with appropriate vertical spacing. Each of the 0.s will be replaced with a number corresponding to the item's position in the list.

Remember that there must be whitespace between the 0. and the list item text! Otherwise, it will be regarded as a plain number.

If you use multiple 0. tokens (0.0., 0.0.0. etc.) for the bullet, you will make a nested list, which uses different numbering from outer lists, and are indented deeper. Use nested lists for "children" of normal list items.

Example usage:

0. Numbered item
   0. Numbered item 2
 0.0. Indented numbered item

Aligner markup

An aligner is a special single-line token which specifies the alignment of the subsequent text. It is essentially 'modal' - all text from the token onward (until another aligner is encountered) is wrapped in a <tw-align> element (or unwrapped in the case of left-alignment, as that is the default).

Any amount of whitespace is permitted before or after each token, as long as it is on a single line.

Example usage:

==>
This is right-aligned
  =><=
This is centered
 <==>
This is justified
<==
This is left-aligned (undoes the above)
===><=
This has margins 3/4 left, 1/4 right
  =><=====
This has margins 1/6 left, 5/6 right.

(Try expanding this code preview using the bar on the left.)

You may apply alignment to specific hooks in your passages by attaching the (align:) macro to them.

Column markup

Column markup is, like aligner markup, a special single-line token which indicates that the subsequent text should be laid out in columns. They consist of a number of | marks, indicating the size of the column relative to the other columns - the total width of all columns equals the page width, and this is divided among the columns by their | marks. They also have a number of = marks surrounding it, indicating the size of the column's margins in CSS "em" units (which are about the width of a capital M).

All text from the token onward, until the next token is encountered, is contained in the specified column. A |==| token ends the set of columns and returns the page to normal.

Columns are laid out from left to right, in order of appearance.

Any amount of whitespace is permitted before or after each token, as long as it is on a single line.

Example usage:

(change:?passage, (text-size:0.6))
|==
This is in the leftmost column, which has a right margin of about 2 letters wide.
    =|||=
This is in the next column, which has margins of 1 letter wide. It is three times as wide as the left column.
 =====||
This is in the right column, which has a right margin of about 5 letters wide. It is twice as wide as the left column.
  |==|
This text is not in columns, but takes up the entire width, as usual.

(Try expanding this code preview using the bar on the left.)

You can create nested columns by enclosing the inner set of columns in an unnamed hook, like so.

(change:?passage, (text-size:0.6))
|==
This is the outer left column.
==|
This is the outer right column.
[\
  |==
This is the inner left column, inside the outer right column.
  ==|
This is the inner right column, inside the outer right column.
\]

Heading markup

Heading markup is used to create large headings, such as in structured prose or title splash passages. It is almost the same as the Markdown heading syntax: it starts on a fresh line, has one to six consecutive #s, and ends at the line break.

Example usage:

#Level 1 heading renders as an enclosing `<h1>`
   ###Level 3 heading renders as an enclosing `<h3>`
 ######Level 6 heading renders as an enclosing `<h6>`

As you can see, unlike in Markdown, opening whitespace is permitted before the first #.

Horizontal rule markup

A hr (horizontal rule) is a thin horizontal line across the entire passage. In HTML, it is a <hr> element. In Harlowe, it is an entire line consisting of 3 or more consecutive hyphens -.

Example usage:

        ---
  ----
     -----

Again, opening whitespace is permitted prior to the first - and after the final -.

Whitespace markup

"Whitespace" is a term that refers to "space" characters that you use to separate programming code tokens, such as the spacebar space, and the tab character. They are considered interchangeable in type and quantity - using two spaces usually has the same effect as using one space, one tab, and so forth.

Harlowe tries to also recognise most forms of Unicode-defined whitespace, including the quads, the per-em and per-en spaces, but not the zero-width space characters (as they may cause confusion and syntax errors if unnoticed in your code).

Collapsing whitespace markup

When working with macros, HTML tags and such, it's convenient for readability purposes to space and indent the text. However, this whitespace will also appear in the compiled passage text. You can get around this by placing the text between { and } marks. Inside, all runs of consecutive whitespace (line breaks, spaces) will be reduced to just one space.

Example usage:

{
    This sentence
    will be
    (set: $event to true)
    written on one line
    with only single spaces.
}

Details:

If you wish to still have line breaks within the markup that won't be collapsed, you can use HTML <br> tags (see the HTML markup section for more information about raw HTML tags).

You can nest this markup within itself - {Good { gumballs!}} - but the inner pair won't behave any differently as a result of being nested.

Text inside macro calls (in particular, text inside strings provided to macro) will not be collapsed. Neither will text outputted by macro calls, either - {(print:" ")} will still print all 3 spaces, and {(display:"Attic")} will still display all of the whitespace in the "Attic" passage.

Also, text inside the verbatim syntax, such as Thunder` `hound, will not be collapsed either.

If the markup contains a (replace:) command attached to a hook, the hook will still have its whitespace collapsed, even if it is commanded to replace text outside of the markup.

You may apply this collapsing effect to specific hooks using the (collapse:) macro. In particular, if you wish for the entire passage's whitespace to collapse, consider using (change: ?passage) and (collapse:).

If you only want to remove specific line breaks, consider the escaped line break markup.

Unclosed collapsing whitespace markup

This is a special version of the collapsing whitespace markup - an open curly brace {, followed by any number of = marks, that has no matching closing brace. When it is placed in a passage, it indicates that all the prose that follows, until the end of the hook that contains it or the end of the passage, should have its whitespace collapsed.

As with the the unclosed hook markup, this has advantages in situations where keeping track of closing brackets would be slightly inconvenient. If you use revision macros or enchantment macros like (change:), (replace:), (click:) and so forth, you can place those at the end of your passage, and use a single {= to separate them from the rest of the passage. Additionally, you can place a {= at the start of your passage to cause the entire passage's whitespace to be collapsed, allowing you to write additional prose without needing to have a closing brace after all of your additions.

This part of the passage
has normal whitespace.
{=
This part of the passage
has collapsed
whitespace.

All of the details pertaining to the collapsing markup apply here - consult its article for more information.

Escaped line break markup

Sometimes, you may want to write an especially long line, potentially containing many macros. This may not be particularly readable in the passage editor, though. One piece of markup that may help you is the \ mark - placing it just before a line break, or just after it, will cause the line break to be removed from the passage, thus "joining together" the lines.

Example usage:

This line \
and this line
\ and this line, are actually just one line.

Details:

There must not be any whitespace between the \ and the line break. Otherwise, it won't work.

Like most passage text markup, this cannot be used inside a macro call (for instance, (print: \
3)) - but since line breaks between values in macro calls are ignored, this doesn't matter.

List of macros

The (set: ) macro

(set: ...VariableToValue) Instant

Stores data values in variables, optionally allowing you to permanently restrict the variable to a single datatype.

Example usage:

Rationale:

Variables are data storage for your game. You can store data values under special names of your choosing, and refer to them later.

There are two kinds of variables. Normal variables, whose names begin with $, persist between passages, and should be used to store data that will be needed throughout the entire game. Temp variables, whose names begin with _, only exist inside the hook or passage that they're first (set:), and are forgotten after the hook or passage ends. You should use temp variables if you're writing passage code that mustn't accidentally affect any other passages' variables (by using (set:) on a variable name that someone else was using for something different). This can be essential in collaborative work with other authors working on the same story independently, or when writing code to be used in multiple stories.

The following example demonstrates where temp variables are visible.

(set: _a to 1) <- This is usable everywhere in this passage.
[
    (set: _b to 1) <-- This is only usable inside this hook.
    (set: _a to it + 1) <-- This changes the outer _a variable.
    [
        (print: _a + _b) <-- You can refer to both _a or _b in this hook.
    ]
]
(print: _b) <-- This will cause an error.

Variables have many purposes: keeping track of what the player has accomplished, managing some other state of the story, storing hook styles and changers, and other such things. You can display variables by putting them in passage text, attach them to hooks, and create and change them using the (set:) and (put:) macros.

Details:

In its basic form, a variable is created or changed using (set: variable to value ). You can also set multiple variables in a single (set:) by separating each VariableToValue with commas: (set: $weapon to 'hands', $armour to 'naked'), etc.

You can also use it in expressions on the right-side of to. Much as in other expressions, it's a shorthand for what's on the left side: (set: $vases to it + 1) is a shorthand for (set: $vases to $vases + 1).

Due to the variable syntax potentially conflicting with dollar values (such as $1.50) in your story text, variables cannot begin with a numeral.

Typed variables:

A common source of errors in a story is when a variable holding one type of data is mistakenly overridden with a different type of data, such as when putting "1" (the string "1") into a variable that should hold numbers. A good way to guard against this is to make the variable a typed variable, which is permanently restricted to a single datatype. The first time you set data to the variable, write (set: num-type $days to 1) to permanently restrict $days to numbers. That way, if you accidentally put "1" into it, an error will appear immediately, explaining the issue. Moreover, typed variables serve a code documentation purpose: they help indicate and explain the purpose of a variable by showing what data is meant to be in it. You can use any datatype before the -type syntax - see the article about datatype data for more details.

In addition to just restricting a variable to a type, you may wish to specify that a variable should only hold one value for the entire story - a style changer, for instance, or a datamap holding fixed values for a procedural-generation algorithm. For these, you want to use the const (short for "constant") datatype. Using this, the variable is guaranteed to constantly hold that value for the entirety of the story (or, if it's a temp variable, the passage or hook).

See also:

(put:), (move:), (unpack:)

The (put: ) macro

(put: ...VariableToValue) Instant

A left-to-right version of (set:) that requires the word into rather than to.

Example usage:

Rationale:

This macro has an identical purpose to (set:) - it creates and changes variables. For a basic explanation, see the rationale for (set:).

Almost every programming language has a (set:) construct, and most of these place the variable on the left-hand-side. However, a minority, such as HyperTalk, place the variable on the right. Harlowe allows both to be used, depending on personal preference. (set:) reads as (set: variable to value ), and (put:) reads as (put: value into variable ).

Details:

Just as with (set:), a variable is changed using (put: value into variable ). You can also set multiple variables in a single (put:) by separating each VariableToValue with commas: (put: 2 into $batteries, 4 into $bottles), etc.

You can also use typed variables with (put:) - (put: 1 into num-type $days) permanently restricts $days to numbers. Consult the article about (set:) for more information about typed variables.

it can also be used with (put:), but, interestingly, it's used on the right-hand side of the expression: (put: $eggs + 2 into it).

See also:

(set:), (move:), (unpack:)

The (move: ) macro

(move: ...VariableToValue) Instant

A variant of (put:) that, if transferring data from a data structure, deletes the source value after copying it - in effect moving the value from the source to the destination.

Example usage:

Rationale:

You'll often use data structures such as arrays or datamaps as storage for values that you'll only use once, such as a list of names to print out. When it comes time to use them, you can remove it from the structure and retrieve it in one go.

Details:

You must use the into keyword, like (put:), with this macro. This is because, like (put:), the destination of the value is on the right, whereas the source is on the left.

As with (set:) and (put:), you can also change multiple variables in a single (move:) by separating each VariableToValue with commas: (move: $a's 1st into $b, $a's 2nd into $c), etc. Also, unpacking syntax (described in detail in (unpack:)'s article) can be used with (move:) as well - (move: $array into (a: $x, $y)) will cause only the first and second values of $array to be moved into $x and $y.

If the data value you're accessing cannot be removed - for instance, if it's an array's length - then an error will be produced.

This macro works very well with the random data value of arrays: (move: $deck's random into $card) will remove a random value from $deck and put it into $card. Thus, you can use arrays as random "decks" of values that you can draw from and use once in your story.

Note that this will only delete the data from the source if the source is inside a data structure. Moving from variable to variable, such as by (move:$p into $q), won't cause $p to be deleted.

Just as with (set:) or (put:), typed variables can also be used with the destination variable of (move:). Writing (move: $enemies's 1st into dm-type $currentEnemy) will move a datamap from $enemies's 1st and put it into $currentEnemy, while also restricting $currentEnemy to datamap data for the rest of the story. Note that if $enemies's 1st is not, in fact, a datamap, an error will result.

See also:

(put:), (set:)

The (print: ) macro

(print: Any) Command

This command prints out any data provided to it, as text.

Example usage:

(print: $var + "s")

Details:

It is capable of printing things which (str:) cannot convert to a string, such as changers - but these will usually become bare descriptive text like [A (font: ) command]. You may find this useful for debugging purposes.

This command can be stored in a variable instead of being performed immediately. Notably, the expression to print is stored inside the command, instead of being re-evaluated when it is finally performed. So, a passage that contains:

(set: $name to "Dracula")
(set: $p to (print: "Count " + $name))
(set: $name to "Alucard")
$p

will still result in the text Count Dracula. This is not particularly useful compared to just setting $p to a string, but is available nonetheless.

Note that, once stored in a variable, a (print:) command is not a string. So, you can't provide it to (upperfirst:) and other such macros. (upperfirst: (print: $name)) will produce an error. However, if $name contains a string, you can provide it to (upperfirst:) before giving it to (print:), such as (print: (upperfirst: $name)).

If you need this command to print strings without the markup in the string being rendered, you may use the (verbatim:) changer to change the command, or use the (verbatim-print:) variant instead.

See also:

(str:), (display:), (verbatim-print:)

The (display: ) macro

(display: String) Command

This command writes out the contents of the passage with the given string name. If a passage of that name does not exist, this produces an error.

Example usage:

(display: "Cellar") prints the contents of the passage named "Cellar".

Rationale:

Suppose you have a section of code or source that you need to include in several different passages. It could be a status display, or a few lines of descriptive text. Instead of manually copy-pasting it into each passage, consider placing it all by itself in another passage, and using (display:) to place it in every passage. This gives you a lot of flexibility: you can, for instance, change the code throughout the story by just editing the displayed passage.

Details:

Text-targeting macros (such as (replace:)) inside the displayed passage will affect the text and hooks in the outer passage that occur earlier than the (display:) command. For instance, if passage A contains (replace:"Prince")[Frog], then another passage containing Princes(display:'A') will result in the text Frogs.

Like all commands, this can be set into a variable. It's not particularly useful in that state, but you can use that variable in place of that command, such as writing $var in place of (display: "Yggdrasil").

The (if: ) macro

(if: Boolean) Changer

This macro accepts only booleans, and produces a changer that can be attached to hooks to hide them "if" the value was false.

Example usage:

(if: $legs is 8)[You're a spider!] will show the You're a spider! hook if $legs is 8. Otherwise, it is not run.

Rationale:

In a story with multiple paths or threads, where certain events could occur or not occur, it's common to want to run a slightly modified version of a passage reflecting the current state of the world. The (if:), (unless:), (else-if:) and (else:) macros let these modifications be switched on or off depending on variables, comparisons or calculations of your choosing.

Details:

Note that the (if:) macro only runs once, when the passage or hook containing it is rendered. Any future change to the condition (such as a (link:) containing a (set:) that changes a variable) won't cause it to "re-run", and show/hide the hook anew.

However, if you attach (if:) to a named hook, and the (if:) hides the hook, you can manually reveal the hook later in the passage (such as, after a (link:) has been clicked) by using the (show:) macro to target the hook. Named hooks hidden with (if:) are thus equivalent to hidden named hooks like |this)[].

Alternatives:

The (if:) and (hidden:) macros are not the only attachment that can hide or show hooks! In fact, a variable that contains a boolean can be used in its place. For example:

(set: $foundWand to true, $foundHat to true, $foundBeard to true)
(set: $isAWizard to $foundWand and $foundHat and $foundBeard)

$isAWizard[You wring out your beard with a quick twisting spell.]
You step into the ruined library.
$isAWizard[The familiar scent of stale parchment comforts you.]

By storing a boolean inside $isAWizard, it can be used repeatedly throughout the story to hide or show hooks as you please.

if you want to conditionally display very short strings, or small values inside a macro call, you may want to use the shorter (cond:) macro instead.

See also:

(unless:), (else-if:), (else:), (cond:), (show:)

The (unless: ) macro

(unless: Boolean) Changer

This macro is the negated form of (if:): it accepts only booleans, and returns a changer that can be attached hooks to hide them "if" the value was true.

For more information, see the documentation of (if:).

Example usage:

(set: $form to "human")
(unless: $form is "duck")[The cold autumn rain chills your skin.]

The (else-if: ) macro

(else-if: Boolean) Changer

This macro's result changes depending on whether the previous hook in the passage was shown or hidden. If the previous hook was shown, then this changer hides the attached hook. Otherwise, it acts like (if:), showing the attached hook if it's true, and hiding it if it's false. If there was no preceding hook before this, then an error message will be printed.

Example usage:

Your stomach makes {
(if: $size is 'giant')[
    an intimidating rumble! You'll have to eat plenty of trees.
](else-if: $size is 'big')[
    a loud growl. You're hungry for some shrubs.
](else:)[
    a faint gurgle. You hope to scavenge some leaves.
]}

Rationale:

If you use the (if:) macro, you may find you commonly use it in forked branches of source: places where only one of a set of hooks should be displayed. In order to make this so, you would have to phrase your (if:) expressions as "if A happened", "if A didn't happen and B happened", "if A and B didn't happen and C happened", and so forth, in that order.

The (else-if:) and (else:) macros are convenient variants of (if:) designed to make this easier: you can merely say "if A happened", "else, if B happened", "else, if C happened" in your code.

Details:

Just like the (if:) macro, (else-if:) only checks its condition once, when the passage or hook contaning it is rendered.

The (else-if:) and (else:) macros do not need to only be paired with (if:)! You can use (else-if:) and (else:) in conjunction with boolean variables, like so:

(set:$married to false, $date to false)
$married[You hope this warrior will someday find the sort of love you know.]
(else-if: not $date)[You hope this warrior isn't doing anything this Sunday (because \
you've got overtime on Saturday.)]

If you attach (else-if:) to a named hook, and the (else-if:) hides the hook, you can reveal the hook later in the passage by using the (show:) macro to target the hook.

if you want to conditionally display very short strings, or small values inside a macro call, you may want to use the shorter (cond:) macro instead.

See also:

(if:), (unless:), (else:), (cond:), (show:)

The (else: ) macro

(else: ) Changer

This is a convenient limited variant of the (else-if:) macro. It will simply show the attached hook if the preceding hook was hidden, and hide it otherwise. If there was no preceding hook before this, then an error message will be printed.

Example usage:

The coins fall... 
\(if: (either:false, false, false, true))
    [and both land on tails! That means you've won the bet!]
\(else: )
    [and one of them lands heads-up.]

Rationale:

After you've written a series of hooks guarded by (if:) and (else-if:), you'll often have one final branch to show, when none of the above have been shown. (else:) is the "none of the above" variant of (else-if:), which needs no boolean expression to be provided. It's essentially the same as (else-if: true), but shorter and more readable.

For more information, see the documentation of (else-if:).

Notes:

Just like the (if:) macro, (else:) only checks its condition once, when the passage or hook contaning it is rendered.

Due to a mysterious quirk, it's possible to use multiple (else:) macro calls in succession:

(set: $isUtterlyEvil to (either:true,false))
$isUtterlyEvil[You suddenly grip their ankles and spread your warm smile into a searing smirk.]
(else:)[In silence, you gently, reverently rub their soles.]
(else:)[Before they can react, you unleash a typhoon of tickles!]
(else:)[They sigh contentedly, filling your pious heart with joy.]

This usage can result in a somewhat puzzling passage source structure, where each (else:) hook alternates between visible and hidden depending on the first such hook. So, it is best avoided.

If you attach (else:) to a named hook, and the (else:) hides the hook, you can reveal the hook later in the passage by using the (show:) macro to target the hook.

See also:

(if:), (unless:), (else-if:), (cond:), (show:)

The (for: ) macro

(for: Lambda, [...Any]) Changer

Also known as: (loop:)

When attached to a hook, this repeats the attached hook, setting a temporary variable to a different value on each repeat.

Example usage:

Rationale:

Suppose you're using arrays to store strings representing inventory items, or character datamaps, or other kinds of sequential game information - or even just built-in arrays like (history:) - and you want to print out a sentence or paragraph for each item. The (for:) macro can be used to print something "for each" item in an array easily - simply write a hook using a temp variable where each item should be printed or used, then give (for:) an "each" lambda that uses the same temp variable.

Details:

If no extra values are given after the lambda (for instance, by using ... with an empty array), then nothing will happen and the attached hook will not be printed at all.

Don't make the mistake of believing you can alter an array by trying to (set:) the temp variable in each loop - such as (for: each _a, ...$arr)[(set: _a to it + 1)]. This will NOT change $arr - only the temp variable will change (and only until the next loop, where another $arr value will be put into it). If you want to alter an array item-by-item, use the (altered:) macro.

The temp variable inside the hook will shadow any other identically-named temp variables outside of it: if you (set: _a to 1), then (for: each _a, 2,3)[ (print: _a) ], the inner hook will print "2" and "3", and you won't be able to print or set the "outer" _a.

You may want to simply print several copies of a hook a certain number of times, without any particular array data being looped over. You can use the (range:) macro with it instead: (for: each _i, ...(range:1,10)), and not use the temp variable inside the hook at all.

As it is a changer macro, (for:)'s value is a changer which can be stored in a variable - this command stores all of the values originally given to it, and won't reflect any changes to the values, or their container arrays, since then.

Alternatives:

You may be tempted to use (for:) not to print anything at all, but to find values inside arrays using (if:), or form a "total" using (set:). The lambda macros (find:) and (folded:), while slightly less straightforward, are recommended to be used instead.

See also:

(find:), (folded:), (if:)

The (either: ) macro

(either: ...Any) Any

Give this macro several values, separated by commas, and it will pick and return one of them randomly.

Example usage:

Rationale:

There are plenty of occasions where you might want random elements in your story: a few random adjectives or flavour text lines to give repeated play-throughs variety, for instance, or a few random links for a "maze" area. For these cases, you'll probably want to simply select from a few possibilities. The (either:) macro provides this functionality.

Details:

As with many macros, you can use the spread ... operator to place all of the values in an array or dataset into (either:), and pick them randomly. (either: ...$array), for instance, will choose one possibility from all of the array contents.

If you want to pick two or more values randomly, you may want to use the (shuffled:) macro, and extract a subarray from its result.

If you want to pick a value more reliably - for instance, to pick a value randomly, but keep using that same value in subsequent visits to the passage - you may want to store an (either:) result in a variable using (set:) in an earlier passage, and use that whenever you want to use the result.

See also:

(nth:), (random:), (shuffled:), (cond:)

The (cond: ) macro

(cond: Boolean, Any, ...Any) Any

When given a sequence of booleans (the "conditions") paired with values, this provides the first value that was paired with a true condition. This can give you one value or another based on a quick check.

Example usage:

Rationale:

While the (if:), (else:) and (else-if:) macros allow blocks of passage prose to be conditionally displayed and code to be conditionally run, there are plenty of situations where you'd prefer to succinctly select values inside macro calls, or select from multiple values, without needing to write multiple (else-if:)s or (set:)s for each possibility. The (cond:) macro (short for "condition") offers such utility.

In situations where you would write something like this,

{(if: not $lostTheSword)[
(set: $weapon to "a holy sword")
](else: )[
(set:$weapon to "an unholy swear-word")
]}

you could instead simply write this.

(set: $weapon to (cond: not $lostTheSword, "a holy sword", "an unholy swear-word"))

Details:

This macro is intended to resemble the "cond" function in Lisp, as well as the "ternary" operator in numerous other programming languages. It also might remind you of the values given to (dm:) - a piece of metadata, followed by its matching data - except that (dm:) ties names to data, whereas this ties conditions to data.

If only one value was given to (cond:), then that value will be returned as-is.

Except for the last, every odd value given to (cond:) must be a boolean, or an error will occur.

See also:

(if:), (dm:), (nth:)

The (nth: ) macro

(nth: Number, ...Any) Any

Given a positive whole number and a sequence of values, this selects the nth value in the sequence, where n is the number. If n is larger than the number of items in the sequence, the selection loops around to the start.

Example usage:

(nth: visit, "Hi!", "Hello again!", "Oh, it's you!", "Hey!") will display a different salutation, in sequence, on the first, second, third and fourth visits, then return to "Hi!" on the fifth visit, and so on.

Rationale:

This macro is designed to be used in passage prose, letting you quickly display one of a varying range of phrases or sentences based on a certain value. In addition to being useful with story variables, it's useful with the visit identifier, allowing you to vary the text shown on each subsequent visit to a passage, with more consistent variation than if you were using (either:).

However, you can use (nth:) with any kind of value, not just strings. For instance, (text-colour: (nth: $wounds, white, yellow, red)) will produce a (text-colour:) changer that differs in colour based on the number in $wounds (up to 3).

Details:

You can, of course, access a specific value in a sequence using the (a:) macro and the 's or of syntax - (a: 1,2,3)'s ($n) is functionally very similar to (nth: $n, 1, 2, 3), and other uses of the (nth:) macro. (nth:), however, allows the given value to exceed the bounds of the sequence - (nth: 4, 1, 2, 3) would produce 1, whereas (a: 1,2,3)'s 4th would produce an error.

If you wish to use (nth:) to display very large blocks of prose, you may wish to simply put that prose in hooks, and use (if:) to selectively display one, such as by writing (if: visits is 3).

If you don't want the "looping" to occur - if you want to only return the final value if the number exceeds the sequence - you can combine this macro with (min:). (nth: (min: 3, visit), "", "", "")

You may be tempted to combine this macro with (shuffled:), as in (nth: visit, ...(shuffled: "A", "B", "C")) - however, this will NOT behave any differently from just using (either:) - each visit, the (shuffled:) macro will shuffle the sequence in a different way, so you can't guarantee that different values will be shown.

See also:

(cond:), (if:), (either:)

The (verbatim: ) macro

(verbatim: ) Changer

Also known as: (v6m:)

When attached to a hook or command, the markup inside that would normally be rendered into HTML is instead presented as plain text, as if the verbatim markup was used on it.

Example usage:

Rationale:

Harlowe conveniently allows you to print strings containing markup and variables, such as "Your rank is ''$rank''", rendering them as if they were written directly in the passage. However, there are many situations where you would prefer not to do so, and where you can't conveniently wrap that content in the verbatim markup. Chief among these is player-inputted text: since players can write valid Harlowe markup into (prompt:) and (input-box:) elements, displaying such text could cause no end of disaster for your story. Additionally, since this text can also include unmatched verbatim markup, attempting to encase it in verbatim markup is non-trivially difficult. This macro provides an easier way to guarantee that the markup, if present, is not rendered.

In addition, you may want to write a hook without having to worry about the task of placing its contents inside verbatim markup, or write a hook containing textual references to HTML or Harlowe code. Even if it turns out to be unnecessary, having this macro on hand can be reassuring.

Details:

This macro takes no values - each changer value it produces is the same.

See also:

(collapse:), (verbatim-print:)

The (verbatim-print: ) macro

(verbatim-print: Any) Command

Also known as: (v6m-print:)

A convenient combination of (verbatim:) and (print:), this prints out any single argument given to it, as text, but without rendering the resulting text as markup.

Example usage:

Rationale:

In practice, this is functionally identical to a (verbatim:) changer attached to a (print:) command. However, one major difference is that this can be stored in a variable and used in passage prose by itself, without having to attach the changer each time. This scenario is especially useful when dealing with player-inputted text: rather than having to display it with two macros each time, you can simply save this command in a variable and use that variable.

Details:

As with (print:), once text is given to this command, there's no easy way to extract it from the command value without using (source:). So, you can't provide it to (upperfirst:) and other such macros. (upperfirst: (verbatim-print: $name)) will produce an error. Instead, convert the original string using (upperfirst:) before giving it to (verbatim-print:).

If you have a string you need to print frequently, and you don't want to call (verbatim-print:) every time you need to print it, you may wish to simply (set:) a (verbatim-print:) into a variable, like so: (set: $vbName to (verbatim-print:$name)). Then, you can put the command (set in that variable) into passage prose, and it will work as expected.

See also:

(verbatim:), (print:)

The (change: ) macro

(change: HookName or String, Changer or Lambda) Command

Applies a changer (or a "via" lambda producing a changer) to every occurrence of a hook or string in a passage, once.

Example usage:

Rationale:

While changers allow you to style or transform certain hooks in a passage, it can be tedious and error-prone to attach them to every occurrence as you're writing your story, especially if the attached changers are complicated. You can simplify this by storing changers in short variables, and attaching just the variables, like so:

(set: _ghost to (text-style:'outline'))
_ghost[Awoo]
_ghost[Ooooh]

Nevertheless, this can prove undesirable: you may want to not use the _ghost styling later in development, which would force you to remove the attached variables to avoid producing an error; you may want to only style a single word or phrase, and find it inconvenient to place it in a hook; you may simply not like dedicating variables to storing changers, or in placing (set:) macros at the start of your passage's prose.

Instead, you can give the hooks the name "ghost", and then (change:) them afterward like so:

|ghost>[Awoo]
|ghost>[Ooooh]
(change: ?ghost, (text-style:'outline'))

This has a few advantages. As it ties the changer styling to a hook name rather than a variable, the (change:) can be removed later without causing errors. Placing the (change:) at the end of the passage can also make the passage's source more readable, the textual content being closer to the top.

Details:

The (change:) macro can target plain text instead of hooks, much like (click:) - simply provide a string instead of a hook name. If a "via" lambda is supplied to (change:) instead of a changer, then that lambda is used to compute a changer dynamically, using the pos keyword to distinguish each hook that's enchanted. For instance, (change: "O", via (text-style:(cond: pos is an even, 'bold', 'none'))) changes only even-numbered instances of the letter "O".

Like the (replace:), (append:) and (prepend:) macros, this macro does not affect text and hooks that appear after it, as it is an immediate command that only affects what has already been rendered. For an alternative version of this macro which does affect hooks and text after it, see (enchant:).

The built-in hook names, ?Page, ?Passage, ?Sidebar and ?Link, as well as their data names like chars or lines, can be targeted by this macro, and can be styled on a per-passage basis this way.

Using (text-colour:) with this macro will let you change the colour of links inside the indicated hook, with one exception: using (change:) to change the entire passage (via ?passage or ?page) with (text-colour:) will NOT affect links. This is to allow you to re-style the entire story without having to lose the distinct colour of links compared to passage text. You can change the colour of all links using an explicit (change: ?link, (text-colour: $color)) or by using (link-style: (text-colour: $color))[= (that is, with unclosed hook markup).

You can use (change:) with (transition:) to add transitions to hooks or text elsewhere in the same passage – however, if the (change:) macro is run after the passage was initially rendered, the transitions will begin animating in the middle of their usual animations, or, if enough time has passed, won't run at all. For example, (event: when time > 2s)[(change:"Riddles", (t8n:"Shudder")+(t8n-time:3s))] will apply a 3-second transition to each instance of the word "Riddles" in the passage, but since 2 seconds have already passed since those words were rendered, only the last 1 second of the transition will be visible.

You cannot use (change:) with (link:), (replace:), or any of its relatives – because the enchanted hook or text is already in the passage, the link can't appear and it can't replace anything.

See also:

(enchant:), (enchant-in:), (replace:)

The (enchant: ) macro

(enchant: HookName or String, Changer or Lambda) Command

Applies a changer (or a "via" lambda producing a changer) to every occurrence of a hook or string in a passage, and continues applying that changer to any further occurrences that are made to appear in the same passage later.

Example usage:

Rationale:

This is a special version of (change:) which doesn't just perform a single transformation of a set of hooks or text - rather, like (click:), it creates an ongoing effect that constantly checks and reapplies the changers whenever new hooks or text are inserted into the passage, persisting until you navigate to another passage. Consider the following:

(enchant: ?ghost, (text-style:'outline'))
|ghost>[Awooo]
(link:">Wait.")[|ghost>[Oooowooo]]

If this were a (change:) command, the second hook revealed by the (link:) wouldn't be affected, as it is inserted into the passage after it's finished rendering. The (enchant:) macro allows you to guarantee that every hook or bit of text that you want the changer to affect is constantly affected.

Details:

The (enchant:) macro takes the same values as (change:) - a string can be given instead of a hook name, and a lambda can be given instead of a changer. See (change:)'s article for more details about these.

This macro works well in "header" or "footer" tagged passages - using a lot of (enchant:) commands to style certain words or parts of every passage, you can essentially write a "styling language" for your story, where certain hook names "mean" certain colours or behaviour. (This is loosely comparable to using CSS to style HTML class names, but exclusively uses macros.)

When targeting ?Page, ?Passage and ?Sidebar, there is generally no difference between using (enchant:) and using (change:), as there (usually) aren't any other hooks with those names in the passage.

Like (change:), you cannot use (enchant:) with (link:), (replace:), or any of its relatives.

See also:

(click:), (change:), (enchant-in:)

The (enchant-in: ) macro

(enchant-in: HookName or String, Changer or Lambda) Changer

A variation of (enchant:) and (change:), this applies a changer to every occurrence of a hook or string within just the attached hook, rather than the whole passage. As with (enchant:), the changer will be applied to every additional occurrence inserted into the attached hook.

Example usage:

(enchant:?frog, (text-style:"italic"))
"Opening remarks?"
|frog>["Crok, crok, crok."]
(enchant-in: ?frog, (text-colour:green))
["Your response?"
|frog>["Croak, croak."]
"A stunning rebuke!"
|frog>["Croooak."]]

Rationale:

While (change:) and (enchant:) both allow hooks to have changers or styles applied to them, these macros produce commands that must be placed in the passage, and which affect every match within the passage. It can sometimes be convenient to restrict the effect of (enchant:) to just matches within a single area of prose, especially when matching using strings, the ?Link hook name, or ?Page's chars. Thus, you can use (enchant-in:), attaching it to a hook that encloses the area you want it to affect. The enchantment it produces will be treated as though it didn't exist outside of the attached hook.

Details:

You can use built-in hook data names such as lines and chars with this macro, such as by (enchant-in: ?page's lines, $changer), which will style all of the lines in the attached hook with $changer. However, this construction appears counterintuitive when written out - the HookName selects all of the lines in the page, but only those within the attached hook are styled. So, more readable shorthand macros exist for both of these - (line-style:) and (char-style:) - which you ought to use instead.

This macro takes the same values as (enchant:) and (change:), and will produce the same errors for the same values. So, (link:), (replace:), or any of its relatives cannot be given as the second value, and neither can a lambda that doesn't produce a changer.

Note that this macro can only affect explicit hooks or string occurrences, and can't affect just "part" of a target. For instance, (enchant-in: ?page, (background:red))[DANGER] will NOT turn the background of the attached hook red, but (enchant-in: ?page's lines, (background:red))[DANGER] will (because the text "DANGER" is a line of text, and is thus targeted by ?page's lines).

This enchantment will be listed in the "Enchantments" tab of the Debug Mode panel when it's active, alongside enchantments created by (enchant:).

See also:

(enchant:), (change:), (link-style:)

The (hooks-named: ) macro

(hooks-named: String) HookName

When given a string, this creates a HookName from it. This can be used to dynamically create HookNames.

Example usage:

|oracle)["I scry with sticks, not bones."]|mage)["No teeth in the jawbones?"]|bodyguard)["Don't sift through rot."]

(set: $companionType to "bodyguard")
(link:"Investigate the bones")[(show:(hooks-named:$companionType))]

Rationale:

The standard syntax for referring to hooks, in macros such as (replace:), (change:) or (show:), is to write a HookName, such as ?door. That syntax, though, requires that you hard-code the name of the hook. This macro lets you construct a HookName from one or more existing strings or other variables, so that the exact hook referenced depends on the game state.

This macro is called (hooks-named:) to avoid confusion with (hook:), and also to convey that a HookName will refer to any number of hooks as long as they have the same name.

Details:

Note that the HookNames produced by this macro have the same functionality as other HookNames. In particular, you can specify the 1st hook, 2ndlast and so forth by writing, for instance, (hooks-named: "A")'s 2ndlast. Also note that the built-in HookNames can be constructed with this macro - (hooks-named:"passage") is the same as ?passage.

If an empty string is given, then this will cause an error.

See also:

(hook:)

The (border: ) macro

(border: String, [String], [String], [String]) Changer

Also known as: (b4r:)

A changer macro that applies a CSS border to the hook.

Example usage:

(b4r:"dotted")[I love you!
I want to be your wife!]

Details:

The border macros accept up to four values. These values refer to sides of a rectangle, going clockwise from the top: the first value is the top edge (12 o'clock), second is the right edge (3 o'clock), third is the bottom edge (6 o'clock), fourth is the left edge (9 o'clock). You can stop giving values anywhere. If an edge doesn't have a value, then it will use whatever the opposite edge's value is.

This macro affects the style of the border, and accepts the following border names.

String Example
"none" Example text
"solid" Example text
"dotted" Example text
"dashed" Example text
"double" Example text
"groove" Example text
"ridge" Example text
"inset" Example text
"outset" Example text

The "none" type can be used to remove a border that another changer may have included.

The default size of the border, with no other CSS changes to any elements, is 2px (2 pixels), unless a change is applied using (border-size:).

Due to browser CSS limitations, the border will force the hook to become a single rectangular area. The hook can no longer word-wrap, and moreover occupies every line in which its text is contained. So, this changer is best suited for entire paragraphs of text (or hooks using the (box:) changer) rather than single words or phrases.

See also:

(border-size:), (border-colour:), (corner-radius:)

The (border-colour: ) macro

(border-colour: String or Colour, [String or Colour], [String or Colour], [String or Colour]) Changer

Also known as: (b4r-colour:), (border-color:), (b4r-color:)

When applied to a hook being changed by the (border:) changer, this changes the border's colour.

Example usage:

Details:

The border macros accept up to four values. These values refer to sides of a rectangle, going clockwise from the top: the first value is the top edge (12 o'clock), second is the right edge (3 o'clock), third is the bottom edge (6 o'clock), fourth is the left edge (9 o'clock). You can stop giving values anywhere. If an edge doesn't have a value, then it will use whatever the opposite edge's value is (or the top value if it's the only one).

Much like (text-colour:), this accepts either a Colour (such as those produced by (hsl:) or (rgb:), or plain literals like #fff), or a CSS colour string.

Certain (border:) styles, namely "ridge", "groove", "inset" and "outset", will modify the colour, darkening it for certain parts of the border to produce their namesake appearance.

Selecting "transparent" as the colour will cause the border to "disappear", but also cause the space surrounding the hook to remain.

See also:

(background:), (text-colour:)

The (border-size: ) macro

(border-size: Number, [Number], [Number], [Number]) Changer

Also known as: (b4r-size:)

When applied to a hook being changed by the (border:) changer, this multiplies the size of the border by a given amount.

Example usage:

(b4r:"solid")+(b4r-size:4)[Do not read anything outside of this box.]

Details:

The border macros accept up to four values. These values refer to sides of a rectangle, going clockwise from the top: the first value is the top edge (12 o'clock), second is the right edge (3 o'clock), third is the bottom edge (6 o'clock), fourth is the left edge (9 o'clock). You can stop giving values anywhere. If an edge doesn't have a value, then it will use whatever the opposite edge's value is (or the top value if it's the only one).

The default size of borders added using (border:) is 2px (2 pixels). The number given is a number of CSS pixels to set the new size to. Since CSS pixels don't exactly correspond to display pixels (such as, for instance, if the browser window is zoomed in) then it's possible to have a non-whole number of CSS pixels (such as 1.5, which would, if the browser window was zoomed in to 200%, become 3 display pixels). Thus, this macro accepts numbers with fractional values. That being said, if a number lower than 0 is given, an error will be produced.

See also:

(border:), (corner-radius:), (text-size:)

The (corner-radius: ) macro

(corner-radius: Number, [Number], [Number], [Number]) Changer

When applied to a hook, this rounds the corners by the given number of pixels, causing the hook to become increasingly round or button-like.

Example usage:

(b4r:'solid')+(corner-radius:8)[Hasn't this gone on too long?]
(b4r:'solid')+(corner-radius:12)[Shouldn't you tell them the truth?]
(b4r:'solid')+(corner-radius:16)[//That you're not really who you say you are??//]

Details:

The border macros accept up to four values. These values refer to corners of a rectangle, going clockwise from the top: the first value is the top-left corner (10 o'clock), second is the top-right corner (2 o'clock), third is the bottom-right corner (4 o'clock), fourth is the bottom-left corner (8 o'clock). You can stop giving values anywhere. If a corner doesn't have a value, then it will use whatever the opposite corner's value is (or the top-left value if it's the only one).

Obviously, unless the hook has a (background:) or a (border:), the rounded corners will not be visible, and this changer will have no real effect.

If the hook has a (border:), values greater than the border's (border-width:) (which is 2 if it wasn't changed) will cause the interior of the element to become constrained by the curvature of the corners, as the rectangle's corners get cut off. Because of this, this macro also adds interior padding (distance between the border and the contained text) equal to each of the passed-in numbers, unless another changer (such as (css:)) provided a different padding value.

See also:

(border:), (background:), (border-size:)

The (hsl: ) macro

(hsl: Number, Number, Number, [Number]) Colour

Also known as: (hsla:)

This macro creates a colour using the given hue (h) angle in degrees, as well as the given saturation (s) and lightness (l) percentages, and, optionally, the transparency (alpha, or a) percentage, which is a fractional value between 0 (fully transparent) and 1 (fully visible).

Anything drawn with a partially transparent colour will itself be partially transparent. You can then layer such elements to produce a few interesting visual effects.

Example usage:

Rationale:

The HSL colour model is regarded as easier to work with than the RGB model used for HTML hexadecimal notation and the (rgb:) macro. Being able to set the hue with one number instead of three, for instance, lets you control the hue using a single variable, and alter it at will.

Details:

This macro takes the same range of numbers as the CSS hsla() function.

Giving saturation or lightness values higher than 1 or lower than 0 will cause an error. However, you can give any kind of hue number to (hsl:), and it will automatically round it to fit the 0-359 degree range - so, a value of 380 will become 20. This allows you to cycle through hues easily by providing a steadily increasing variable or a counter, such as (hsl: time / 100, 1, 0.5).

Giving alpha percentages higher than 1 or lower than 0 will cause an error.

See also:

(rgb:), (lch:), (gradient:)

The (rgb: ) macro

(rgb: Number, Number, Number, [Number]) Colour

Also known as: (rgba:)

This macro creates a colour using the three red (r), green (g) and blue (b) values provided, whose values are numbers between 0 and 255, and, optionally, the transparency (alpha, or a) percentage, which is a fractional value between 0 (fully transparent) and 1 (fully visible).

Anything drawn with a partially transparent colour will itself be partially transparent. You can then layer such elements to produce a few interesting visual effects.

Example usage:

Rationale:

The RGB additive colour model is commonly used for defining colours: the HTML hexadecimal notation for colours (such as #9263AA) simply consists of three hexadecimal values placed together. This macro allows you to create such colours computationally, by providing variables for certain components.

Details:

This macro takes the same range of numbers as the CSS rgb() function.

Giving values higher than 255 or lower than 0 will cause an error. Former versions of Harlowe did not allow fractional values to be given, but that restriction is no longer present.

Giving alpha percentages higher than 1 or lower than 0 will cause an error.

See also:

(hsl:), (lch:), (gradient:)

The (lch: ) macro

(lch: Number, Number, Number, [Number]) Colour

Also known as: (lcha:)

This macro creates a colour using three values in the CIELAB colour model - a lightness (l) percentage, a chroma (c) value, and a hue (h) angle in degrees, and, optionally, the transparency (alpha, or a) percentage, which is a fractional value between 0 (fully transparent) and 1 (fully visible).

Anything drawn with a partially transparent colour will itself be partially transparent. You can then layer such elements to produce a few interesting visual effects.

Example usage:

Rationale:

The CIELAB colour model is considered to be more universally useful than the RGB model and its HSL representation, whose treatment of "lightness" doesn't properly reflect the actual perceived luminosity of the colours in question. For instance, this colour (produced by (hsl:120,1,0.5)) and this colour (produced by (hsl:220,1,0.5)) have the same HSL lightness (0.5), but one appears to the human eye to be less bright than the other, due to one hue being less luminous than the other.

The lightness in LCH more closely corresponds to how the human eye perceives luminosity - (lch:0.9,80,120) produces , and (lch:0.9,80,220) produces , which, as you can see, is a pair closer in luminosity than the previous pair. Note that this means the lightness and hue values of LCH are not directly transferable between (hsl:) and this macro - they have different meanings in each. A hue angle in LCH is usually between 10 and 20 degrees less than its angle in HSL, varying by the LCH lightness.

Additionally, CIELAB's colour model replaces the "saturation" value of HSL with "chroma". Rather than being a single percentage from 0 to 1, LCH's chroma is a value whose upper bound varies with the colour's hue, reflecting how the human eye distinguishes some hues more accurately than others.

Details:

Despite all of the above, any colour produced by this macro will have to be internally converted back to HSL in order to be used, due to HTML and CSS not fully supporting LCH as of 2020. As such, colours produced by this macro are constrained by HSL's limits - as LCH accepts a greater variety of chroma and lightness combinations than what HSL can represent, the output colour will be automatically converted to the nearest valid HSL values, if necessary.

Giving lightness or alpha values less than 0 and greater than 1 will cause an error. Giving chroma values less than 0 and greater than 132 will cause an error. However, you can give any kind of hue number to (lch:), and it will automatically round it to fit the 0-359 degree range - so, a value of 380 will become 20. This allows you to cycle through hues easily by providing a steadily increasing variable or a counter, such as (lch: 0.9, 80, time / 100).

See also:

(hsl:), (rgb:), (gradient:), (complement:)

The (complement: ) macro

(complement: Colour) Colour

When given a colour, this provides a complement to that colour.

Example usage:

(complement:orange) produces .

Details:

This is a very simple macro - the returned colour is the same as the input colour, except that its LCH hue (as given to the (lch:) macro) has been rotated by 180 degrees, producing a colour with equivalent chroma and luminosity, but an opposite hue.

Note that, unlike (text-colour:), this will not take a string containing a CSS colour. This is because it operates purely on Harlowe colour data, and doesn't have a means of converting CSS colours into colour data.

See also:

(lch:)

The (palette: ) macro

(palette: String, Colour) Array

When given a string specifying a palette type, and a colour, this macro produces an array containing the given colour followed by three additional colours that together form a palette, for use with (text-colour:), (background:), and other macros.

Example usage:

{(set: _p to (palette: "mono", orange + black))
(enchant: ?page, (background: _p's 1st) + (text-colour: _p's 2nd))
(enchant: ?link, (colour: _p's 3rd) + (hover-style: (colour:_p's 4th)))}
This passage uses (link:"(more)")[a brown palette.]

Rationale:

Intended for game jams and rapid prototyping, (palette:) provides a quick and simple palette for stories and passages. When you aren't too fussed with making your story look significantly different from the Harlowe default, but just want a certain colour scheme to provide a certain mood, or colour a specific passage differently to offset it from the rest of the story, you can defer the task of choosing text or background colours to this macro. It will choose colours which contrast with the given colour to attempt to maximise readability, while still having an interesting relationship with the given colour's hue.

Details:

The following type strings are accepted.

String Explanation
"mono" The returned colours are tints and shades of the given colour.
"adjacent" The returned colours' hues are 30° to the left, 30° to the right, and 60° to the right of the given colour's hue.
"triad" The returned colours' hues are 140° to the left, 140° to the right, and 180° to the right of the given colour's hue.

This macro interprets the passed-in colour as a background colour, and the three colours it provides are intended as text colours - but you can easily use them for other purposes. The given colour could be used as a text colour, and any of the received colours could be used as different backgrounds.

The three returned colours all have a luminosity chosen to provide sufficient contrast with the given colour's luminosity. If the given colour's luminosity is very low or very high (near 0 or 1) then the returned colours will have a luminosity near the other extremity.

The (gradient: ) macro

(gradient: Number, ...Number, Colour) Gradient

When given a degree angle, followed by any number of number-colour pairs called "colour stops", this macro produces a gradient that fades between those colours in the direction of the angle.

Example usage:

(set: $desertChrome to (gradient: 0, 0, #e6a860, 0.49, black, 0.5, white, 1, blue))
(background: $desertChrome)+(text-color:white)[Sunshine Desert]

The above example produces Sunshine Desert

Rationale:

An easy way to add a subtle sense of depth, texture, direction or variety to elements in Harlowe, without having to create and import background images from outside of Twine, is to use this macro to generate a gradient, a dynamically-generated background which can be used with (background:).

A gradient consists of a series of flat colours. One of those colours will be used on one side of the element, one on the other, and the space in between will smoothly fade between them. You can supply additional colours that the gradient will smoothly fade to in between the start and end colours, too.

To specify where exactly these intermediate colour fades will occur on the element, the colours are paired with a percentage number - 0 being one side of the element, 1 being the other, 0.5 being exactly in-between. This pairing is called a "colour stop".

Consider this (gradient:) call, with six colour stops. (gradient:90, 0,#bf3f3f, 0.2,#a5bf3f, 0.4,#3fbf72, 0.6,#3f72bf, 0.8,#a53fbf, 1,#bf3f3f)

The six colour stops are 0,#bf3f3f , 0.2,#a5bf3f , 0.4,#3fbf72 , 0.6,#3f72bf , 0.8,#a53fbf , and 1,#bf3f3f . This corresponds to the following gradient, which for documentation purposes has its colour stops marked.

0,
#bf3f3f

0.2,
#a5bf3f

0.4,
#3fbf72

0.6,
#3f72bf

0.8,
#a53fbf

1,
#bf3f3f

(gradient:)'s first argument is a degree angle, which can be used to rotate the gradient's direction, changing where the first and last colours are placed in the element. Changing the degree angle in the above example from 90 degrees to 0 changes it from a horizontal gradient to a vertical gradient, using the same colours and stops:

Any angle can be given to (gradient:), including diagonal values like 40 or 66.

Details:

An error will be produced if you give colour-stop percentages that aren't between 0 and 1, or give less than 2 colour-stops. However, any number of degrees given to (gradient:), even below 0 or above 359, will automatically be rounded to fit the 0-359 degree range - so, a value of 380 will become 20.

You do not necessarily need to supply colour-stops at positions 0 and 1 - instead, the nearest colour-stop to those positions will be extended to the edge of the gradient. Furthermore, you don't need to supply colour-stops in ascending order - they will be reordered by Harlowe if they are not.

Gradients in Harlowe are implemented using CSS linear-gradients, and have the same limitations in output and browser support. Note, however, that the order of values for a colour stop is reversed from that of the CSS syntax (numbers go first, then colours). This is to help ensure that the initial degree number is not confused for a colour-stop percentage. Additionally, CSS linear-gradient "colour hints", which are used to adjust the midpoints between colour stops, are currently not supported by this macro.

See also:

(stripes:)

The (stripes: ) macro

(stripes: Number, Number, Colour, Colour) Gradient

When given a degree angle, a pixel distance, and two or more colours, this macro produces a gradient that draws a striped background, with each stripe as wide as the distance, and alternating through the given colours.

Example usage:

Rationale:

The (gradient:) macro can be used to dynamically create gradient backgrounds, which smoothly transition between multiple colours. By using certain pairs of colour stops that are very close together, however, you can create gradients where the colours transition sharply, producing stripes. Rather than use that macro, you can instead use this one to generate striped backgrounds succinctly, that repeat uniformly, and with easily-adjusted stripe width.

Details:

The degree angle matches that given to (gradient:). A number of 0 causes the stripes to be drawn horizontally, and increasing that number rotates the stripes counter-clockwise. Any number below 0 or above 359 will automatically be rounded to fit the 0-359 degree range - so, a value of 380 will become 20.

The distance value given is in pixels, and determines the width of a single stripe.

Gradients (including those produced by (stripes:)) in Harlowe are implemented using CSS repeating-linear-gradients, and have the same limitations in output and browser support.

(stripes:) gradients still have a "stops" array, accessible via $stripeGradient's stops, as with other gradients. Even though (stripes:) doesn't accept "colour stops", this array still contains colour stop datamaps, as if this gradient had been generated by (gradient:). There are two "stops" for each colour, and instead of a "percent" value, they have a "pixels" value. So, $stripeGradient's stops's 1st's colour will produce the colour of the first stripe, $stripeGradient's stops's 3rd's colour will produce the colour of the second stripe, and so forth. $stripeGradient's stops's 2nd's pixels will produce the pixel width of each stripe.

See also:

(gradient:)

The (macro: ) macro

(macro: [...TypedVar], CodeHook) CustomMacro

Use this macro to construct your own custom macros, which you can (set:) into variables and call as easily as a built-in macro.

Example usage:

The following custom macro creates a command that displays a randomly rotated hook in red, with the given opacity.

(set:$ghostlyLaughter to (macro: num-type _o, [
    (output: )+(text-rotate:(random:0,360))+(text-colour:(hsla:0, 1, 0.5, _o))[HE HE HE]
]))
($ghostlyLaughter:0.9) ($ghostlyLaughter:0.5) ($ghostlyLaughter:0.3)

The following custom macro creates a text string based on how many turns the player has taken. It takes no data.

(set: $fancyTimeName to (macro: [
    (set: _timeOfDay to (history: )'s length % 24 + 1)
    (output-data: (a:
        "midnight", "dreamshour", "wolfshour", "dark's end", "lightbreak", "afterdawn", "early rise", "awakening",
        "early warming", "joyshour", "first lunch", "shadow's end", "zenith", "shadow's birth", "second lunch", "hopeshour", "early cooling",
        "lightfade", "sundown", "dark's birth", "supper", "early rest", "slumbering", "catshour"
    )'s (_timeOfDay))
]))
It is now ($fancyTimeName:).

The following custom macro takes a datamap containing a character's attributes, and prints a line of text describing a character.

(set: $charSheet to (dm: "name", str, "HP", num, "poison", num, "heartbreak", bool))
(set: $healthSummary to (macro: $charSheet-type _stats, [
    This text inside the macro is not displayed during the game.
    (set: _TheyAre to _stats's name + " is ")
    Dead characters get a single, pithy line.
    (if: _stats's HP <= 0)[(output: _TheyAre + "deceased.")]
    Living characters get specific status conditions referred to.
    (output-data:
        _TheyAre + "in " + (cond: _stats's HP > 50, "fair", "poor") + " health." +
        (cond: _stats's poison > 0, " " + _TheyAre + "poisoned.", "") +
        (cond: _stats's heartbreak, " " + _TheyAre + "heartbroken.", "")
    )
]))
(set: $steelyStats to (dm: "name", "Steely", "HP", 80, "poison", 0, "heartbreak", true))
($healthSummary: $steelyStats)

Rationale:

This macro provides you with the means to expand Harlowe's collection of built-in macros with custom utilities tailored specifically for your story. While many Twine projects are simple hypertext stories, there are many that use it to make more complicated simulations, role-playing games, generative art, and so on. Being able to craft a personal language of macros in which to write the many algorithms and textual structures such games involve is essential to keeping your code succinct and readable.

Writing the parameters:

Custom macros consist of two structures: a set of data inputs (called parameters), and a body of code that creates the output.

Each parameter consists of a datatype or pattern of data, the "-type" suffix, and a temp variable, just like typed temp variables created with (set:). When you, the author, call the macro and give data at that parameter's position, it is put into the temp variable if it fits the datatype. A macro stored in $treasure with str-type _name, num-type price can be called by ($treasure: "Gold Watch", 155). The types are checked, and if they don't match (for instance, by incorrectly writing ($treasure: 155, "Gold Watch")), then an error will result. This ensures that incorrectly written custom macro calls are caught early, just like with built-in macros.

As with TypedVars used in other places, you can use a complex data structure as the "type" of the variable - (a: num, num)-type _coords specifies a parameter that requires an array of two numbers. If you wish to write a very general value-selection or data-structure macro, such as (a:) or (either:), that can take any kind of data value, you can write any-type for that parameter. However, using any-type is not recommended unless you genuinely need it, as you miss out on the ability to catch wrong-type errors.

You might, on occasion, want to make a macro that can take an arbitrary amount of values, similar to certain built-in macros like (a:), (altered:), and so forth. To do this, you can place the spread ... syntax in front of a parameter's datatype. Just as a spread datatype matches zero or more values when it is used with the matches operator, a spread parameter represents zero or more values of the same data type that you give to a macro call. Think of this as the opposite counterpart of the spread ... syntax in macro calls. Instead of turning one value (such as an array) into many spread-out values, this turns many values into a single array value. A custom macro stored in $mean with ...num-type _n can be called with ($mean:1,4,5,6), which sets _n to (a:1,4,5,6). ($mean:2,3) sets _n to (a:2,3), and ($mean:) sets _n to (a:). Note that because it takes every value at or after it, it must be the final parameter of your custom macro.

(set: $mean to (macro: ...num-type _a, [
    (output-data:(folded: _num making _total via _total + _num, ..._a) / _a's length)
]))
One's 7 foot 4, one's 4 foot 7. Add 'em up and divide by two, ya get a regular ($mean:7 + 4/12, 4 + 7/12)-foot person.

Writing the code:

The CodeHook, conversely, is where the code of your custom macro is written. You can (set:) temp variables in it, use (if:), (for:), (cond:), and so forth to run different sections of code, and finally output something using either (output:) or (output-data:). (Consult each of those macros' articles to learn the exact means of using them, and their differences.) The temp variables specified by the aforementioned typed variables are automatically set with the passed-in data.

Custom macros can be called like any other macro, by using the variable instead of a name: ($someCustomMacro:) is how you would call a custom macro stored in the variable $someCustomMacro, and (_anotherCustomMacro:) is how you would call a custom macro stored in the temp variable _anotherCustomMacro.

If you want to use a custom macro throughout your story, the best place to create it is in a "startup" tagged passage. This will aid in testing your story, as those passages' contents are always run first, regardless of the starting passage.

Normally, players can't see inside code hooks, but if an error occurs inside a custom macro, the error message will have an "Open" button allowing the code hook's interior to be viewed. You can take advantage of this by adding (print:) commands inside the code hook, showing you what certain variables contain at certain places in the hook.

Details:

You can, of course, have zero parameters, for a macro that needs no input values, and simply outputs a complicated (or randomised) value by itself.

Currently, (macro:) code hooks do NOT have access to temp variables created outside of the (macro:) call. (set: _name to "Fox", _aCustomMacro to (macro:[(output-data:_name)])) (_aCustomMacro:) will cause an error, because _name isn't accessible inside the _aCustomMacro macro. They do, however, have access to global variables (which begin with $).

Much like with typed variables given to (set:) or (put:), each temp variable associated with a parameter is restricted to the given data type. So, (macro:num-type _a,[(set:_a to 'text')(output-data:_a)] will cause an error when run.

All custom macros must return some value. If no (output:) or (output-data:) macros were run inside the code hook, an error will result.

See also:

(output:), (output-data:)

The (output: ) macro

(output: ) Changer

Also known as: (out:)

Use this macro inside a (macro:)'s CodeHook to output a command that, when run, renders the attached hook.

Example usage:

(set: $describePotion to (macro: dm-type _potion, [
    (size:0.7)+(box:"=XXXXX=")+(border:"solid")+(output:)[\
    ##(print:_potion's name)
    |==
    ''Hue'': (print:_potion's hue)
    ''Smell'': (print:_potion's smell)
    ''Flask'': (print:_potion's flask)
    ''Effect'': (print: _potion's effect)
    ==|
    //(print: _potion's desc)//
    ]
]))
($describePotion: (dm:
    "name", "Vasca's Dreambrew",
    "hue", "Puce",
    "smell", "Strong acidic honey",
    "flask", "Conical, green glass, corked",
    "effect", "The drinker will, upon sleeping, revisit the last dream they had, exactly as it was.",
    "desc", "Though Vasca was famed in life for her more practical potions, this brew is still sought after"
    + " by soothsayers and dream-scryers alike.",
))

Rationale:

For more information on custom macros, consult the (macro:) macro's article. All custom macros have inputs and output. This macro lets you output an entire hook, displaying it in a single call of the macro. Attach this to a hook at the end of your custom macro's code hook, and the custom macro will produce a command that displays the hook, similar to how (print:) or (link-goto:) work.

If you want your custom macro to return single values of data, like numbers or arrays, rather than hooks, please use the (output-data:) macro instead.

Details:

As soon as a hook with (output:) attached is encountered, all further macros and code in the CodeHook will be ignored, just as how (output:) behaves. This behaviour is unique among changers.

You can combine (output:) with other changers, like (text-style:) or (link:). The hook that is displayed by the command will have those other changers applied to it.

As you might have noticed, (output:) accepts no values itself - simply attach it to a hook.

Attempting to use (output:) outside of a custom macro's CodeHook will cause an error.

See also:

(output-data:), (error:)

The (output-data: ) macro

(output-data: Any) Instant

Also known as: (out-data:)

Use this macro inside a (macro:)'s CodeHook to output the value that the macro produces.

Example usage:

(set: $randomCaps to (macro: str-type _str, [
    (output-data:
        (folded: _char making _out via _out + (either:(lowercase:_char),(uppercase:_char)),
        ..._str)
    )
]))
($randomCaps:"I think my voice module is a little bit very broken.")

Rationale:

For more information on custom macros, consult the (macro:) macro's article. All custom macros have inputs and output. This macro specifies the data value to output - provide it at the end of your macro's CodeHook, and give it the value you want the macro call to evaluate to.

This is best suited for macros which primarily compute single data values, like strings, arrays and datamaps. If you wish to output a long span of code, please consider using the (output:) changer instead.

Details:

As soon as an (output-data:) macro is run, all further macros and code in the CodeHook will be ignored, much like how the (go-to:) and (undo:) macros behave.

Attempting to call (output-data:) outside of a custom macro's CodeHook will cause an error.

See also:

(output:), (error:)

The (error: ) macro

(error: String) Instant

Designed for use in custom macros, this causes the custom macro to immediately produce an error, with the given message string, and ceases running any further code in the CodeHook.

Example usage:

(set: $altCaps to (macro: str-type _input, [
    (if: _input is "")[(error: "I can't alt-caps an empty string.")]
    (output:
        (folded: _char making _result via _result +
            (cond: pos % 2 is 0, (lowercase:_char), (uppercase:_char)),
            ..._input
        )
    )
]))
($altCaps:"")

Rationale:

Allowing your custom macros to produce insightful error messages is essential to making them user-friendly, especially if you intend other authors to use them. In the example above, for instance, an empty string inputted to the $altCaps macro would causes (folded:) to produce an error, as ..._input would spread zero characters. However, the earlier custom error provides a better message, explaining exactly what the problem is.

Details:

As with (output-data:), as soon as this is encountered, all further macros and code in the CodeHook will be ignored. Note that this occurs even if the macro is given as input to another macro - (cond: false, (error:"There's a problem"), "") will always produce the error, regardless of (cond:)'s behaviour.

If an empty string is given to this macro, an error (different from the intended error) will be produced. Also, attempting to call (error:) outside of a custom macro's CodeHook will cause another (also different from intended) error.

See also:

(output:), (output-data:), (assert:)

The (datatype: ) macro

(datatype: Any) Datatype

This macro takes any storeable value, and produces a datatype that matches it.

Example usage:

Rationale:

This isn't a macro you're likely to commonly use, because most of the time, you have exact knowledge of the types of data you use throughout your story. But, this can be helpful in custom macros created with (macro:), if they have any any-type parameters. Being able to identify the exact type that such a value is allows you to give types to other data based on that type.

Details:

The only types that this will return are "general" types, like string, number, boolean and such. More specific types like even, or descriptive types like empty, will not be returned, even if it's given a value that matches those types. Nor will spread datatypes be returned - even if a given string consists only of, say, digits, then ...digits won't be returned instead of str.

if there isn't a known datatype value for the given data (for instance, if you give it a HookName) then an error will be produced.

See also:

(source:), (datapattern:)

The (datapattern: ) macro

(datapattern: Any) Any

This takes any storeable value, and produces a datatype that matches it, in a manner similar to (datatype:). However, when given an array or datamap, it creates an array or datamap with its values replaced with their datatypes, which can be used as a more accurate pattern with matches or (set:) elsewhere.

Example usage:

Details:

The (datatype:) macro is useful for examining and comparing the datatypes of values, but when dealing with arrays and datamaps, each of which can have radically different purposes and meanings throughout your story, that macro only produces array or dm when given them, which isn't too helpful when checking if one array is similar to another. This macro produces a more precise result - an array or datamap with datatypes replacing its values - which is compatible with the matches operator, the (set:) macro, parameters of the (macro:) macro, and other places where datatypes are useful.

Details:

This won't return structures containing spread datatypes, even if those could plausibly describe the passed-in data structure - an array with 26 numbers in it will, when given to this macro, produce an array containing num 26 times, no more or less.

Note that this does not produce any string patterns, like those produced by (p:) - any string given to this will still result in str being returned.

See also:

(source:), (datatype:)

The (a: ) macro

(a: [...Any]) Array

Also known as: (array:)

Creates an array, which is an ordered collection of values.

Example usage:

(a:) creates an empty array, which could be filled with other values later. (a: "gold", "frankincense", "myrrh") creates an array with three strings. This is also a valid array, but with its elements spaced in a way that makes them more readable.

(a:
    "You didn't sleep in the tiniest bed",
    "You never ate the just-right porridge",
    "You never sat in the smallest chair",
)

Rationale:

For an explanation of what arrays are, see the Array article. This macro is the primary means of creating arrays - simply supply the values to it, in order.

Details:

Note that due to the way the spread ... operator works, spreading an array into the (a:) macro will re-create the original array unchanged: (a: ...$array) is the same as just $array.

See also:

(dm:), (ds:)

The (dm: ) macro

(dm: [...Any]) Datamap

Also known as: (datamap:)

Creates a datamap, which is a data structure that pairs string names with data values. You should provide a string name, followed by the value paired with it, and then another string name, another value, and so on, for as many as you'd like.

Example usage:

(dm:) creates an empty datamap. (dm: "Cute", 4, "Wit", 7) creates a datamap with two names and values. The following code also creates a datamap, with the names and values laid out in a readable fashion.

(dm:
    "Susan", "A petite human in a yellow dress",
    "Tina", "A ten-foot lizardoid in a three-piece suit",
    "Gertie", "A griffin draped in a flowing cape",
)

Rationale:

For an explanation of what datamaps are, see the Datamap article. This macro is the primary means of creating datamaps - simply supply a name, followed by a value, and so on.

In addition to creating datamaps for long-term use, this is also used to create "momentary" datamaps which are used only in some operation. For instance, to add several values to a datamap at once, you can do something like this:

(set: $map to (dm:))
(set: $map to it + (dm: "Name 1", "Value 1", "Name 2", "Value 2"))

You can also use (dm:) as a kind of "multiple choice" structure, if you combine it with the 's or of syntax. For instance...

(set: $monsterName to "Slime")
(set: $element to $monsterName of (dm:
    "Chilltoad", "Ice",
    "Rimeswan", "Ice",
    "Brisketoid", "Fire",
    "Slime", "Water"
))

...will set $element to one of those elements if $monsterName matches the correct name. But, be warned: if none of those names matches $monsterName, an error will result.

See also:

(a:), (ds:)

The (ds: ) macro

(ds: [...Any]) Dataset

Also known as: (dataset:)

Creates a dataset, which is an unordered collection of unique values.

Example usage:

(ds:) creates an empty dataset, which could be filled with other values later. (ds: "gold", "frankincense", "myrrh") creates a dataset with three strings.

Rationale:

For an explanation of what datasets are, see the Dataset article. This macro is the primary means of creating datasets - simply supply the values to it, in any order you like.

Details:

You can also use this macro to remove duplicate values from an array (though also eliminating the array's order) by using the spread ... operator like so: (a: ...(ds: ...$array)).

See also:

(dm:), (a:)

The (all-pass: ) macro

(all-pass: Lambda, [...Any]) Boolean

Also known as: (pass:)

This takes a "where" lambda and a series of values, and evaluates to true if the lambda, when run using each value, never evaluated to false.

Example usage:

Rationale:

The contains and is in operators can be used to quickly check if a sequence of values contains an exact value or values, and, combined with the all and any data names, can check that the values in a sequence merely resemble a kind of value - for instance, that they're positive numbers, or strings beginning with "E". But, they are times when you're writing the same check over and over, like is an empty or is a whitespace, or something more complicated, and would like the ability to store the check in a lambda and reuse it.

The (all-pass:) macro lets you perform these checks easily using a lambda, identical to that used with (find:) - simply write a "temp variable where a condition" expression, and every value will be put into the temp variable one by one, and the condition checked for each.

Additionally, you can use (all-pass:) just to run a single "where" lambda against a single value - for instance, as a variation of (if:). This is permitted, too - simply write the lambda and the single value. For those cases, you may wish to write it as (pass:), a shorthand form that visually indicates that you're only checking one value rather than "all".

Details:

Of course, if any condition should cause an error, such as checking if a number contains a number, then the error will appear.

If zero values are given to (all-pass:), then it will return true by default.

The temp variable, which you can name anything you want, is controlled entirely by the lambda - it doesn't exist outside of it, it won't alter identically-named temp variables outside of it, and you can't manually (set:) it within the lambda.

You can refer to other variables, including other temp variables, in the where condition. For instance, you can write (set: _name to "Eva")(all-pass: _item where _item is _name, "Evan", "Eve", "Eva"). However, for obvious reasons, if the outer temp variable is named the same as the lambda's temp variable, it can't be referred to in the condition.

See also:

(sorted:), (count:), (find:), (some-pass:), (none-pass:)

The (altered: ) macro

(altered: Lambda, [...Any]) Array

This takes a "via" lambda and a sequence of values, and creates a new array with the same values in the same order, but altered via the operation in the lambda's "via" clause.

Example usage:

Rationale:

Transforming entire arrays or datasets, performing an operation on every item at once, allows arrays to be modified with the same ease that single values can - just as you can add some extra text to a string with a single +, so too can you add extra text to an entire array of strings using a single call to (altered:).

This macro uses a lambda (which is just the "temp variable via an expression" expression) to take each item in the sequence and produce a new value to populate the resulting array. For (altered: _a via _a + 1, 10,20,30) it will produce 10 + 1, 20 + 1 and 30 + 1, and put those into a new array.

Details:

Of course, if any operation applied to any of the values should cause an error, such as trying to add a string to a number, an error will result.

An error will NOT appear if you provide no values after the lambda - an empty array will be returned instead. This allows you to write (altered: $lambda, ...$array) without checking whether $array contains any values (which you may not be certain of, if it contains the result of a previous (find:)).

The temp variable, which you can name anything you want, is controlled entirely by the lambda - it doesn't exist outside of it, it won't alter identically-named temp variables outside of it, and you can't manually (set:) it within the lambda.

You can refer to other variables, including other temp variables, in the via expression. For instance, you can write (altered: _object via _playerName + "'s " + _object, "Glove", "Hat", "Purse"). However, for obvious reasons, if the outer temp variable is named the same as the lambda's temp variable, it can't be referred to in the expression.

If no values are given to (altered:) except for the lambda, an empty array will be produced.

See also:

(for:), (folded:)

The (count: ) macro

(count: Array or String, ...Any) Number

Accepts a string or array, followed by a value, and produces the number of times any of the values are inside the string or array.

Example usage:

(count: (a:1,2,3,2,1), 1, 2) produces 4. (count: "Though", "ugh","u","h") produces 4.

Rationale:

You can think of this macro as being like the contains operator, but more powerful. While contains produces true or false if occurrences of the right side appear in the left side, (count:) produces the actual number of occurrences.

Note that if you only want to check if an array or string contains any or all of the values, it's easier to use contains with the all property like so: $arr contains all of (a:1,2) and $arr contains any of (a:1,2). But, if you need an exact figure for the number of occurrences, this macro will be of use.

Details:

If you use this with a number, boolean, datamap, dataset (which can't have duplicates), or anything else which can't have a value, then an error will result.

If you use this with a string, and the values aren't also strings, then an error will result.

Substrings are counted separately from each other - that is, the string "Though" contains "ugh" once and "h" once, and (count: "Though","ugh","h") results in 3. To check for "h" occurrences that are not contained in "ugh", you can try subtracting two (count:)s - (count: "Though","ugh") - (count: "Though","h") produces 1.

See also:

(datanames:), (datavalues:)

The (dataentries: ) macro

(dataentries: Datamap) Array

This takes a datamap, and returns an array of its name/value pairs. Each pair is a datamap that only has "name" and "value" data. The pairs are ordered by their name.

Example usage:

Rationale:

There are occasions where operating on just the names, or the values, of a datamap isn't good enough - you'll want both. Rather than the verbose process of taking the (datanames:) and (datavalues:) arrays and using them (interlaced:) with each other, you can use this macro instead, which allows the name and value of each entry to be referenced using "name" and "value" properties.

See also:

(datanames:), (datavalues:)

The (datanames: ) macro

(datanames: Datamap) Array

This takes a datamap, and returns a sorted array of its data names, sorted alphabetically.

Example usage:

(datanames: (dm:'B','Y', 'A','X')) produces the array (a: 'A','B')

Rationale:

Sometimes, you may wish to obtain some information about a datamap. You may want to list all of its data names, or determine how many entries it has. You can use the (datanames:) macro to do these things: if you give it a datamap, it produces a sorted array of all of its names. You can then (print:) them, check the length of the array, obtain a subarray, and other things you can do to arrays.

See also:

(datavalues:), (dataentries:)

The (datavalues: ) macro

(datavalues: Datamap) Array

This takes a datamap, and returns an array of its values, sorted alphabetically by their name.

Example usage:

(datavalues: (dm:'B',24, 'A',25)) produces the array (a: 25,24)

Rationale:

Sometimes, you may wish to examine the values stored in a datamap without referencing every name - for instance, determining if 0 is one of the values. (This can't be determined using the contains keyword, because that only checks the map's data names.) You can extract all of the datamap's values into an array to compare and analyse them using (datavalues:). The values will be sorted by their associated names.

See also:

(datanames:), (dataentries:)

The (find: ) macro

(find: Lambda, [...Any]) Array

This searches through the given values, and produces an array of those which match the given search test (which is expressed using a temp variable, the where keyword, and a boolean condition). If none match, an empty array is produced.

Example usage:

Rationale:

Selecting specific data from arrays or sequences based on a user-provided boolean condition is one of the more common and powerful operations in programming. This macro allows you to immediately work with a subset of the array's data, without caring what kind of subset it is. The subset can be based on each string's characters, each datamap's values, each number's evenness or oddness, whether a variable matches it... anything you can write.

This macro uses a lambda (which is just the "temp variable where a condition" expression) to check every one of the values given after it. For (find: _item where _item > 40, 30, 60, 90), it will first check if 30 > 40 (which is false), if 60 > 40 (which is true), and if 90 > 40 (which is true), and include in the returned array those values which resulted in true.

Details:

Of course, if any condition should cause an error, such as checking if a number contains a number, then the error will appear.

However, an error will NOT appear if you provide no values after the lambda - searching an empty sequence will simply result in an empty array being returned. This allows you to write (find: $lambda, ...$array) without checking whether $array contains any values (which you may not be certain of, if it contains the result of a previous (find:)).

The temp variable, which you can name anything you want, is controlled entirely by the lambda - it doesn't exist outside of it, it won't alter identically-named temp variables outside of it, and you can't manually (set:) it within the lambda.

You can refer to other variables, including other temp variables, in the where condition. For instance, you can write (set: _name to "Eva")(find: _item where _item is _name, "Evan", "Eve", "Eva"). However, for obvious reasons, if the outer temp variable is named the same as the lambda's temp variable, it can't be referred to in the condition.

There isn't a way to examine the position of a value in the condition - you can't write, say, (find: _item where _pos % 2 is 0, "A", "B", "C", "D") to select just "B" and "D".

You shouldn't use this macro to try and alter the given values! Consider the (altered:) or (folded:) macro instead.

See also:

(sorted:), (all-pass:), (some-pass:), (none-pass:)

The (folded: ) macro

(folded: Lambda, ...Any) Any

This takes a "making" lambda and a sequence of values, and creates a new value (the "total") by feeding every value in the sequence to the lambda, akin to folding a long strip of paper into a single square. The first value after the lambda is put into the total (which is the variable inside the lambda's "making" clause) before running the lambda on the remaining values.

Example usage:

Rationale:

The (for:) macro, while intended to display multiple copies of a hook, can also be used to run a single macro call multiple times. You may wish to use this to repeatedly (set:) a variable to itself plus one of the looped values (or some other operation). (folded:) is meant to let you perform this in a shorter, more fluid fashion.

Consider, first of all, a typical (for:) and (set:) loop such as the following:

(set:$enemies to (a:(dm:"Name","Mossling", "HP",7), (dm:"Name","Moldling","HP",2)))
{(set:_allHP to 0)
(for: each _enemy, ...$enemies)[
    (set:_allHP to it + _enemy's HP)
]}
TOTAL HEART POINTS: _allHP

This can be rewritten using (folded:) as follows. While this version may seem a little harder to read if you're not used to it, it allows you to accomplish the same thing in a single line, by immediately using the macro's provided value without a variable:

(set:$enemies to (a:(dm:"Name","Mossling", "HP",7), (dm:"Name","Moldling","HP",2)))
TOTAL HEART POINTS: (folded: _enemy making _allHP via _allHP + _enemy's HP, 0, ...$enemies)

If you need to perform this operation at various different times in your story, you may wish to (set:) the lambda into a variable, so that you, for instance, might need only write:

(set:$enemies to (a:(dm:"Name","Mossling", "HP",7), (dm:"Name","Moldling","HP",2)))
(set: $totalEnemyHP to (_enemy making _allHP via _allHP + _enemy's HP))
TOTAL HEART POINTS: (folded: $totalEnemyHP, 0, ...$enemies)

Details:

Let's look at this example usage again.

(set:$enemies to (a:(dm:"Name","Mossling", "HP",7), (dm:"Name","Moldling","HP",2)))
(folded: _enemy making _allHP via _allHP + _enemy's HP, 0, ...$enemies)

This macro call uses a "making" lambda (which is the "temp variable making another temp variable via expression" expression) to run the expression using every provided value, much like those repeated (set:) calls. The temp variable in the "making" clause, _allHP, is the total, and at the start, it is set to the first provided value (in this case, 0). The temp variable at the start, _enemy, is then set to the next value after that (which in this case would be the "Mossling" datamap), and the "via" clause is used to set _allHP to a new value. This repeats until all of the values have been handled. Then, the final result of _allHP is returned.

Of course, if at any time the expression should cause an error, such as adding a number to a string, then an error will result.

Both of the temp variables can be named anything you want. As with other lambda macros, they don't exist outside of it, won't alter identically-named temp variables outside of it, and can't be manually (set:) within the lambda.

You can refer to other variables, including other temp variables, in the via expression. For instance, you can write (folded: _score making _totalScore via _totalScore + _score * _bonusMultiplier). However, for obvious reasons, if the outer temp variable is named the same as the lambda's temp variables, it can't be referred to in the expression.

You can also use a "where" clause inside the "making" lambda to prevent an operation from occurring if a value isn't suitable - (folded: _item making _total via _total + _item where _item > 0, ...$arr) will only sum up the values in $arr which are greater than 0.

See also:

(for:), (altered:), (joined:)

The (interlaced: ) macro

(interlaced: Array, ...Array) Array

Takes multiple arrays, and pairs up each value in those arrays: it creates an array containing each array's first value followed by each array's second value, and so forth. If some values have no matching pair (i.e. one array is longer than the other) then those values are ignored.

Example usage:

(interlaced: (a: 'A', 'B', 'C', 'D'), (a: 1, 2, 3)) is the same as (a: 'A',1,'B',2,'C',3)

Rationale:

There are a couple of other macros which accept data in pairs - the most notable being (dm:), which takes data names and data values paired. This macro can help with using such macros. For instance, you can supply an array of (datanames:) and (datavalues:) to (interlaced:), and supply that to (dm:), to produce the original datamap again. Or, you can supply just the names, and use a macro like (repeated:) to fill the other values.

However, (interlaced:) can also be of use alongside macros which accept a sequence: you can use it to cleanly insert values between each item. For instance, one can pair an array with another array of spaces, and then convert them to a string with (str:). (str: ...(interlaced: $arr, (repeated: $arr's length, ' '))) will create a string containing each element of $arr, followed by a space.

Details:

If one of the arrays provided is empty, the resulting array will be empty, as well.

See also:

(a:), (rotated:), (repeated:)

The (none-pass: ) macro

(none-pass: Lambda, ...Any) Boolean

This can be thought of as the opposite of (all-pass:): it produces true if every value, when given to the lambda, never evaluated to true. If zero values are given to (none-pass:), then it will return true by default, just like (all-pass:). For more information, consult the description of (all-pass:).

Example usage:

(set: $partyMembers to (a: (dm: "name", "Alan", "curseLevel", 0), (dm: "name", "Jess", "curseLevel", 0)))))
(set: $noMelvins to (none-pass: where its name is "Melvin", ...$partyMembers))

The (permutations: ) macro

(permutations: ...Any) Array

When given a sequence of values, this produces an array containing each permutation of the order of those values, as arrays.

Example usage:

Rationale:

If you're writing an algorithm that cares about combinations of data, such as a procedurally generated puzzle or password, you may find that this macro has a number of subtle uses. This macro by itself provides an easy way to check if a sequence of values contains exactly the same values as another sequence, regardless of order. For instance, you can check if another array stored in $array contains exactly two 3s, two 2s and one 1 by writing (permutations:3,1,3,2,2) contains $array, because if it was so, the array would be included among those permutations. You can't perform this check by writing (dataset:3,1,3,2,2) is (dataset:...$array) because datasets, by design, don't hold multiples of a single value (such as 3).

Additionally, this macro can be combined with (filtered:) and (shuffled:) to help you find a permutation that matches certain criteria. For instance, to find a random permutation of the numbers 0 to 5 that doesn't begin with 0, you can write 1st of (shuffled: ...(filtered: where its 1st is not 0, ...(permutations:0,1,2,3,4))). While this could be performed by simply re-running (shuffled:0,1,2,3,4) until it produced an array that didn't begin with 0, the code to check and re-run this would be much more complicated.

Details:

When given no values, this simply returns the empty array (a:).

See also:

(shuffled:)

The (range: ) macro

(range: Number, Number) Array

Produces an array containing an inclusive range of whole numbers from a to b, in ascending order.

Example usage:

(range:1,14) is equivalent to (a:1,2,3,4,5,6,7,8,9,10,11,12,13,14) (range:2,-2) is equivalent to (a:-2,-1,0,1,2)

Rationale:

This macro is a shorthand for defining an array that contains a sequence of integer values. Rather than writing out all of the numbers, you can simply provide the first and last numbers.

Details:

Certain kinds of macros, like (either:) or (dataset:), accept sequences of values. You can use (range:) with these in conjunction with the ... spreading operator: (dataset: ...(range:2,6)) is equivalent to (dataset: 2,3,4,5,6,7), and (either: ...(range:1,5)) is equivalent to (random: 1,5).

See also:

(a:)

The (repeated: ) macro

(repeated: Number, ...Any) Array

When given a number and a sequence of values, this macro produces an array containing those values repeated, in order, by the given number of times.

Example usage:

Rationale:

This macro, as well as (range:), are the means by which you can create a large array of similar or regular data, quickly. Just as an example: you want, say, an array of several identical, complex datamaps, each of which are likely to be modified in the game, you can use (repeated:) to make those copies easily. Or, if you want, for instance, a lot of identical strings accompanied by a lone different string, you can use (repeated:) and add a (a: "string")to the end.

When you already have an array variable, this is similar to simply adding that variable to itself several times. However, if the number of times is over 5, this can be much simpler to write.

Details:

An error will, of course, be produced if the number given is negative, or contains a fraction. As of 3.2.0, however, it will no longer error if the number is 0.

If you wish to repeat a string multiple times, please use (str-repeated:).

See also:

(a:), (range:), (str-repeated:)

The (reversed: ) macro

(reversed: [...Any]) Array

Similar to (a:), except that it creates an array containing the elements in reverse order.

Example usage:

(set: $a to (reversed: ...$a, 2)) sets $a to its reverse, with 2 at the start.

Rationale:

Having stored items in an array, or obtained a built-in array like (history:), you may want to perform some action using it - maybe assemble them into a single string using (folded:) - in the opposite order to which they are stored. (reversed:) allows this reversal to be easily created.

Details:

Unlike (shuffled:), which produces an error if one or no elements are given, this does not error if a non-reversible sequence of one or zero is given. This is meant to permit its wider use with data whose length you may not always have control over, such as the (history:) array.

If you wish to specifically reverse the characters in a string, please use (str-reversed:).

See also:

(a:), (shuffled:), (rotated:), (str-reversed:)

The (rotated-to: ) macro

(rotated-to: Lambda, [...Any]) Array

Similar to the (a:) macro, but it also takes a "where" lambda at the start, and cycles the order of the subsequent values so that the first value to match the lambda is placed at the start.

Example usage:

Rationale:

This is a variation of the (rotated:) macro. Both of these macros allow you to cycle through a sequence of values, wrapping back to the start, until a certain value is at the front, then provide an array of the values in that order. The former macro lets you specify an exact number of rotations to do; this one lets you specify what kind of value should be at the front, if you don't know the exact order of the passed-in strings (which may be the case if they come from an array).

Note that while the lambda argument provides a lot of flexibility, if you simply want to compare each value to a known value, where it is (such as in an example above) is a simple enough lambda formulation to do so.

Details:

If the lambda doesn't match any of the values (that is, there's no value to rotate to) then an error will result.

To ensure that it's being used correctly, this macro requires two or more items - providing just one or none will cause an error to be presented.

See also:

(sorted:), (rotated:), (find:)

The (rotated: ) macro

(rotated: Number, ...Any) Array

Similar to the (a:) macro, but it also takes a number at the start, and moves each item forward by that number, wrapping back to the start if they pass the end of the array.

Example usage:

Rationale:

Sometimes, you may want to cycle through a sequence of values, without repeating any until you reach the end. For instance, you may have a rotating set of flavour-text descriptions for a thing in your story, which you'd like displayed in their entirety without the whim of a random picker. The (rotated:) macro allows you to apply this "rotation" to a sequence of data, changing their positions by a certain number without discarding any values.

Remember that, as with all macros, you can insert all the values in an existing array using the ... syntax: (set: $a to (rotated: 1, ...$a)) is a common means of replacing an array with a rotation of itself.

Think of the number as being an addition to each position in the original sequence - if it's 1, then the value in position 1 moves to 2, the value in position 2 moves to 3, and so forth.

Incidentally... you can also use this macro to rotate a string's characters, by doing something like this: (str: ...(rotated: 1, ...$str))

Details:

To ensure that it's being used correctly, this macro requires two or more items - providing just one or none will cause an error to be presented.

If you can't reliably know how many positions you wish to rotate, but know that you need a certain value to be at the front, simply use the (rotated-to:) variant of this macro instead.

See also:

(sorted:), (rotated-to:)

The (shuffled: ) macro

(shuffled: Any, ...Any) Array

Similar to (a:), this produces an array of the given values, except that it randomly rearranges the elements instead of placing them in the given order.

Example usage:

(if: $condiments is not an array)[(set: $condiments to (shuffled: "mustard", "mayo", "chili sauce", "salsa"))]
You reach into the fridge and grab... (nth: visits, ...$condiments)? OK.

Rationale:

If you're making a particularly random story, you'll often want to create a 'deck' of random descriptions, elements, etc. that you can use repeatedly. That is to say, you'll want to put them in a random order in an array, preserving that random order for the duration of a game.

The (either:) macro is useful for selecting an element from an array randomly (if you use the spread ... syntax), but isn't very helpful for this particular problem. Additionally, the random data name of arrays can be used to retrieve a random value, and can remove that value using (move:), but it isn't as effective if you want that value to remain in the deck after being used.

The (shuffled:) macro is another solution: it takes elements and returns a randomly-ordered array that can be used as you please.

Details:

To ensure that it's being used correctly, this macro requires two or more items - providing just one (or none) will cause an error to be presented.

See also:

(a:), (either:), (rotated:)

The (some-pass: ) macro

(some-pass: Lambda, ...Any) Boolean

This is similar to (all-pass:), but produces true if one or more value, when given to the lambda, evaluated to true. It can be thought of as shorthand for putting not in front of (none-pass:). If zero values are given to (all-pass:), then it will return false by default. For more information, consult the description of (all-pass:).

Example usage:

(set: $partyMembers to (a: (dm: "name", "Alan", "curseLevel", 0), (dm: "name", "Jess", "curseLevel", 0)))))
(set: $taintedParty to (some-pass: where its curseLevel > 0, ...$partyMembers))

The (sorted: ) macro

(sorted: Number or String, ...Number or String) Array

Similar to (a:), except that it requires only numbers or strings, and orders them in English alphanumeric sort order, rather than the order in which they were provided.

Example usage:

(set: $a to (a: 'A','C','E','G', 2, 1))
(print: (sorted: ...$a))

Rationale:

Often, you'll be using arrays as 'decks' that will provide values to other parts of your story in a specific order. If you want, for instance, several strings to appear in alphabetical order, this macro can be used to create a sorted array, or (by using the spread ... syntax) convert an existing array into a sorted one.

Details:

Unlike other programming languages, strings aren't sorted using ASCII sort order, but alphanumeric sorting: the string "A2" will be sorted after "A1" and before "A11". Moreover, if the player's web browser supports internationalisation (that is, every current browser except Safari 6-8 and IE 10), then the strings will be sorted using English language rules (for instance, "é" comes after "e" and before "f", and regardless of the player's computer's language settings. Otherwise, it will sort using ASCII comparison (whereby "é" comes after "z").

Currently there is no way to specify an alternative language locale to sort by, but this is likely to be made available in a future version of Harlowe.

To ensure that it's being used correctly, this macro requires two or more items - providing just one (or none) will cause an error to be presented.

See also:

(a:), (shuffled:), (rotated:)

The (subarray: ) macro

(subarray: Array, Number, Number) Array

When given an array, this returns a new array containing only the elements whose positions are between the two numbers, inclusively.

Example usage:

Rationale:

This macro may seem redundant, as you can obtain subarrays of arrays without this macro, by using the 's or of syntax along with either a specified range of consecutive positions, or an array of arbitrary position numbers. For instance, $a's 4thto12th obtains a subarray of $a containing its 4th through 12th values, $a's (a:1,3,5) obtains a subarray of $a containing just its 1st, 3rd and 5th positions, and $a's (range:1, $b) obtains a subarray of each position up to $b.

However, in the specific situation where you want to use a variable negative position, counting from the end of the array, there isn't a succinct option using that syntax. When gathering each value in array $a between position 1 and $b, where $b is a negative position counting from the end, (range:1, $b) doesn't work, and the best you can do without this macro is something like $a's (range: 1, $b + $a's length). So, this macro can be used as a slightly shorter alternative, by writing (subarray: $a, 1, -$b).

Details:

As mentioned above, if you provide negative numbers, they will be treated as being offset from the end of the array - -2 will specify the 2ndlast item, just as 2 will specify the 2nd item.

If the last number given is larger than the first (for instance, in (subarray: (a:1,2,3,4), 4, 2)) then the macro will still work - in that case returning (a:2,3,4) as if the numbers were in the correct order.

See also:

(substring:), (rotated:)

The (unpack: ) macro

(unpack: ...VariableToValue) Instant

A specialised variation of (put:) that lets you extract multiple values from an array, datamap or string, at once, and put them into multiple variables, by placing a matching data structure on the right of into containing variables at the positions of those values. For instance, (unpack: (a: 1, 2, 3) into (a: $x, 2, $y)) sets $x to 1 and $y to 3, and (unpack: (dm: "B", 3, "A", 1) into (dm: "A", $x, "B", $y)) sets $x to 1 and $y to 3.

Example usage:

Rationale:

Extracting values from data structures into variables using just the (set:) and (put:) macros can be rather cumbersome, especially if you need to extract values from the same array or datamap. The (unpack:) macro provides a means to efficiently access multiple values in such structures, by describing the locations of those values within the structure - if you want to obtain the first, second, and fourth values in an array and put them into $a, $b and $c, just write (a: $a, $b, any, $c), in exactly those positions, to the right of into. The visual clarity of this can provide great assistance in understanding and reminding you of what the data structure is, and the relationship of the destination variables to their source values.

The (unpack:) macro also lets you use string patterns (produced by (p:) and other such macros) to unpack strings into multiple components. To obtain all the digit characters at the start of a string, and nothing else, and put them into $a, just write (p: ...digit-type $a). No long-winded (for:) loops and individual character checks are needed - simply describe the string using the pattern macros, using typed variables to mark parts to extract, and they can be easily extracted.

Details:

Harlowe checks that each value on the right side (henceforth called the "pattern side") has a value that matches it (using the same rules as the matches operator) on the left side (the "data side"), and overwrites the pattern side with the data side, causing the variables at various positions in the pattern side to be overwritten with values from the data. (Remember that datamaps' "positions" are determined by their datanames, not their locations in the (dm:) macro that created them, as, unlike arrays, they are not sequential.)

For extracting substrings from strings, use the (p:) macro and its related macros to construct a string pattern, resembling array patterns. For instance, (unpack: "Slime Ball" into (p: (p-either: "Silt", "Mud", "Peat", "Slime")-type _element, " ", (p-either: "Ball", "Blast", "Shot", "Beam")-type _shape)) extracts the words "Slime" and "Ball" from the value, and puts them in the _element and _shape temp variables. Note that when this is done, the _element variable is restricted to (p-either: "Silt", "Mud", "Peat", "Slime")-type data, so putting any other kind of string into it will cause an error. Generally, it's recommended to use temp variables for string destructuring, and then, if you need more general variables to hold the extracted substrings, place them in a less restricted variable afterward.

Note that the pattern side's data structure can have any number of nested data structures inside it. (unpack: (a: (a: 1), 2, (dm: "A", 3)) into (a: (a: $x), 2, (dm: "A", $y))) also sets $x to 1 and $y to 3. If you need to reach deeply into a data structure (such as one produced by (passages:), (saved-games:) or (open-storylets:)) to get a certain set of values, this can come in handy.

You may have noticed that the data structures on the pattern side may have values that aren't variable names, such as the 2 in the preceding example. These can be used as error-checking values - if a matching value isn't present on the right side at that same position, then an error will be reported. To ensure that the data side does indeed contain the values you expect it to, you may include these values in the pattern side. Of course, you may want to simply enforce that a value of a given datatype is in that position, rather a specific value - this can be done by placing a datatype name at that position, like num or str or empty. Consult the datatype article for more information on datatype names.

As you may have also noticed, this syntax is convenient for extracting values from the left side of arrays. But, what if you wish to only select values from the middle, or to skip certain values without putting them in variables? You could use a value or a datamap name at that position, such as by writing (set: (a: num, $y, $x) to $array) - though, if you aren't even certain of the data types that could be at those positions, you may find the special any data type to be a big help - (set: (a: any, $y, $x) to $array) sets $x to the 3rd value in $array and $y to the 2nd value, without needing to worry about what the first value might be.

(When dealing with string patterns, the equivalent of any is simply str, as strings can't contain non-string data.)

This syntax can be combined with typed variables - simply place the typed variable where a normal variable would be within the pattern side. (unpack: $array into (a: num, num-type $y, num-type _x)) not only sets $y and _x, but it also restricts them to number data, all in one statement. If the typed variable is inside an array, and involves a spread datatype (like ...num) then it is restricted to data that matches (a: ...num) (i.e. arrays of zero or more num values), and it automatically gathers multiple values from the right-hand-side that match the datatype. (set: (a: str, ...bool-type $examAnswers) to (a: "ANSWER KEY", true, false, false, true, false)) sets $examAnswers to (a:true, false, false, true, false).

If the destination doesn't contain any variables - for instance, if you write (set: (a:2,3)'s 1st to 1), or (set: true to 2) - then an error will be printed.

For obvious reasons, (unpack:) can't be used with datasets - you'll have to convert them to arrays on the right side.

See also:

(set:), (put:), (move:)

The (current-date: ) macro

(current-date: ) String

This date/time macro produces a string of the current date the current player's system clock, in the format "Thu Jan 01 1970".

Example usage:

Right now, it's (current-date:).

The (current-time: ) macro

(current-time: ) String

This date/time macro produces a string of the current 12-hour time on the current player's system clock, in the format "12:00 AM".

Example usage:

The time is (current-time:).

The (monthday: ) macro

(monthday: ) Number

This date/time macro produces a number corresponding to the day of the month on the current player's system clock. This should be between 1 (on the 1st of the month) and 31, inclusive.

Example usage:

Today is day (monthday:).

The (weekday: ) macro

(weekday: ) String

This date/time macro produces one of the strings "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" or "Saturday", based on the weekday on the current player's system clock.

Example usage:

Today is a (weekday:).

The (ignore: ) macro

(ignore: ...[Any]) Command

If you want to test your passage while ignoring a specific command macro in it, temporarily change that command macro's name to (ignore:), and it will ignore all of the data given to it.

Example usage:

(ignore: ?ghost, (text-style:'outline')) is an (enchant:) macro that has been temporarily changed to (ignore:), so that the passage may be tested without the style being applied.

Rationale:

If you want to quickly test some aspect of your passage, you may wish to remove one or more of the commands in it, such as (enchant:) or (hide:). These commands can have a large and cumbersome set of data given to them, and removing and adding them can be bothersome. The (ignore:) macro can be of assistance here: simply change the command's name to "ignore", and it will do nothing, while NOT causing an error regardless of what sort of data is given to it. Then, you can quickly change it back to the original name after testing.

Details:

While it will ignore all well-formed data given to it, (ignore:) will NOT suppress errors that are already present in the data. For instance, (ignore: 4 + "2") will still cause an error.

This command can have changers attached, but will, of course, ignore them.

See also:

(test-true:), (test-false:)

The (test-true: ) macro

(test-true: ...[Any]) Changer

If you want to test your passage, while ignoring a specific changer macro in it, temporarily change that changer macro's name to (test-true:), and it will ignore all of the data given to it, while enabling the hook.

Example usage:

Rationale:

While testing your passage, you may wish to examine what would happen if a changer, such as (if:) or (else:), were to have no effect on its hook. But, removing and adding the macro from your passage code may get tedious and error-prone, especially if you need to disable several such changers at once. Instead, you can simply temporarily change the macro's name to (test-true:), and change it back later. Regardless of what data is given to this macro (colour data for (background:), booleans for (if:), hooks for (replace:)), this macro won't cause an error.

Details:

While it will ignore all well-formed data given to it, (test-true:) will NOT suppress errors that are already present in the data. For instance, (test-true: 5 + "3")[] will still cause an error.

If (test-true:) and another changer are added together, such as in (test-true:)+(if:visits is 1), then the latter changer will take precedence and override it.

See also:

(ignore:), (test-false:)

The (test-false: ) macro

(test-false: ...[Any]) Changer

If you want to test your passage in order to see what would happen if an (if:), (unless:) or (else-if:) macro would hide the hook it's attached to, you can temporarily change the name of the macro to (test-false:), which causes it to ignore the data given to it and act as if it was given false.

Example usage:

Rationale:

This is a counterpart of (test-true:), designed specifically for testing hooks with (if:), (unless:) and (else-if:) changers attached. For most changers, using (test-true:) is sufficient to temporarily suppress the effect of the changer. However, if you want the hook to remain hidden by default during the test, then using (test-true:) would still cause the hook to be displayed. While you could temporarily attach (hidden:) to the hook as well, this can be cumbersome, especially if that would involve adding an additional changer to a long sequence of changers attached to that hook. (test-false:) provides a more convenient alternative.

Details:

While it will ignore all well-formed data given to it, (test-false:) will NOT suppress errors that are already present in the data. For instance, (test-false: 5 + "3")[] will still cause an error.

If (test-false:) and another changer are added together, such as in (test-false:)+(if:visits is 1), then the latter changer will take precedence and override it.

See also:

(ignore:), (test-true:)

The (assert: ) macro

(assert: Boolean) Instant

A debugging macro that produces a helpful error whenever the expression given to it produces Boolean false. Use this when testing your story to help ensure that certain facts about the game state are true or not.

Example usage:

(assert: $isWounded is $isBleeding or $isBandaged) ensures that if $isWounded is true, one of $isBleeding or $isBandaged MUST be true, and if it's false, both of them MUST be false. Otherwise, an error is produced.

Rationale:

Harlowe's debug mode provides panels to check the current game state, the contents of variables, active enchantments, and so forth, providing assistance in identifying bugs. Of course, knowing what variables contain is not the same as knowing whether the relationships between them are being maintained, and a way of encoding these relationships, in your story, can provide an additional layer of security when debugging your game.

(assert:) allows you to assert facts about the game state, facts which absolutely must be true, so much so that an error should be produced. For example, if your story's code assumes that the variable $nails will always be smaller than or equal to $maxNails, and you want to ensure that no bugs are written that cause $nails to be greater, you may write (assert: $nails is <= $maxNails), and place that call in a "debug-header" tagged passage. Thus, should a bug ever appear which causes these variables to no longer maintain their relationship, the (assert:) call will produce an error.

Details:

Note that there are other tools within Harlowe to ensure that variables are obeying certain restrictions, which make the need for certain simple (assert:) calls unnecessary. Chief among these features is TypedVars, which can be provided to (set:) to permanently and automatically restrict a certain variable to a narrow range of data. Instead of writing (assert: $petals is an int), you can change the earliest (set:) call that creates $petals to (set: int-type $petals to 0).

Though this is classed as a "debugging" macro, this works even outside of debug mode.

This can also be useful within custom macros, as a shortened form of combining (if:) with (error:). However, the error message produced by (assert:) may not be as insightful as the customisable error message given to (error:), so it's not especially recommended for use within custom macros that are meant for other authors to use.

See also:

(error:), (assert-exists:)

The (assert-exists: ) macro

(assert-exists: HookName or String) Command

A debugging macro that confirms whether a hook with the given name, or passage text matching the given string, is present in the passage. If not, it will produce a helpful error. Use this to test whether enchantment macros like (click:), (enchant:) or (show:) are working properly.

Example usage:

Rationale:

The macros in Harlowe that remotely affect other hooks or text based on their name or contents, such as (click:), are designed such that they do not cause an error if no matching hooks or text is found in the passage. This allows them to be thought of as similar to CSS rules for how passage prose is to be rendered - something like (enchant:?dust, (text-style:'blur')) states the "rule" that ?dust hooks are to be blurred - rather than as imperative commands that must be fulfilled there and then. This means that they can be placed in every passage, via "header" or "footer" tagged passages, without errors occurring. But, this flexibility comes at a cost. In the minority of situations where you need to be certain that a macro is affecting a visible in-passage structure, you'll often want to test with this macro, so as to produce an error if those structures do not exist.

Details:

This command (and note that (assert:) doesn't produce a command) probes the current passage in which it's located in order to determine whether to produce an error or not. As such, like all commands, it can be saved into a variable, and deployed into passage code using that variable, to save having to retype it in full.

If you provide an empty string to this macro (which obviously can't be found in the passage), it will produce a different kind of error than what would be desired.

See also:

(error:), (assert:)

The (mock-visits: ) macro

(mock-visits: ...String) Command

A macro that can only be used in debug mode, this allows you to mark various passages as "visited", even though the player actually hasn't. This allows you to quickly test passages that use the visits keyword, or the (history:) datamap, without having to play through the whole game from the start.

Example usage:

Rationale:

Using the visits keyword, or the (history:) array, as a way to track the player's progress instead of using Boolean variables and (set:) can produce simpler, more understandable code - it's obvious what (if: visits is > 2) means just by looking at it. But, it comes with a cost: when testing your story using the "Play from here" feature in the Twine editor, you may want to pretend that you have visited the requisite passages a given number of times, so as to examine the resulting prose. If you were using variables, you could add a few temporary (set:) macros to the passage, or put them in a "debug-header" tagged passage, to adjust the variables to match a game in progress. This macro provides that same functionality to the visits keyword and (history:) array, letting you temporarily adjust it for testing purposes.

Details:

It's critical to understand that these are mock visits - the passages listed are not actually visited, and code inside them is not run by this macro.

As stated above, this macro will cause an error if it's used outside debug mode. This is NOT intended for use in a final game - while temporarily tweaking the meaning of visits and (history:) is convenient for testing, the author should be able to trust that in the "real" game, they correctly report the visits the player has actually made, so that the story's code can be properly understood.

Each occurrence of a passage name given to this macro counts as a single mock visit. Add multiples of the same passage name to register multiple mock visits to that passage.

If this macro is used multiple times, only the final usage will count - all the rest will be forgotten. (mock-visits:"A")(mock-visits:"B"), for instance, will only cause the "B" passage to be considered visited 1 time. This allows you to remove mock visits in the middle of a story by writing (mock-visits:).

The effects of (mock-visits:) are NOT saved by (save-game:).

If you undo past a passage that used (mock-visits:), the effects of that macro call will be removed, as if it had been a (set:) macro call.

This command can't have changers attached - attempting to do so will produce an error.

See also:

(history:), (set:)

The (verbatim-source: ) macro

(verbatim-source: Any) Command

Also known as: (v6m-source:)

A convenient combination of (verbatim-print:) and (source:), this prints out the Harlowe source code of any value given to it.

Example usage:

(v6m-source: [(open-storylets:)](#macro_open-storylets)'s 1st) prints the source of the first datamap in the array generated by (open-storylets:).

Rationale:

This macro provides a quick way for you to display the source code of a Harlowe value. Normally, you can't easily print the string returned by (source:), because, funnily enough, Harlowe will immediately re-render it. You can use this macro instead. This can be helpful when you're debugging a story and wish to have a complicated expression's value constantly in view, especially in a "debug-footer" tagged passage.

Details:

For more details about the particulars of the source code generated by (source:), see that macro's article. Note that, as with that macro, commands created by custom macros (via the (output:) macro) cannot be converted to source - attempting to do so will produce an error.

See also:

(verbatim-print:), (source:)

The (history: ) macro

(history: [Lambda]) Array

This returns an array containing the string names of all of the passages the player has visited up to now, in the order that the player visited them. An optional lambda can filter the passages, by checking the (passage:) datamap of each. The (mock-visits:) macro can, during debugging, artifically add values to this array to simulate having visited various passages.

Example usage:

Rationale:

Often, you may find yourself using "flag" variables to keep track of whether the player has visited a certain passage in the past. In some cases, you can use (history:), along with data structure operators, such as the contains operator, to obviate this necessity.

Details:

The array includes duplicate names if the player has visited a passage more than once, or visited the same passage two or more turns in a row.

This does not include the name of the current passage the player is visiting.

This macro can optionally be given a where lambda, which is used to only include passage names from the returned array if they match the lambda. Note that even though this produces an array of strings, the variable in the lambda is always a datamap - the same datamap as would be returned by (passage:) for that passage name. That datamap contains these values:

Name Value
source The source markup of the passage, exactly as you entered it in the Twine editor
name The string name of this passage.
tags An array of strings, which are the tags you gave to this passage.
storylet If a (storylet:) call is in the passage, this holds the lambda for that call. Otherwise, it's absent.
exclusivity The storylet exclusivity number. Usually only present if an (exclusivity:) call is in the passage.
urgency The storylet urgency number. Usually only present if an (urgency:) call is in the passage.

So, you can think of (history: where its tags contains "Forest") as a shorthand for (find: _name where (passage: _name)'s tags contains "Forest", ...(history:)), which takes the normal (history:) array, and finds only those names for passages whose tags contain "Forest".

If you're testing your story in debug mode using (mock-visits:), then each of the "mock" visits you simulate using that macro will be added to the front of the returned array (if they match the passed-in lambda). (mock-visits:"A","B") will cause (history:) to produce an array starting with "A","B", followed by passages the tester has actually visited on this playthrough. It will also cause (history: where its name contains "A") to produce an array starting with "A".

See also:

(passage:), (savedgames:), (passages:)

The (metadata: ) macro

(metadata: [...Any]) Metadata

When placed in a passage, this adds the given names and values to the (passage:) datamap for this passage.

Example usage:

Rationale:

While the (passage:) and (passages:) datamaps can provide the tags, name and source code of your story's passages by default, there are many cases when you need more specific data than just strings, such as a number or a changer. An example is when making links for passages that have been chosen non-deterministically, such as by (open-storylets:) - you may want the link to be accompanied with a short description fitting the passage, or you may want each passage to have a random chance of not appearing at all. Moreover, you want to be able to write this information inside the passage itself, as you write it, just as tags are written on the passage as you write it.

The (metadata:) macro provides this functionality - it augments the (passage:) datamap for the current passage, adding extra data of your choosing to it, as if by adding a (dm:) to it at startup.

Details:

The data names and values are provided to (metadata:) as if it was a (dm:) macro call – first the string names, then the values, in alternation.

Being a metadata macro, a (metadata:) macro call must appear in the passage before every other non-metadata macro in the passage, such as (set:) and (if:). (This doesn't include macros inside a "header" tagged passage.) The recommended place to put it is at the top of the passage.

Every passage's (metadata:) macro is run just once, at startup. If an error occurs while doing so (for instance, if a value is given without a matching name) then a dialog box will appear at startup, displaying the error.

Since passages already have "source", "name" and "tags" data names in their datamap, trying to use these names in a (metadata:) macro will produce an error.

Putting this in a "header", "startup" or "footer" tagged passage will NOT cause this metadata to be applied to every passage, much as how adding extra tags to a "header", "startup" or "footer" tagged passage will not cause those tags to apply to every passage.

See also:

(passage:), (passages:), (storylet:)

The (passage: ) macro

(passage: [String]) Datamap

When given a passage string name, this provides a datamap containing information about that passage. If no name was provided, then it provides information about the current passage.

Example usage:

(passage:"Cellar")

Rationale:

There are times when you wish to examine the data of the story as it is running - for instance, checking what tag a certain passage has, and performing some special behaviour as a result. In particular, checking what data the current passage has can be very useful in a header tagged passage, or in a (display:)ed passage. This macro, as well as its counterpart (passages:), provides that functionality.

Details:

The datamap contains the following names and values.

Name Value
source The source markup of the passage, exactly as you entered it in the Twine editor
name The string name of this passage.
tags An array of strings, which are the tags you gave to this passage.
storylet If a (storylet:) call is in the passage, this holds the lambda for that call. Otherwise, it's absent.
exclusivity The storylet exclusivity number. Usually only present if an (exclusivity:) call is in the passage.
urgency The storylet urgency number. Usually only present if an (urgency:) call is in the passage.

However, if the passage contained a (metadata:) macro call, then the data provided to that macro call will be added to that passage's datamap. So, you can have any amount of author-defined data in it as well.

The "source" value, like all strings, can be printed using (print:). Be warned that printing the source of the current passage, while inside of it, may lead to an infinite regress.

Interestingly, the construction (print: (passage: "Cellar")'s source) is essentially identical in function (albeit longer to write) to (display: "Cellar").

See also:

(history:), (passages:), (metadata:)

The (passages: ) macro

(passages: [Lambda]) Array

This returns an array containing datamaps of information for the passages in the story, sorted by passage name, and using the optional search test to only include certain types of passages.

Example usage:

(passages: where its name contains "Fight") produces an array of datamaps for passages in the story that contain "Fight" in their name.

Rationale:

There are times when you wish to examine the data of the story as it is running - for instance, checking which of the story's passages has a certain tag, or a certain word in its source. While you could manually write an array of such passages' data yourself and include them as an array, it is usually easier to use this macro (or the (passage:) macro) to produce such an array automatically.

Details:

The datamaps for each passage resemble those returned by (passage:). They initially contain the following names and values.

Name Value
name The string name of the passage.
source The source markup of the passage, exactly as you entered it in the Twine editor
tags An array of strings, which are the tags you gave to the passage.
storylet If a (storylet:) call is in the passage, this holds the lambda for that call. Otherwise, it's absent.
exclusivity The storylet exclusivity number. Usually only present if an (exclusivity:) call is in the passage.
urgency The storylet urgency number. Usually only present if an (urgency:) call is in the passage.

However, if the passage contained a (metadata:) macro call, then the data provided to that macro call will be added to that passage's datamap. So, you can have any amount of author-defined data in it as well.

If no passage matches the lambda, then the array will be empty.

If you wish to take the array of passages and reduce them to just their names, the (altered:) macro can be used. For instance, (altered: via its name, ...(passages:)) produces an array of every passage's name.

See also:

(history:), (passage:), (metadata:)

(cycling-link: [Bind], ...String) Command

A command that, when evaluated, creates a cycling link - a link which does not go anywhere, but changes its own text to the next in a looping sequence of strings, and sets the optional bound variable to match the string value of the text.

Example usage:

Rationale:

The cycling link is an interaction idiom popularised in Twine 1 which combines the utility of a dial input element with the discovery and visual consistency of a link: the player can typically only discover that this is a cycling link by clicking it, and can then discover the full set of labels by clicking through them. This allows a variety of subtle dramatic and humourous possibilities, and moreover allows the link to sit comfortably among passage prose without standing out as an interface element.

The addition of a variable bound to the link, changing to match whichever text the player finally dialed the link to, allows cycling links to affect subsequent passages, and thus for the link to be just as meaningful in affecting the story's course as any other, even though no hooks and (set:)s can be attached to them.

Details:

This macro accepts two-way binds using the 2bind syntax. These will cause the link to rotate its values to match the current value of the bound variable, if it can - if $pressure is "Medium" when entering the passage with (cycling-link: 2bind $pressure, "Low", "Medium", "High"), then it will rotate "Medium" to the front, as if the link had already been clicked once. Also, it will automatically update itself whenever any other macro changes the bound variable. However, if the variable no longer matches any of the link's strings, then it won't update - for instance, if the variable becomes "It's Gonna Blow", then a cycling link with the strings "Low", "Medium" and "High" won't update.

If one of the strings is empty, such as (cycling-link: "Two eggs", "One egg", ""), then upon reaching the empty string, the link will disappear permanently. If the first string is empty, an error will be produced (because then the link can never appear at all).

If attempting to render one string produces an error, such as (cycling-link: "Goose", "(print: 2 + 'foo')"), then the error will only appear once the link cycles to that string.

The bound variable will be set to the first value as soon as the cycling link is displayed - so, even if the player doesn't interact with the link at all, the variable will still have the intended value.

If the bound variable has already been given a type restriction (such as by (set:num-type $candy)), then, if that type isn't string or str, an error will result.

If you use (replace:) to alter the text inside a (cycling-link:), such as (cycling-link: bind $tattoo, "Star", "Feather")(replace:"Star")[Claw], then the link's text will be changed, but the value assigned to the bound variable will not - $tattoo will still be "Star", and clicking the link twice will return the link's text to "Star". This differs from (dropdown:)'s behaviour in this situation.

If only one string was given to this macro, an error will be produced.

(seq-link: [Bind], ...String) Command

Also known as: (sequence-link:)

A command that creates a link that does not go anywhere, but changes its own text to the next in a sequence of strings, becoming plain text once the final string is reached, and setting the optional bound variable to match the text at all times.

Example usage:

Rationale:

This is a variation of the (cycling-link:) command macro that does not cycle - for more information about that macro, see its corresponding article. This is a simpler macro, being simply a link that changes when clicked without looping, albeit less useful as a means of obtaining the player's input.

While it's possible to produce this effect by simply using (link:) and nesting it, such as by (link:"We nodded,")[(link:"turned,")[and departed, not a word spoken]], this macro is much more convenient to write when you wish to use a large amount of link labels. Additionally, this macro allows a bound variable to keep track of which string the player viewed last, as with (cycling-link:), which would be slightly more complicated to track using (link:) and (set:).

Details:

If one of the strings is empty, such as (seq-link: "Two eggs", "One egg", ""), then upon reaching the empty string, the link will disappear permanently. If the first string is empty, an error will be produced (because then the link can never appear at all).

If attempting to render one string produces an error, such as (seq-link: "Goose", "(print: 2 + 'foo')"), then the error will only appear once the link cycles to that string.

The bound variable will be set to the first value as soon as the sequence link is displayed - so, even if the player doesn't interact with the link at all, the variable will still have the intended value.

If the bound variable has already been given a type restriction (such as by (set:num-type $candy)), then, if that type isn't string or str, an error will result.

If you use (replace:) to alter the text inside a (seq-link:), such as (seq-link: bind $candy, "Two candies", "One candy", "Some wrappers")(replace:"Two")[Five], then the link's text will be changed, but the value assigned to the bound variable will not - $candy will still be "Two candies" until the link is clicked.

If only one string was given to this macro, an error will be produced.

The (input-box: ) macro

(input-box: [Bind], String, [Number], [String]) Command

A command macro that creates a text input box of the given position, width and height, allowing the player to input any amount of text, which can optionally be automatically stored in a variable.

Example usage:

Rationale:

While there are other means of accepting player text input into the story, such as the (prompt:) macro, you may desire an input region that is integrated more naturally into the passage's visual design, and which allows a greater quantity of text to be inputted. This macro offers that functionality.

Details:

Most of the values you can give to this macro are optional. The only mandatory value is the sizing line, which is the same kind of line given to (box:) - a sequence of zero or more = signs, then a sequence of characters (preferably "X"), then zero or more = signs. Think of this string as a visual depiction of the box's horizontal proportions - the = signs are the space to the left and right, and the characters in the middle are the box itself. Also, to avoid ambiguity with the second string given to this macro, a string representing 100% width (no margins) must be a single character, such as just "X".

The optional number, which must come directly after the sizing line, is a height, in text lines. If this is absent, the box will be sized to 3 lines. Note, however, that this macro creates a <textarea> element, which, in some browsers, can be dynamically resized by the reader by clicking and dragging the lower-right corner - so your passage's layout should take into account the possibility of the input box changing size dramatically.

This macro accepts two-way binds using the 2bind syntax. These will cause the box's contents to always match the current value of the bound variable, and automatically update itself whenever any other macro changes it. However, if the variable no longer contains a string, then it won't update - for instance, if the variable becomes the number 23, the box won't update.

If the bound variable isn't two-way, the variable will be set to the box's contents as soon as it is displayed - so, it will become the optional initial text string, or, if it wasn't given, an empty string.

If the bound variable has already been given a type restriction (such as by (set:num-type $candy)), then, if that type isn't string or str, an error will result.

The optional initial text string given to this macro will not be parsed as markup, but inserted into the box verbatim - so, giving "''CURRENT SAVINGS'': $lifeSavings" will not cause the $lifeSavings variable's contents to be printed into the box, nor will "CURRENT SAVINGS" be in boldface.

A note about player-submitted strings: because most string-printing functionality in Harlowe (the (print:) macro, and putting variable names in bare passage prose) will attempt to render markup inside the strings, a player may cause disaster for your story by placing Harlowe markup inside an (input-box:) bound variable, which, when displayed, produces either an error or some effect that undermines the story. In order to display those strings safely, you may use either the verbatim markup, the (verbatim:) changer, or (verbatim-print:).

See also:

(force-input-box:), (prompt:)

The (force-input-box: ) macro

(force-input-box: [Bind], String, [Number], String) Command

A command macro that creates an empty text input box of the given position, width and height, which appears to offer the player a means to input text, but instead replaces every keypress inside it with characters from a pre-set string that's relevant to the story.

Example usage:

Rationale:

There are times when, for narrative reasons, you want the player, in the role of a character, to type text into a diegetic textbox, or make a seemingly "spontaneous" dialogue reply, but are unable to actually permit the player to type anything they want, as the story you're telling calls for specific dialogue or text at this point. While you could simply offer a "pretend" textbox using the (box:) macro, that can't actually be typed into, this macro offers an interesting and unexpected alternative: a textbox that tricks the player into thinking they can type anything, only to change the text to fit your narrative letter-by-letter as they type it.

This interface element has very potent and unsettling symbolism - the player suddenly being unable to trust their own keyboard to relay their words gives a strong feeling of unreality and loss of control, and as such, it is advised that, unless you wish to leverage that symbolism for horror purposes, you should perhaps prepare the player for this element's eccentricity with some accompanying text. Besides that, giving the player the tactile sense of typing words can help them occupy the role of their viewpoint character in situations where it's called for, such as a story revolving around text messaging or chat clients.

Details:

Unlike (input-box:), the final string is mandatory, as it holds the text that the input box will contain as the player "types" it in.

The first string you give to this macro is a "sizing line" identical to that accepted by (box:) and (input-box:) - consult their documentation for more information about those lines.

The optional number, which must come directly after the sizing line, is a height, in text lines. If this is absent, the box will be sized to 3 lines. Note, however, that this macro creates a <textarea> element, which, in some browsers, can be dynamically resized by the reader by clicking and dragging the lower-right corner - so your passage's layout should take into account the possibility of the input box changing size dramatically.

Because you already know what the text in the box will become, you may feel there's no need to have a bound variable. However, you might wish to bind a temporary variable, and then check using a live macro when that variable has become filled with the full string, thus indicating that the player has read it. Otherwise, there is no mechanism to ensure that the player actually type out the entire string.

If the bound variable is two-way, and it contains a string, then, when the input box appears, a number of fixed text characters equal to the string's length will be inserted into the input box automatically, and then the variable will update to match. Otherwise, if the bound variable is one-way, the variable will simply become an empty string (and then be updated to match the box's contents whenever the player "types" into it).

See also:

(input-box:), (prompt:), (box:)

The (checkbox: ) macro

(checkbox: Bind, String) Command

A command that creates a checkbox input, which sets the given bound variable to true or false, depending on its state.

Example usage:

Rationale:

This command uses the common web page checkbox input to let you ask the player for their preference on an auxiliary or metatextual feature or decision. Unlike the (confirm:) command, this doesn't directly ask the player a yes/no question, but simply presents a phrase or option that they can opt into or out of. Thus, it is useful for offering choices that aren't directly "in-character" or diegetic in the narrative - though, you could still use it for that purpose if the clinical nature of a checkbox was especially fitting for the setting.

Details:

This macro accepts two-way binds using the 2bind syntax. These will cause the checkbox's contents to always match the current value of the bound variable, and automatically update itself whenever any other macro changes it. However, if the variable no longer contains a boolean, then it won't update - for instance, if the variable becomes the number 23, the checkbox won't update.

If the bound variable isn't two-way, the checkbox will be unchecked when it appears, and the variable will be set to false as soon as it is displayed.

If the bound variable has already been given a type restriction (such as by (set:num-type $candy)), then, if that type isn't string or str, an error will result.

If the label string is empty, an error will result.

See also:

(dropdown:), (input-box:), (confirm:)

The (checkbox-fullscreen: ) macro

(checkbox-fullscreen: String) Command

A command that creates a checkbox input, which toggles the browser's fullscreen mode and windowed mode. The checkbox will automatically update to match the browser's fullscreen status. If fullscreen mode cannot be entered, the checkbox will be disabled.

Example usage:

(checkbox-fullscreen: "Fullscreen mode")

Rationale:

Modern browsers allow web pages to take up the entire screen of the user, in a manner similar to desktop games. This feature can be useful for immersive or moody stories, such as horror stories, that wish to immerse the player's senses in a certain colour or shade, or to display impactful text that doesn't have to compete for attention from any other screen elements. While it can be more convenient to place an (icon-fullscreen:) in your story's sidebar, this macro can be useful if you remove or replace the sidebar with something else, and can serve as an alternative means of activating fullscreen mode.

Details:

When activated, this will make the page's <html> element be the fullscreen element, not <tw-story>. This is because, in most browsers, removing the fullscreen element from the page, however briefly, will deactivate fullscreen mode. Since the (enchant:) macro, when given ?Page, will often need to wrap <tw-story> in another element, those macro calls will deactivate fullscreen mode if <tw-story> was the fullscreen element. So, if you have edited the compiled HTML to add elements before and after it, such as a navigation bar, that will remain visible while the story is in fullscreen mode. Additionally, this means that the Debug Mode panel is still visible when fullscreen mode is activated.

Currently, there is no special functionality or error reporting when the browser reports that fullscreen mode is available, but the attempt to switch to fullscreen mode fails. In that case, the checkbox will simply appear to do nothing.

See also:

(checkbox:), (link-fullscreen:), (icon-fullscreen:)

The (dropdown: ) macro

(dropdown: Bind, ...String) Command

A command that, when evaluated, creates a dropdown menu with the given strings as options. When one option is selected, the bound variable is set to match the string value of the text.

Example usage:

Rationale:

Dropdown menus offer a more esoteric, but visually and functionally unique way of presenting the player with a choice from several options. Compared to other list-selection elements like (cycling-link:)s or lists of links, dropdowns are best used for a long selection of options which should be displayed all together, but would not otherwise easily fit in the screen in full.

While dropdowns, whose use in form UI suggests themes of bureaucracy and utility, may appear best used for "character customisation" screens and other non-narrative purposes, that same imagery can also be a good reason to use them within prose itself - for instance, to present an in-story bureaucratic form or machine control panel.

Details:

This macro accepts two-way binds using the 2bind syntax. These will cause the dropdown to always match the current value of the bound variable, if it can. Also, it will automatically update itself whenever any other macro changes the bound variable. However, if the variable no longer matches any of the dropdown's strings, then it won't update - for instance, if the variable becomes "Peasant", then a dropdown with the strings "Duke", "King" and "Emperor" won't update.

Note that unlike (cycling-link:), another command that uses bound variables, the bound variable is mandatory here.

Also note that unlike (cycling-link:), empty strings can be given. These instead create separator elements, which are rendered as unselectable horizontal lines that separate groups of options. Having empty strings as the first or last elements, however, will result in an error (as these can't meaningfully separate one group from another).

The first element in a (dropdown:) will always be the one initially displayed and selected - and thus, the one that is immediately set into the bound variable.

If you use (replace:) to alter the text inside a (dropdown:), such as (dropdown: bind $tattoo, "Star", "Feather")(replace:"Star")[Claw], then the option text and the value assigned to the bound variable will change - but only when the player next interacts with the dropdown. $tattoo will be "Star" until a new option is selected, whereupon it will become either "Claw" or "Feather" depending on which was picked.

Harlowe markup inside (dropdown:) labels will be ignored - the text will be treated as if the markup wasn't present. For instance, (dropdown: bind $mode, "//Stealth//", "//Speed//") will produce a dropdown with "Stealth" and "Speed", with the italic styling markup removed.

See also:

(cycling-link:), (checkbox:)

The (meter: ) macro

(meter: Bind, Number, String, [String], [Colour or Gradient]) Command

A command that creates a horizontal bar-graph meter, showing the current value of a number variable, relative to a maximum value, and updating it whenever that variable changes.

Example usage:

Rationale:

For those making number-heavy games, presenting those numbers in an immediately recognisable fashion can be essential to a smooth game experience - and there are times when simply stating the numbers in the prose isn't as direct as you'd like. The standard videogame UI meter, a bar that fills with a colour to represent an important value, is a visual idiom familiar to many people. In addition to their familiarity, meters have important semantic value, too - simply by graphically presenting a value in a meter, a player can immediately get a sense of how important to their play session that value is, as well as understand what numeric range that value should occupy during play.

Details:

The meter will graph the value of the bound variable, from 0 to the given maximum value number (which must be positive). For instance, if that number is 20, then if the bound variable is 5, the meter bar will be 25% full.

The meter is a "box" element, which takes up the full width of the passage or hook in which it's contained. Placing it inside column markup can allow you to place text alongside it, or other (meter:) commands, if you so desire.

The first string you give to this macro is a "sizing line" identical to that accepted by (box:) and (input-box:) - consult their documentation for more information about those lines. However, the sizing line also determines the direction that the meter's bar fills. If the meter is left-aligned or occupies the full width (by being given "X" as a sizing string), the bar fills from left (empty) to right (full), and the opposite is true for right-alignment. Centre-alignment causes the bar to fill from the centre, expanding outward in both directions.

The second, optional string is a label that is placed inside the meter, on top of the bar. This text is aligned in the same direction as the meter itself. Whenever the meter updates, the label is also re-rendered.

Either a colour or a gradient can be given as the final value, with which to colour the bar. If neither is given, the bar will be a simple gray. If a gradient is given, and it isn't a (stripes:) gradient, its rotation will be automatically changed to 90 degrees, with the leftmost colour (at colour stop 0) being placed at the "empty" end of the meter, and the rightmost colour (at colour stop 1) placed at the "full" end. If the bar is center-aligned, then the gradient will be modified, with both ends of the graph having the leftmost colour, and the center having the rightmost colour.

The meter is exclusively horizontal, and cannot be rotated unless you attach (text-rotate:) to it.

Note: In Internet Explorer 10, the vertical height of the meter may be lower than as drawn in other browsers. This is due to a CSS limitation in that browser.

See also:

(icon-counter:)

(link: String, [Changer]) Changer

Also known as: (link-replace:)

When attached to a hook, this replaces the hook with a link that has the given text. The link, when clicked, vanishes and reveals the hook. An optional changer can be given to alter the style of the link (instead of altering the style of the attached hook).

Example usage:

Rationale:

As you're aware, links are what the player uses to traverse your story. However, links can also be used to simply display text or run macros inside hooks. Just attach the (link:) macro to a hook, and the entire hook will not run or appear at all until the player clicks the link.

Note that this particular macro's links disappear when they are clicked - if you want their words to remain in the text, or for only a small portion of the text to disappear, consider using (link-reveal:).

Details:

This creates a link which is visually indistinguishable from normal passage links. However, a changer can optionally be given, after the string, to change the appearance of the link. This must be a changer that would be accepted by (enchant-in:) or (link-style:) - which is to say, (link:), (replace:), (append-with:), or any of their relatives cannot be given, or else an error will result. Note that if you wish to apply a changer to every link in the passage (or, via the use of a 'header' or 'footer' tagged passage, every link in the story), then you can simply use (enchant:) with ?Link instead.

The created link is displayed in place of the hook's contents, and is exempt from all changers that would normally apply to the hook. This means that changers like (text-colour:), added to this changer, will ONLY apply to the hook once it's revealed, and not the link itself. To apply changers to just the link, consider wrapping it in a hook itself and using (link-style:), such as by the following:

(link-style: (text-colour:red))[(link:"Hands stained red")[Hands pure and dry :)]]

Alternatively, you can just use (enchant:) with ?Link to enchant every link with the same changer.

See also:

(link-reveal:), (link-rerun:), (link-repeat:), (link-goto:), (click:), (more:)

(link-reveal: String, [Changer]) Changer

When attached to a hook, this replaces the hook with a link that has the given text. The link, when clicked, reveals the hook and becomes plain, unstyled text. An optional changer can be given to alter the style of the link (instead of altering the style of the attached hook).

Example usage:

(link-reveal: "Heart")[broken] will create a link reading "Heart" which, when clicked, changes to plain text, and shows "broken" after it.

Rationale:

This is similar to (link:), but allows the text of the link to remain in the passage after it is clicked. It allows key words and phrases in the passage to expand and reveal more text after themselves. Simply attach it to a hook, and the hook will only be revealed when the link is clicked.

Details:

This creates a link which is visually indistinguishable from normal passage links. However, a changer can optionally be given, after the string, to change the appearance of the link. This must be a changer that would be accepted by (enchant-in:) or (link-style:) - which is to say, (link:), (replace:), (append-with:), or any of their relatives cannot be given, or else an error will result. Note that if you wish to apply a changer to every link in the passage (or, via the use of a 'header' or 'footer' tagged passage, every link in the story), then you can simply use (enchant:) with ?Link instead.

The created link is displayed in place of the hook's contents, and is exempt from all changers that would normally apply to the hook. This means that changers like (text-colour:), added to this changer, will ONLY apply to the hook once it's revealed, and not the link itself. To apply changers to just the link, consider wrapping it in a hook itself and using (link-style:), or just using (enchant:) with ?Link to enchant every link.

If the link text contains formatting syntax, such as "bold", then it will be retained when the link is demoted to text.

See also:

(link:), (link-rerun:), (link-repeat:), (link-goto:), (click:), (more:)

(link-repeat: String, [Changer]) Changer

When attached to a hook, this replaces the hook with a link that has the given text. The link, when clicked, reveals the hook. Further clicks will cause the hook to repeat itself - a copy of the hook's code will be run, and the result appended to it, in a manner similar to (for:). An optional changer can be given to alter the style of the link (instead of altering the style of the attached hook).

Example usage:

Rationale:

This is similar to (link:), but allows the created link to remain in the passage after it is clicked. It can be used to make a link that fills with increasingly more text after each click, possibly conveying a sense of powerlessness or desperation.

This macro is part of a pair with (link-rerun:) - the latter macro will empty the hook each time the link is clicked. This one should be used if you'd prefer the hook to retain each of its past runs.

The created link is displayed in place of the hook's contents, and is exempt from all changers that would normally apply to the hook. This means that changers like (text-colour:), added to this changer, will ONLY apply to the hook once it's revealed, and not the link itself. To apply changers to just the link, consider wrapping it in a hook itself and using (link-style:), or just using (enchant:) with ?Link to enchant every link.

Details:

This creates a link which is visually indistinguishable from normal passage links. However, a changer can optionally be given, after the string, to change the appearance of the link. This must be a changer that would be accepted by (enchant-in:) or (link-style:) - which is to say, (link:), (replace:), (append-with:), or any of their relatives cannot be given, or else an error will result. Note that if you wish to apply a changer to every link in the passage (or, via the use of a 'header' or 'footer' tagged passage, every link in the story), then you can simply use (enchant:) with ?Link instead.

See also:

(link-rerun:), (link-reveal:), (link:), (link-goto:), (click:)

(link-rerun: String, [Changer]) Changer

When attached to a hook, this replaces the hook with a link that has the given text. The link, when clicked, reveals the hook. Further clicks will cause the hook to rerun itself, as if by the effect of (rerun:). An optional changer can be given to alter the style of the link (instead of altering the style of the attached hook).

Example usage:

Rationale:

This is similar to (link:), but allows the created link to remain in the passage after it is clicked. It can be used to make a link which displays a slightly varying run of prose over and over, or a link which must be clicked multiple times before something can happen (using (set:) and (if:) to keep count of the number of clicks).

This macro is part of a pair with (link-repeat:) - the latter macro will append each run of the hook, so that text gradually accumulates within it. This one should be used if you'd prefer the hook to remain at a certain size, or need it to always naturally flow from the link text.

The created link is displayed in place of the hook's contents, and is exempt from all changers that would normally apply to the hook. This means that changers like (text-colour:), added to this changer, will ONLY apply to the hook once it's revealed, and not the link itself. To apply changers to just the link, consider wrapping it in a hook itself and using (enchant-in:) with ?Link, or just using (enchant:) with ?Link to enchant every link.

Details:

This creates a link which is visually indistinguishable from normal passage links. However, a changer can optionally be given, after the string, to change the appearance of the link. This must be a changer that would be accepted by (enchant-in:) or (link-style:) - which is to say, (link:), (replace:), (append-with:), or any of their relatives cannot be given, or else an error will result. Note that if you wish to apply a changer to every link in the passage (or, via the use of a 'header' or 'footer' tagged passage, every link in the story), then you can simply use (enchant:) with ?Link instead.

See also:

(link-repeat:), (link-reveal:), (link:), (link-goto:), (click:)

(link-goto: String, [String]) Command

Takes a string of link text, and an optional destination passage name, and makes a command to create a link that takes the player to another passage. The link functions identically to a standard link.

Example usage:

Rationale:

This macro serves as an alternative to the standard link syntax ([[Link text->Destination]]), and has a key difference: The link syntax lets you supply a fixed text string for the link, and a markup expression for the destination passage's name. (link-goto:) also allows the link text to be any expression - so, something like (link-goto: "Move " + $name + "to the cellar", "Cellar") can be written.

Details:

If the passage name doesn't correspond to any existing passage, a broken link (a red link that can't be clicked) will be created.

The resulting command from this macro, like all commands, can be saved and used elsewhere. If you have a complicated link you need to use in several passages, you could (set:) it to a variable and use that variable in its place.

As a bit of trivia... the Harlowe engine actually converts all standard links into (link-goto:) macro calls internally - the link syntax is, essentially, a syntactic shorthand for (link-goto:).

Note that (link-goto:), unlike (link:), doesn't accept a changer value to style the produced link. This is because, as this produces a command (and not a changer), you can simply attach changers to the front of it to style the link.

See also:

(link:), (link-reveal:), (link-undo:), (goto:)

(link-reveal-goto: String, [String], [Changer]) Changer

This is a convenient combination of the (link-reveal:) and (go-to:) macros, designed to let you run commands like (set:) just before going to another passage. The first string is the link text, and the second is the passage name. An optional changer, with which to style the link, can also be provided.

Example usage: