How to link to a random passage with conditions in a footer

Twine Version: 2.3.16
Harlowe 3.2.3

I’m writing a non-linear story and I’d like to read the passages in random order every time I play it.

However, I can’t just list every passage and shuffle the order. The story is gated, so reading certain passages unlocks certain other passages. So, for instance, you have to read Watering Can and Seed before you can read Plant. You have to read Plant and Harvest before you can read Food.

First, in every story passage I inserted a variable to show it was read. For “Watering Can,” it looked like this:

(set: $wateringcan to "read")

Then I created a passage with a “footer” tag and used code like this:

((if: $wateringcan is "read" and $seed is "read" and $plant is not "read") [(set: _list to _list + (array:"Plant"))]
((if: $plant is "read" and $harvest is "read" and $food is not "read") [(set: _list to _list + (array:"Food"))]

I believed that this would create a temporary list that would include every unread passage that I had fulfilled the conditions to unlock, and then I could shuffle that list and go to one of the passages at random.

Then, at the foot of the footer, I added:

(link-goto: "Next", (shuffled: ..._list)'s 1st)

When I test it, I get the error “There isn’t a temp variable named _list in this place.”

I’m confused. Do I have to do something to create the temp variable? Am I adding potential passages to the array correctly?

If you click the arrow next to the error message, you should see this explanation: “Temp variables only exist inside the same passage and hook in which they’re (set:).” What you’re doing involves multiple hooks, so you’re going to need to use a permanent variable.

I assume you wanted to use a temp variable to avoid having multiple copies of the same passage name in the array, but there’s another way around that: using a dataset instead. A dataset is a list of unique values, so if you try to add “Plant” and “Plant” is already there, nothing will happen.

Datasets work pretty much like arrays, so not much needs to change about what you’re doing, but they’re not ordered, so you can’t use the (shuffled:) macro anymore. Luckily you can just use (either:) instead and there should be no functional difference in this case.

So with these changes, your footer code would look something like this:

((if: $wateringcan is "read" and $seed is "read" and $plant is not "read") [(set: $list to $list + (ds: "Plant"))]
((if: $plant is "read" and $harvest is "read" and $food is not "read") [(set: $list to $list + (ds: "Food"))]
(link-goto: "Next", (either: ...$list))

The other thing that you would need to do is manually remove passages from the dataset as they’re visited, which is simple: (set: $list to it - (ds: "Plant")) (e.g.) in the body of the “Plant” passage.

1 Like

E. Joyce has already explained that the error message indicates that you’re trying to access a Temporary variable named _list in a scope in which that variable either doesn’t exist or isn’t available. And they have given one explication on how to over come the situation using a Dataset.

One thing I noticed about your code example is that it doesn’t include where you initialise the _list variable.
eg. To be able to use code like the following…

(set: _list to _list + (array: "Plant"))

…to add a String value of “Plant” to an existing Array contained in the _list variable you first need to assign an (empty) Array object to that variable like so…

(set: _list to (array:))

So if you changed the code in your footer tagged Passage to initialise the _list Array before you try to add items to it then you shouldn’t see that specific error…

(set: _list to (array:))

(if: $wateringcan is "read" and $seed is "read" and $plant is not "read")[
		(set: _list to _list + (array: "Plant"))
(if: $plant is "read" and $harvest is "read" and $food is not "read")[
	(set: _list to _list + (array: "Food"))

(unless: _list's length is 0)[
	(link-goto: "Next", (shuffled: ..._list)'s 1st)

note: I added a check of the Array’s length to the above because:

  • there is no point in shuffling an Array that has not elements in it.
  • you will receive an error if you try to access the 1st element of an empty Array.

Thank you both so much! Greyelf’s solution worked perfectly.