Going to random event and repeatable event

Twine Version: 2.36.1

Hi, I’m having some issues with creating a system that allows the player to go to either a random (non-repeatable) event that plucks from an array, and a few passages that I want to be repeatable. Here is the code I’m using.
In StoryInit:

<<set $passages to ["passage 1", "passage 2", "passage 3", "passage 4", "passage 5"]>>\

In passage:

<<if $passages.length > 0>>\
<div class="choice">
<<link "Go to Passage">>\
<<goto `either($passages.pluck(),"repeatable1","repeatable2","repeatable3")`>>
<</link>>\
</div>\
<<else>>\
<div class="choice">[[Go to Passage|No Passage]]</div>\
<</if>>\

It works a bit strange though, when clicking the link it DOES go to a random passage, either from the $passages.pluck array or the 3 repeatable passages, but it seems to remove a passage from the array either way. For example, going to “repeatable1” passage will remove a “passage” from the 5 passages that are in the array, even though it’s not in the array.

Is there any solution to make it so that the repeatable passages are not taken from the non-repeatable array, so that even when the player has gone through all 5 $passages events, they still have the 3 “repeatable” events to do (infinitely)?

Sorry for horrible explanation!

The reason the $passages Array is losing an element each time is that for this function call to be executed…

either($passages.pluck(), "repeatable1", "repeatable2", "repeatable3")

…any expressions (like the $passages.pluck() method call) referenced in its argument list need to be evaluated first. And the <array>.pluck() method removes an element from its associated array.

1: Setup the list of potential unique & repeatable events in the project’s StoryInit special Passage.

<<set $uniqueEvents to ["passage 1", "passage 2", "passage 3", "passage 4", "passage 5"]>>
<<set $repeatableEvents to ["repeatable1","repeatable2","repeatable3"]>>

2: Determine which repeatable or unique Event to transition to.

<<nobr>>
	/* default to transitioning to the no events Passage. */
	<<set _passage to "No Passage">>
    
	/* If unique Events are still available */
	<<if $uniqueEvents.length > 0>>
		<<set _roll to random($repeatableEvents.length)>>
        
		/* repeatable or unique? */
		<<if _roll is $repeatableEvents.length>>
			<<set _passage to $uniqueEvents.pluck()>>
		<<else>>
			<<set _passage to $repeatableEvents[_roll]>>
		<</if>>
	<</if>>
    
	<div class="choice">
    	<<link "Go to Passage" _passage>><</link>>
	</div>
<</nobr>>

The above uses the following features:

  • the <<nobr>> macro, to remove all unwanted HTML <br> line-breaks from the output.
  • a _passage Temporary variable, to track which Passage to transition to when the link is selected.
  • the fact that the random() function returns a value that can include its upped limit (a).
  • Bracket Notation to reference an element of an Array.

(a). JavaScript (thus SugarCube) Arrays use offset (zero) based indices.

So the index of the 1st element will be 0 (zero), the 2nd element will be 1 (one), and so forth. So the index of the last element can be calculated using <array>.length - 1.

eg. if the $repeatableEvents Array contains three elements, then its length will be 3 (three) and the index of the last element will be 2 (two).

So the above random($repeatableEvents.length) function call is the equivalent of random(3) which will return either 0 or 1 or 2 or 3. The first three values being valid indices for the $repeatableEvents Array, and the 4th value being equal to that array’s length.

And the <<if _roll is $repeatableEvents.length>> condition check uses the fact that _roll can potentially equal that length to determine if the value is an index or not.

1 Like

Thank you, it worked perfectly! Actually, with your solution I realised I don’t really need a “No Passage” option at all, do you know how I could remove that so after all the unique events are finished, it just continually rolls the repeatable events?

Simply add an <<else>> condition, and randomly select from the $repeatableEvents Array.

<<nobr>>
	/* If unique Events are still available */
	<<if $uniqueEvents.length > 0>>
		<<set _roll to random($repeatableEvents.length)>>
        
		/* repeatable or unique? */
		<<if _roll is $repeatableEvents.length>>
			<<set _passage to $uniqueEvents.pluck()>>
		<<else>>
			<<set _passage to $repeatableEvents[_roll]>>
		<</if>>
	<<else>>
		/* no unique Events are available, select a repeatable one instead. */
		<<set _passage to $repeatableEvents.random()>>
	<</if>>
    
	<div class="choice">
		<<link "Go to Passage" _passage>><</link>>
	</div>
<</nobr>>