Can I get names of included passage?

regarding to documentation of sugarcube, I can get Passage names with passage() function.
but in included passage, it returns parent’s passage name not of included passage
how can I get the name of included passage?

1 Like

If you’re <<include>>ing a passage, then you must already know the name of the passage in order to include it. Also, you don’t have to use the passage() function for a passage to know its own name, you could just hard-code the passage name into that passage’s code.

If you could explain what you’re actually trying to accomplish by doing this, that would be better, since not all ways to achieve what you’re asking for may help you, depending on what you’re doing with that data.

Also, please note if you’re ever including more than one passage at a time, since that may affect your answer.

The short answer is that there’s no built-in way to do that. SugarCube doesn’t track that information at all. I’ve wanted it for a few things, and I’ve looked through the source code, and it wouldn’t be a minor change.

But as HiEv said, if you explain why you want that info, we might be able to help you choose which of the possible work-arounds is best for you…

oh…
What I want to do is count how many times each passage which have ‘Perform’ tag, has been visited(or run maybe)
And every Perform taged passage will selected in parent 'Command ’ passage and return the result of each ‘Perform’ passage on dialog.

I made decades of passages with specific tag like ‘Perform’ and save it in a variable in storyinit passage

<<set _Per to Story.lookup('tags', 'Perform')>>
<<set $Per to []>>
<<for _i range Object.keys(_Per)>>
	<<set $Per.push(_Per[_i].title)>>
<</for>>
<<set $Char to {Stat:[].....and so on... Per:[]}>>
<<set $Char.Per[0] to []>>
<<set $Char.Per[1] to []>>
<<for _i to 0; _i < $Per.length ; _i++>>
	<<set $Char.Per[0][_i] to $Per[_i]>>
	<<set $Char.Per[1][_i] to 0>>
<</for>>

one of the Perform Passages will be

:: deadlift [Perform]
<<set _now to passage()>>
<<if $Char.Per[1][$Per.indexOf(_now)] > 0 >>
	<<set $Char.[1][$Per.indexOf(_now)] += 1>>
<<else>>
	<<set $Char.[1][$Per.indexOf(_now)] to 1>>
<</if>>

Deadlift!!
count: <<= $Char.Per[1][$Per.indexOf('Deadlift')]>>

but passage()will not work properly, so I instead that all to ‘Deadlift’
and I skipped dialog part because it is too complex with my other codes…

and Command passage will be

<<for _i = 0; _i < $Per.length;_i++>>
	<<capture _i>><<nobr>>
	<<link $Char.Per[0][_i]>>
		<<replace '#Per'>>
			<<include $Char.Per[0][_i]>>
		<</replace>>
	<</link>>
	<</nobr>><</capture>>
<</for>>

<span id='Per'></span>

and some other page, I’ll show $Char.Per variables.

so now,
how can I treat this with <> macro not write every dozens of Perform passages?

I’m still not 100% clear on what you need, but I think this should do the trick.

First, put this in your StoryInit passage:

<<set $PsgCount = {}>>

That will initialize the $PsgCount variable as an empty generic object.

Next, create a passage with “widget” and “nobr” tags, and put this <<count>> widget in it:

<<widget "count">>
	<<if ndef $PsgCount[$args[0]]>>
		<<set $PsgCount[$args[0]] = 1>>
	<<else>>
		<<set $PsgCount[$args[0]]++>>
	<</if>>
<</widget>>

When that widget is called, it will either add a property to the $PsgCount object with the passage’s name and set the count to 1 - or - it will add 1 to the existing count for that passage.

After that, all you need to do is put:

<<count "Passage Name">>

somewhere within every “Perform” passage where it will get executed, with the passage’s name (or whatever phrase you wish to use to track that passage) replacing where it says “Passage Name”.

After you’ve done that, if at any time you want to see the list of all actions performed and the count of how many times each have been performed, you can just do this:

<<for _name, _count range $PsgCount>>
	_name: _count
<</for>>

If you wanted that list sorted by passage name, you could do this instead:

<<set _names = Object.keys($PsgCount).sort()>>
<<for _name range _names>>
	_name: <<= $PsgCount[_name]>>
<</for>>

With the above code, tasks which have never been performed will not be displayed. However, if you want all “Perform” tagged passages to be displayed and initialized to a count of 0, then you could put this in your StoryInit passage instead:

<<set $PsgCount = {}>>
<<set _Per to Story.lookup('tags', 'Perform')>>
<<for _i range Object.keys(_Per)>>
	<<set $PsgCount[_Per[_i].title] = 0>>
<</for>>

(Note: If you use this technique, then the names used when calling the <<count>> widget must match the passage names.)

Please let me know if you have questions on any of the above code.

Hope that helps! :slight_smile:

1 Like

It’s not exactly what I want, but got some insight
thank you

Theoretically, you could include code in “perform” to increase a variable each time it runs. In ASM whenever a remote passage is displayed, all the code in it fires. There’s also an IF VISITED(‘passage’) EQ # function - but I’m not sure if Twine has a built-in analogue.

If you could clarify what you want a bit more then I’ll try to help if you’re still having problems.

My purpose is to make simple code
my insight?
now I know that I should write every passage name on every included passage haha…
thanks for all of your answers

I saw this pop up when searching for a similar answer. The answers here are extremely specific to this situation and involve injecting code into each passage rather than calling “Include” slightly differently. I came up with a generic way to do this that can apply for all situations. So with that said, excuse my variable names and such, I’m just passing through these forums and thought I’d add a robust answer to the idea of the question.

A generic way to do this would be to implement your own wrapper to the <<include [passagename]>> macro.

Something like <<stacked_include "passage name" >> for example.

