Problem with including widgets passage

Twine Version: 2.3.5
Story Format: SugarCube 2.30.0

I’ve created a widgets passage (called “My Widgets”) that has a few recurring macros. From the SugarCube documentation, it says “Widgets should always be defined within a widget-tagged passage.”

So, I’ve done that, tagging the “My Widgets” passage with “widget” and “nobr” tags (using the GUI “+Tag” button). Then, in the StoryInit passage, I include the line: <<include "My Widgets">>. However, when I run my story, I get an error dialog:

“Apologies! An error has occurred. You may be able to continue, but some parts may not work properly. Error [StoryInit]: <<include>>: passage “My Widgets” does not exist.”

I’ve tried taking the include line out of StoryInit and instead put it in my first self-created passage where the story begins. But it gives a similar error embedded in the web page: “Error: <<include>>: passage “My Widgets” does not exist.”

And funny enough, when I remove the “widget” tag, everything seems to work fine. What am I doing wrong?

Thanks!

Adding a widget tag to a passage makes it like StoryInit: it is automatically included when the game starts. And it doesn’t exist as a normal passage.

So you can do one or the other (include in StoryInit or tag with widget) but not both.

That did the trick! Thanks for the quick response.

Just for clarity, you should not do the <<include>> in StoryInit. You should just set the “widget” tag on the widget’s passage, and then it will work.

Ah, good to know. When I tested it, it seemed to work fine both initially and across saves and loads, so it seemed like a valid technique.

It seems that the [widget] tag might be useful in a Twee2 “library” file that wants to include initialization, even if it doesn’t define any widget macros. Does anyone know why this might be a bad idea?

Only the SugarCube developer, @TheMadExile, could answer that.

I can think of a few unlikely scenarios where it could cause problems, but they’re kind of a stretch.

That said, why do it that way when you could simply use either the JavaScript section or the StoryInit passage instead?

To me, it just seems like a bad idea to use something in a way which it wasn’t intended to be used, when there are already things which are intended to be used for that purpose.

My 2¢ anyways. :stuck_out_tongue:

If I am using SugarCube macros, I think the JavaScript section won’t work. And if this is a Twee2 “library” (intended to be included by the main story), then it shouldn’t define StoryInit, I guess. (What does Twee2 do with multiple passages with the same name? I don’t see this mentioned in the documentation – maybe I should test it out?).

I would advise against it. widget-tagged passages are evaluated before various other parts of the system are finalized because they’re meant to define widgets, not run arbitrary code. You may run into issues, you may not. I wouldn’t chance it.

If you just want to define some story variables, you can do that easily enough from a JavaScript section—whatever that means for the compiler in question.

If you’re defining JavaScript macros, then a JavaScript section would be the place to define them.

As with most Twee compilers, I’d expect it probably overwrites passages with the same name—in order of encountering them.

Also. Twee2 is abandonware, and has been for quite a while now. You’d be better served in the long run by switching to a Twee compiler that (a) is maintained and (b) follows the Twee v3 specification—e.g., Extwee, Tweego.

@TheMadExile Thanks! Didn’t know that twee2 was not the way to go. So now I’m trying tweego. So putting things like initializing of story variables is better in a JavaScript passage than in a widget passage? It seems a little low-level: I guess I would use State.setVar then.

Based on the earlier context, I think that, instead of “macros”, he meant “widgets”. (Which, of course, don’t go in the JavaScript section.)

Well, again, the StoryInit passage is probably where you want to do that. There’s no reason not to use that passage, regardless of how you’re compiling your Twine/SugarCube v2 code.

However, if you did want to set a variable in your JavaScript, it’s usually better to just set it on the State.variables object. Something like:

State.variables.varName = "value";

And that would set $varName to the string “value”.

If you don’t want to type “State.variables” a bunch of times, you can do:

var svars = State.variables;
svars.varName = "value";
svars.varNum = 10;

And that would set $varName to the string “value”, plus set $varNum to the integer value 10.

Hope that helps! :smiley:

Thanks. With regard to StoryInit passage: the problem is that there’s only one of these. If I’m defining a Twine story with multiple parts (as with tweego, or formerly twee2), then each part may need its own initialization. Each part can’t have its own StoryInit passage (or as @TheMadExile says, all but the last are lost). So instead, it’d be nice to have a place to put initialization. I saw the [widget] tag as the closest equivalent to Harlowe’s [startup] tag. It seems that this might work, but it is (apparently) not the recommended place, rather one should write these initializations at a low-level in a JavaScript passage. I suppose this thread is ending up as a feature request for a startup tag in SugarCube.

I’m a bit confused. Since it’s all compiled into one HTML file in the end, why does it matter if it was in multiple parts originally? Just make one StoryInit passage and use it for all parts.

