Using Passages visited as countdown

Twine Version: 2.3.8
Story Format: Sugarcube 2.31.1

Hello all.
I have a very beginner’s knowledge on Twine/Sugarcube (or general, in fact) programming and I wanted some help with implementing a system.

I’m working on a game with an exploration focus. The navigation system is working well, with each room being a different passage. What I wanted to ask help with is: is it possible to trigger an event that will only “fire” after walking through X rooms?

Situation 1: You find a fountain and drink from it (setpoison1 to true), but nothing happens at first. Then you walk through 3 rooms and oops, -50 HP (with either a “Dialog popup” or an automatic redirection to a passage saying how “all of a sudden you feel very ill” and such).

Situation 2 (if they are exclusive, this one would be priority): You find a fountain and drink from it (setpoison2 to true), but nothing happens at first. Then, for every 3 to 5 rooms you move, -10 HP, again with either the Dialog popup or forced redirection to give player a feedback, permanently - until you drink the antidote and setpoison2 to false.

Also, two complications: Is it possible to mark which passage should and should not be considered on the countdown (maybe with tags) to avoid passages like Inventory and Character profile being counted?

And the second: to “force” the event to fire manually? I have a simple Time Passage system controled by a variable “set $daytime to 1” (morning) and certain actions advance time ($daytime += 1). Can it be made that, if time advances while under the “Poisoned” effect, immediately trigger the damage event and (in Situation 2) restarts the countdown?

Thank you all in advance.

I will assume that you have assigned a known Passage Tag (like room) to each of the Passages that constitute a ‘Room’ within your project. The following examples are written using TWEE Notation.

You can use the visitedTags() function to determine how many times the Reader have visited a Passage that has been assigned a specific Passage Tag.
eg. visitedTags("room") would return the number of times the Reader has visited passages that have been assigned the room Passage Tag.

:: Fountain Room [room]
This room has a fountain in it.
<<linkreplace "Drink from fountain">>
	\You drink from the fountain.
	\<<set $setpoison1 to true>>
	\<<set $visitedRooms to visitedTags("room")>>
\<</linkreplace>>
[[Continue Exploring|Another Room]]

You can use a known Story Variable to track how many ‘rooms’ had been visited at the time the Reader drunk from the fountain, and then compare the return the difference between that Story Variable and the current count returned by visitedTags("room") until it equals 3 (or whatever other count is required!).

<<if def $visitedRooms and (visitedTags("room") - $visitedRooms) is 3 >>Third Room! <<unset $visitedRooms>><</if>>

So the question is where in your project to do the above check? One possible answer is within either the contents of a PassageHeader or PassageFooter special Passage, depending on whether you want the check to occur before the processing of the ‘current’ Passage or after. And because you want that check to only occur when the Reader visits another ‘room’ then you will likely need to use the tags() function to determine if the ‘current’ passage is a ‘room’

:: PassageFooter
<<if tags().includes("room")>>
	<<if def $visitedRooms and (visitedTags("room") - $visitedRooms) is 3 >>
		\Third Room!
		\<<unset $visitedRooms>>
	<</if>>
<</if>>

notes: The above code makes use of:

  1. the <Array>.includes() method to determine if the Array returned by the tags() function contains the room Passage Tag.
  2. the def operator of the <<if>> macro to determine if the $visitedRooms variable has been assigned a value yet.
  3. the <<unset>> macro to get rid of the $visitedRooms variable once it is no longer needed.
  4. You can rename the $visitedRooms variable to whatever makes sense to you & your project, just make sure you rename ever instance of the that variable!

Hello Greyelf, how are you? Thank you for the answer.
Unfortunately, I’m only succeeding in making it work partially.

First of all, the 2 last code blocks: you said “either the contents of a PassageHeader or PassageFooter special Passage, depending on whether you want the check to occur before the processing of the ‘current’ Passage or after”, but the last code block specifically says ::PassageFooter. Apologize for the confusion, but it’s one code block in each? Both in the Footer?

Regardless, I tried combinations (currently, one in PassageHeader and the other on PassageFooter) and it’s working well for the first example.
But is there a way to make it recurring, like in the second example, until you find the Antidote and “setpoison1 to false”?
I tried changing the “unset $visitedRooms” to “set $visitedRooms to 0” to create a loop, but didn’t work.

Currently this is what I have on the PassageHeader (and made the similar changes to the PassageFooter):