Within that passage you can store a list of passages, I’m going to type up pseudo code for this.

<<widget "stacked_include">>
<<run 
  [_thePassage] = $args;
  const passage = Story.get(_passageName)
  SomeGlobalArray.passageList.push(passage); // add current passage object to a list --- This is where you can create a generic function that captures the passage information
>>
<<include _thePassage >>
<<run 
  SomeGlobalArray.passageList.pop(); // Remove the current passage
>>
<</widget>>

You will also want to add the first passage to the top of the list when the passage loads.

$(document).on(':passagestart', function (ev) {
  SomeGlobalArray.passageList = []; // Clear the array when a passage starts
  SomeGlobalArray.passageList.push(Story.get(passage())); // add the current passage to the list
});

Now at any point you can grab the last element of that array.

To grab the current passage all you need to do is just grab the last element in the list of passages.
SomeGlobalArray.passageList[SomeGlobalArray.passageList.length -1]

And as a helper to get that last passage:

SomeGlobalArray.getCurrentPassage = () =>{
  return SomeGlobalArray.passageList[SomeGlobalArray.passageList.length -1];

// then you can do things like  SomeGlobalArray.getCurrentPassage().tags, Basically anything you can do with the passage api
}

With that done, and to SPECIFICALLY answer the OP’s question.
All you’d have to do is loop over the list of passages and check for the tag.

I’m kind of baffled at the overcomplexity of your code, while it also seems to fail to accomplish what was originally asked.

I agree that you could simply create a wrapper around the <<include>> macro for this. That could simply be accomplished using my earlier code by changing the “count” widget I made earlier to a “show” widget, simply by inserting an <<include>> like this:

<<widget "show">>
	<<include $args[0]>>
	<<if ndef $PsgCount[$args[0]]>>
		<<set $PsgCount[$args[0]] = 1>>
	<<else>>
		<<set $PsgCount[$args[0]]++>>
	<</if>>
<</widget>>

Now you don’t need to include <<count>> in each perform passage, you just have to use the <<show>> widget to display the perform passages and track them.

However, I didn’t suggest doing that because I assumed that adding <<count>> to their perform passages would be simpler than replacing all of their <<include>> lines which displayed perform passages, due to the reasonable presumption that the number of <<include>>s would be greater than the number of perform passages.

Also, your <<stacked_include>> widget adds an entire passage object to an array and then almost immediately removes it, so all that’s ever going to be in SomeGlobalArray.passageList after the widget is done is the current passage object due to what happens during the :passagestart event. Maybe I’m missing something, but what’s the point of adding and almost immediately removing the data like that? And why store the whole passage object, instead of just the passage name? Also, if they want to get the current passage name, then they can just use the passage() function. And your code resets every time you go to a new passage, so in the end, it isn’t really tracking anything, which is what the OP needed.

Honestly, I feel like I’m missing something, because it doesn’t look like your code actually does anything other than temporarily store the current passage object in an overly complex way.

I do agree that your answer solves the original user’s specific problem. Their title suggests a different idea that lead me to this topic, and your second example of replacing include also is essentially accomplishing what I’d be looking to do but I’m using it to transform various other globals for TemplateApi reasons. (And if my code was simpler in general I could in theory just set a bunch of globals within the widget but a lot of my functions are extracted into pure js).

So for my particular usage, let’s say I have 4 passages with special tags. (Do note that my actual usage is a bit more complicated and I need to know multiple tags)

:: Passage 1[is-blue speaker-john]
:: Passage 2[is-green speaker-lisa]
:: Passage 3[is-yellow speaker-joe]
:: Passage 4[is-pink speaker-jim]

If I’m using a generic function outside of them to fetch the “Color”, If my passage looks like this:

:: Passage 1[is-blue]
 
 Words
<<include "Passage 2">>
<<include "Passage 3">>  (And passage 3 could also:  <<include "Passage 4">>
<<include "Passage 4">>

I have an outside function within ::passageStart that sets certain variables based on the tags.
Calling passage() within passageStart will always return “is-blue” in this example.

:: Passage 1[is-blue]
 
 Words
<<stacked_includes "Passage 2">>
<<stacked_includes"Passage 3">>  (And imagine Passage 3 also includes Passage 4)
<<stacked_includes"Passage 4">>

In the second example, calling SomeGlobalHelper.getCurrentPassage().tags will give me “is-pink”, “is-blue” from within the passage that is currently being displayed.

The reason for the Push and pop is so that the nesting can go beyond 1 level and certain dynamic properties can be set. I’m using the templateApi extensively and with this system ?person can use the tags defined in the passage to deduce who the speaker is in the current passage if it’s different from the parent.

So if I’m in Passage 1->Passage 3->Passage 4 I can still reliably get the tags associated with passage 4 even if I’m using the templateApi.

Does that make sense?

To reiterate my answer is definitely overkill for what you got the origianl poster to describe in detail what they were looking to do! And they’d have to intercept the information instead of pushing to an array.

So instead it’d be more like

<<widget "stacked_include">>
<<run 
  [_thePassage] = $args;
  const passage = Story.get(_passageName)
  SomeGlobalArray.passageList.addPassageToStack(passage); // add current passage object to a list --- This is where you can create a generic function that captures the passage information
>>
<<include _thePassage >>
<<run 
  SomeGlobalArray.passageList.removePassageFromStack(); // Remove the current passage
>>
<</widget>>

Then “AddPassageToStack” will actually do the heavy lifting of storing the information in an array and do the magic behind the scenes, like when templates are used.

OK, yeah, now I see what was missing. You were doing something during that brief interval where other data was in the SomeGlobalArray.passageList, and it required other parts of the passage object.

It all makes sense now. :+1:

1 Like