Assigning properties to passages?

Twine Version: 2.5.1
Sugarcube 2.36.1

I’ve noted the handy passage() function which is neat!

But I am wondering if there is any way to assign a property to a passage. I couldn’t find one after a few stabs at the documentation.

What I’d like is to have passage.solved as a boolean state, so if a passage (a location in a world map) is solved, you maybe get a note at the bottom in “helpful” mode.

Also I am curious if this could be expanded to passages having dictionaries e.g.

possible_links[passage_1]) = [ passage_2, passage_3 ].

So that I could simply say “Oh, hey, that passage is solved.”

If this doesn’t happen, I am okay with creating my own dictionaries and finding ways to check the code I will push through TweeGo. It would be a dictionary of strings that had arrays of passages, and it wouldn’t be hard to look for the lines in StoryInit that define what nodes lead where & maybe even verify them against each in-story passage.

But I wanted to check for native stuff I missed before doing any heavy lifting. Thanks!

I don’t think you can assign a property to a passage. The passages are saved in the Sugarcube.State array.

You could create a simple array, and push the passage name when solved:

<<set $solved to []>>>
<<set $solved.push(passage())>>
<<if $solved.includes(passage())>>...
1 Like

I’m fairly certain Sugarcube doesn’t have a built-in way to do this. It is technically possible to add a property to the Passage class with Javascript, but I really wouldn’t mess with existing classes.

1 Like

You should not attempt to modify Passage instances and it wouldn’t be stateful if you did—i.e., your story would forget what you did.

You’ll probably want to use a Map or generic object, preferably one without a prototype, stored within a story variable to track solved passages.

Map example

Initialize the Map in your StoryInit special passage:

<<set $solved to new Map()>>

Adding new entries:

<<run $solved.set(/* solved passage name */, true)>>

/* E.g., */
<<run $solved.set(passage(), true)>>
<<run $solved.set("a passage name", true)>>

Checking entries:

<<if $solved.get(/* solved passage name */)>>
    The specified passage was solved!
<</if>>

<<if not $solved.get(/* solved passage name */)>>
    The specified passage was not solved.
<</if>>

Generic object example

Initialize the generic object in your StoryInit special passage:

/* Create a regular generic object. */
<<set $solved to {}>>

/* Create a prototype-less generic object. */
<<set $solved to Object.create(null)>>

The latter is better when using a generic object as a dictionary, since without a prototype there’s no possibility of key collisions from the prototype chain.

Adding new entries:

<<set $solved[/* solved passage name */] to true>>

/* E.g., */
<<set $solved[passage()] to true>>
<<set $solved["a passage name"] to true>>

Checking entries:

<<if $solved[/* solved passage name */]>>
    The specified passage was solved!
<</if>>

<<if not $solved[/* solved passage name */]>>
    The specified passage was not solved.
<</if>>
3 Likes

Some kind of dictionary would be better for this.

1 Like

Thanks! I"ve used dictionaries before, so I think that can be shoehorned in. It’s not perfect, but it also shouldn’t be too thorny.

The main thing I have to ask for, with what I want, is to make sure the dictionaries don’t have inconsistent values e.g. if passage3 points to passage5 but there is no passage5.

1 Like
<<if Story.get('passage5')>>
Stuff
<<else>>
Other stuff
<</if>>
1 Like

I’m not sure what you’re asking here.

<<if Story.get('passage5')>>
Stuff
<<else>>
Other stuff
<</if>>

You cannot use the Story.get() static method that way. It always returns a Passage instance, so the first clause in that example will always evaluate to true—i.e., the <<else>> is unreachable.

I’m unsure this even relates to what the OP is after, but assuming it is, the Story.has() static method is what would be needed here.

1 Like

ooh ok, my bad.