(Show:) doesn't work unless fired by link?

Please specify version and format if asking for help, or apply optional tags above:
Twine Version: 2
Story Format: Harlowe 3.1


I want to make a link, e.g. [[Pet the cat]] appear at the bottom of a passage describing a bedroom, next to others (eg. [[Go to kitchen]], but only when the player discovers the cat in the room.

In order for the player to discover the cat, they either have to see it as they enter (if the cat’s variable is “visible” for example) or pull some clothes to reveal it (if the cat is “hiding”).

First, I make the bottom link a hidden hook named ‘4’—so far so good:

[[Go to kitchen]]|4)[ | [[Pet the cat]]]

I know how to use (show:) to make the named hook appear when triggered by a link (for example when—cat hiding—the player pulls the clothes):

(if: $cat is "hiding")[You see a strange (link-reveal: "lump")[(show: ?3)] under the clothes on the bed. It looks... breathing. |3)[You pull the clothes and find Jones, the cat.(show: ?4)]]]

In case the cat is already there, though, (show:) doesn’t show the named hook, if called as part of an (if/else:) macro:

(else:)[The cat is asleep on the table.(show: ?4)]

This (else:) hook doesn’t trigger the (show:) macro, since (show:) is not part of a (reveal-link:) or similar macro.

So, is there another macro that can simply make a hidden named hook appear whenever one feels like it?

Well, yes and no! Try this:

Your bed is big and comfortable, but under the bedclothes, there is a mysterious hump.

(click-append: "hump.")[ You pat the lump and Mephistopholes, your ginger tabby shoots out from beneath the bedclothes.

Pet Mephistopholes.]

(click-replace: "Pet Mephistopholes")[You stretch out your hand, but Mephistopholes hisses and flees out the door..]

The thing is that links become active when they are rendered, that is, appear on screen. When a passage appears on screen, that’s when all the if/else logic is run. So if something is shown or hidden by an if/else link it will stay that way until the passage gets re-visited (like the kitchen passage earlier, where a link from the “look through the window” passage points to it. When the player returned to the kitchen passage, all code in that passage would be re-run.)

But if you use a click-append, or click-replace macro, then that will cause the text it contains to be rendered to screen when the player clicks it. If that text contains a phrase that another click-whatever macro is waiting for, then Twine will notice and “activate” it or whatever the phrase is.

As you can see from the example you have to be pernickety about punctuation and spacing if you want to avoid odd effects (look at the way the period in the petting the cat response gets bunted along to form one period of the ellipsis, for example.) But I usually solve that problem by simply doing it wrong multiple times! :slight_smile:

Yeah, I see what you mean. I can definitely work around the problem. :sunglasses: Thanks!

warning: Due to how the (click:) family of macros are implemented (1) it is generally recommended that you use one of the (link:) family of macros instead whenever possible.

(1) The (click:) family of macros need to wait until the contents of the entire page has been generated before each (click:) macro call (contain within that page) can scan that entire page (one by one) looking for its target text or hook. Each dynamic change to that page, like that caused by macros such as (show:) or (replace), can cause that scanning process to be executed again.

So don’t use them too much or the game will slow down?

As I stated earlier, the general recommendation is not to use the (click:) family of macros at all, unless there is no other way to achieve a similar result using one of the (link:) family of macros.

It takes time to scan the HTML structure of the whole page, including the parts that aren’t visible, looking for every possible instance of the macro call’s target. And that scan is done for each and every active (click:) related macro call contained within the ‘current’ Passage being shown. If that page is dynamically altered after it has been shown to the end-user, then that scanning process is done again for all active (click:) macros and while this is occurring the end-user can’t interact with the page.

How visibly noticeable this is to the end-user depends on the number of (click:) macros within the ‘current’ Passage (the initial time it takes to do all the scanning needed), and how often the ‘current’ Passage is dynamically changed after it has been shown (the secondary total scanning time)
eg. (contrived and silly example)

(set: $countdown to 100)\
Count down: |countdown>[$countdown]

The quick brown fox jumps over the lazy |dog>[dog],
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.

	(click: "quick")[(set: $countdown to it - 1)]
	(click: "fox")[(set: $countdown to it - 5)]
	(click: "over")[(set: $countdown to it - 10)]
	(click: ?dog)[(set: $countdown to it - 100)]

	(live: 1s)[
		(if: $countdown <= 0)[
			(set: $countdown to it - 1)
		(replace: ?countdown)[$countdown]

Each time the displayed countdown changes value all the ‘active’ (click:) based links (13 initially) are recreated, which involves the scanning of the whole HTML document 4 times (once for each (click:) macro call). Each time a link is selected then number of active links is decreased but that doesn’t effect the number of scans done, just the number of links that are created as a result of that scanning.


Thanks for the explanation. I guess it’s similar to the situation in I7 where people use “instead” rules rather than check and carry out. Instead is OK for a few instances (and especially small projects while learning) but bogs down when used in any serious sized project.

So thanks again, I think you’ve saved me from making some very bad design decisions!

1 Like