<<if def $visitedRooms and (visitedTags("navigation") - $visitedRooms)
is 3 >> <<script>> Dialog.setup("Poisoned!", "poison"); 
Dialog.wiki(Story.get("poison1").processText()); Dialog.open(); <</script>>
<<set $visitedRooms to 0>> <</if>>

Thank you.

You decide which of the two special passages you want to use. If you want the check to be done before the contents of the ‘current’ Passage is process then you would use a PassageHeader

:: PassageHeader
<<if tags().includes("room")>>
	<<if def $visitedRooms and (visitedTags("room") - $visitedRooms) is 3 >>
		\Third Room!
		\<<unset $visitedRooms>>
	<</if>>
<</if>>

on the other hand if you want the contents of the ‘current’ Passage to be processed before the check is done then you would use a PassageFooter

:: PassageFooter
<<if tags().includes("room")>>
	<<if def $visitedRooms and (visitedTags("room") - $visitedRooms) is 3 >>
		\Third Room!
		\<<unset $visitedRooms>>
	<</if>>
<</if>>

…you don’t need to use both of the special passages.

You don’t indicate if the fountain mentioned in your two examples is the same fountain, or two different fountains, so I will assume they are two different fountains.

The purpose of the variable is to track how many ‘rooms’ had been visited at the point the drinking action occurs, and that number gets stored in a variable associated with that action.

<<set $drunkFromFountain1 to visitedTags("navigation")>>

eg. If twenty-eight ‘rooms’ had been visited at the time the drinking from the fountain then the above variable would contain 28.

note: If you need to track two different drinking actions then use two different variables to store how many ‘rooms’ had been visited at the time the specific action occurred.

<<set $drunkFromFountain2 to visitedTags("navigation")>>

Now the story needs to check the number returned by the visitedTags("navigation") function until the difference between that number and the one stored in a variable equals the delay you want.

eg. if you want a delay of 3 'rooms before the effects of drinking from fountain 1 is revealed then you want to wait until visitedTags("navigation") returns 31. (31 - 28 = 3)

(visitedTags("navigation") - $drunkFromFountain1) is 3

eg. if you want a delay of 5 'rooms before the effects of drinking from fountain 2 is revealed, and the drinking action happened when 34 ‘rooms’ had been visited then you want to wait until visitedTags("navigation") returns 39. (39 - 34 = 5)

(visitedTags("navigation") - $drunkFromFountain2) is 5

note: Again the variable names you use can be whatever makes sense to you & your project, you don’t have to name the variable(s) $visitedRooms or $drunkFromFountain1 or $drunkFromFountain2

Hello Greyelf.
Thank you for the clarifications.

The codes are working well, both variations with the different amount of passages needed for activation. But the second version still only fires once, instead of being a recurring effect until finding the cure.
However, I managed to make it work: I removed the “unset $visitedRooms” from the code and on the “poison1” passage, the one that will be called with the Dialog popup, I have:

[Descriptive text]
<<sethealth -10>>
<<set $visitedRooms to visitedTags("navigation")>>

That resets the countdown without removing it. So every X tagged passages visited, the popup appears, deal damage and starts it over. To remove the effect, there’s the Antidote:

<<linkreplace "Antidote">> You drink the antidote.
   <<set $setpoison1 to false>> <<unset $visitedRooms>>
<</linkreplace>>

With that, everything works out.

There’s just a small “wrinkle”: entering Menu passages like Inventory or Character Profile doesn’t add to the count because they are not tagged, but Returning from them does. So it’s possible to activate the event by being in the Main Hall, entering Inventory and Returning back to the game. I figured using the Return link to reduce in 1 the count of visited rooms could compensate for it, but I couldn’t get the right variable:

<<link "Return" $return>>
<<if $poison1 is true>> <<set $visitedRooms -=1>><<else><</if>>
<</link>>

To be honest, I don’t really mind this. It’s not a big deal. But in case someone else needs a more precise version of this, the Return thing could use addressing.

Once again, thank you Greyelf, for your help.

You would likely need to increase the count of visited rooms in the variable, because you ignore the ‘extra’ visit after returning from a ‘menu’ passage.

<<link "Return" $return>>
<<if $poison1>><<set $visitedRooms +=1>><<else><</if>>
<</link>>

note: You shouldn’t compare variable that contain a Boolean value against the true & false literals. The correct way to test for those states are:

<<if $variable>>The variable current equals true, or a Truthy value.<</if>>
or
<<if not $variable>>The variable current equals false, or a Falsy value.<</if>>

see: JavaScript’s Truthy and Falsy documentation to learn what those states are.