Maybe I’m just not understanding why you think you need to have multiple initialization passages.
 

You don’t need that in SugarCube, you just use the Config.passages.start property to determine which is the starting passage, assuming you’re using Tweego or the like and don’t want the starting passage to be named Start. (Though I’m unclear how that really changes anything.)

Anyways, if I’m missing something here, please clarify.

If I had to guess, I suspect that you have some incorrect underlying assumption here.

That’s right. You’re not understanding. I have a story that is broken up into several parts. Each part wants to have its own initialization. But each part cannot have its own StoryInit passage. In programming, this is called modularity. The idea that all your variables would have to be initialized in one module is not modular.

Yes. The startup tag in Harlowe is a tag on passages that will all be “evaluated” at the beginning of the story. In other words, they are all treated like StoryInit is treated. This is separate from the (configurable) Start passage.

I’m familiar with modular programming, but it doesn’t really apply here in quite the way you’re thinking (or at least, it would be bad design to try to apply it the way you’re apparently describing here due to how Twine works).

The JavaScript section and StoryInit passage should be used to initialize any code or variables which are used throughout the game, especially variables whose contents will never change. Otherwise, all other variables should be created (<<set>>) or destroyed (<<unset>>) on an as-needed basis, and temporary variables (the ones that start with an underscore) should be used whenever possible.

Whether your story is internally broken up into several parts or not doesn’t change that.

The reason why is that, every story variable which you add will get copied into every step of the game’s history, up until the point where the variable is destroyed. Keeping too many variables and/or not getting rid of variables you no longer need will bloat up the game’s history. And the larger the data in your game’s history is, the slower your game will get during saves, loads, and passage transitions. Thus it’s best to minimize the use and size of story variables however possible.

If one “chapter” (or whatever you want to define a module as) starts needing some variables, just define them as needed. There’s no need for a special “initialization section” for each chapter. You can initialize them right in the passage where you first need them.

If you want to make sure that you don’t overwrite a variable which was already initialized, then you can do something like this:

<<if ndef $someVar>>
	<<set $someVar = "initial value">>
<</if>>

That would initialize the variable only if it was currently undefined.

If a chapter could have several “entry points”, then it would be best to make a widget which initializes the chapter’s variables using that technique, and then call that widget at the start of each “entry point” passage in that chapter.

Basically, if you have any variables which are used throughout the game, then those variables aren’t actually “modular” and you should initialize them in the JavaScript section and/or the StoryInit passage. On the other hand, any other story variables, such as ones which are only used once you enter a particular chapter of the game, should simply be initialized when first used and then disposed of when no longer useful.

There is no need or point to having a special “initialization section” for each chapter, and having them will likely make your game run more slowly. If you simply prefer having such sections, just initialize the relevant story variables in the first passage of each chapter, however you will likely be unnecessarily bloating up the game history by doing so.

And, since it’s relevant here, I should mention that you can use the Config.history.maxStates setting to control how many moments in the game history that your game will keep track of. If you expect to use a large number of variables and/or the variables contain lots of strings, then you might want to reduce that setting from the default value of 100 moments of game history, to something more like 10 or 20. That will also help prevent slowdown in your game.

Another trick to shrink the history is, instead of storing data which will never change in story variables, store that data on the SugarCube setup object, which is not saved into the game’s history. For example, in your JavaScript section, you might have:

setup.day = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

and then in any of your passages you could do:

Day $dayNum (<<= setup.day[$dayNum % 7]>>)

(<<=>> is an alias of the <<print>> macro)

Then, if $dayNum was set to 12, that code would display:

Day 12 (Friday)

Since the days of the week won’t change, it’s fine to not store that variable in the game’s history, and thus by using the setup object in this way, it means that you’re storing a smaller amount of data in the game’s history than if you’d used a story variable to store that.

Anyways, hopefully you understand my confusion/concern now, and what I was trying to explain makes sense to you. :smiley:

1 Like

Harlowe’s startup Passage Tag is a just a special variation of the story format’s header tag that only gets appended to the top of the first displayed Passage. So it isn’t the equivalent of SugarCube’s StoryInit special passage, because the contents of that special passage is processed in the background.

Thanks. That’s very useful.

  • I don’t intend to have any saved states, and so now I can see how to avoid having variables saved.
  • Now I know how I can locally initialize using the “ndef” trick.
  • You mentioned this setup context for storing unchanging information. This was what I was using my “module initializations” for mostly. I hadn’t seen setup in the SugarCube documentation (but now that I look, it is there).

Just to be clear, the minimum number of saved states you can have is not zero, but one. That’s required so that the save game function can work. If you set Config.history.maxStates to 0, then that will actually mean that you aren’t limiting the number of stored save states, which is the opposite of what you want.

Anyways, glad I could help. :smiley:

1 Like