Using widget links

Twine Version: 2.8.1

Hi All,

I am trying to use widgets for changing game state and generating a status message. I want to embed these in links so they will only fire when the link is clicked.

When I put the following in a widget passage:

<<widget play-red>>
<<set $reds = $reds + 1>>
You play a red card.
<</widget>>

<<widget play-black>>
<<set $blacks = $blacks + 1>>
You play a black card.
<</widget>>

<<widget play>>
	<<link _args[0]>>
		<<set $atext = '<<play-'+_args[0]+'>>'>>
	    <<replace '#action'>>$atext<</replace>>
        <<goto Room>>
	<</link>>
<</widget>>

And using the following passage (named “Room”):

<span id="room">
Red: $reds.
Black: $blacks.
This is a test story for testing widgets and side effects.
Click the <<play red>> link to play a red card.
Click the <<play black>> link to play a black card.
</span>
<hr>
<span id="action">$atext</span>

Whenever I click a link, counters are updated twice instead of once. How can I use a widget within a link to update state and produce some output without side effects?

Feedback is very much appreciated!

It’s your use of <<set $atext = '<<play-'+_args[0]+'>>'>> that is causing the widget to be evaluated twice, once to turn it into a value inside <<set>> and again as get clicked.

There are plenty of ways around it, such as just having one widget to do both colours, with dynamic text, or bundling everything inside <<play>>.

However, the simplest way to stay close to what you have now is:

<<widget play>>
  <<link _args[0]>>
    <<replace '#action'>>
      <<if _args[0] == 'red'>>
         <<play-red>>
      <<else>>
         <<play-black>>
      <</if>>
    <</replace>>
    <<goto Room>>
  <</link>>
<</widget>>

The example I gave above was a simplified example to isolate the problem. I have many references to these widgets. I guess I can write a script to go through all references to e.g. the play widget and construct a big switch statement out of the results but I was hoping for a simpler way.

I wanted to use widgets instead of passages to generate content since the Twine UI becomes a bit cumbersome when I have a large number of passages.

The example above does not set the $atext variable, so the goto statement will refresh the page and the results of the replace will be overwritten with whatever is in $atext. That is why I wanted to capture the output of the widget in $atext. If I cannot use a widget to update variables and generate some text output then I will look for other ways.

Thank you for your feedback!

Hmmm

In general, if what you want is to capture the output of some piece of reusable code, you really want to be using a function, rather than a widget. Functions return values for you to reuse, widgets — in general — do not.

If you aren’t comfortable writing JS functions , I do have a custom Macro to write functions the same way you write widgets (hituro-makes-macros/function-macro at main · hituro/hituro-makes-macros · GitHub), though in general I wouldn’t say it was so hard to write the actual functions that I’d advise using it.

Another option would be to change the <<play>> widgets to set $atext

<<widget play-red>>
<<set $reds = $reds + 1>>
<<set $atext = "You play a red card.">>
<</widget>>

But going back to the original question, I realise that I don’t actually know why you have the <<goto>> at all. The <<replace>> sets the contents of #action to the result of running your widget, but then you refresh the passage, throwing away the text in #action.

So it sounds like you don’t actually want the <<goto>> at all

1 Like

Long story short: This is my first attempt at creating a Twine game. It will basically be the Twine version of my parser game I submitted for IFComp last year.

I now have this working:

:: Room {"position":"300,100","size":"100,100"}
<span id="room">
Red: $reds.
Black: $blacks.
This is a test story for testing widgets and side effects.
Click the <<play red>> link to play a red card.
Click the <<play black>> link to play a black card.
</span>
<hr>
<span id="action">$atext</span>

:: StoryInit {"position":"100,100","size":"100,100"}
<<set $reds = 0>>
<<set $blacks = 0>>
<<set $atext = 'Here the action text will be shown.'>>

:: Widgets [widget] {"position":"500,100","size":"100,100"}
<<nobr>>

<<widget play-red>>
<<set $reds = $reds + 1>>
<<set $atext = "You play a red card.">>
<</widget>>

<<widget play-black>>
<<set $blacks = $blacks + 1>>
<<set $atext = "You play a black card.">>
<</widget>>

<<widget play>>
	<<link _args[0]>>
		<<if _args[0] == 'red'>>
			<<play-red>>
		<<else>>
			<<play-black>>
		<</if>>
		<<goto Room>>
	<</link>>
<</widget>>

<</nobr>>

I need that goto to ensure saves work for my game. As I understand it, a save only works for the state preserved at the last page transition. If I never change the page (and goto to itself also counts as a transition), then a save game would not work.

That is largely true, State is only saved as a moment in the history when a passage transition happens.

So, since you refreshing the passage, your original <<replace>> does nothing, and I see you have removed it now. Basically <<replace>> helps with dynamically updating the passage when something changes without a passage load. If you are navigating to new passages, or reloading the current one, you don’t need it.

A different question, of course, is why only have one passage.

I wanted to offer a lightweight alternative (less clicking on links) to what the “default” appears to be: using a passage for each and every action.


The page mode looks something like this:

[1] Player clicks broad dark stain

[2] Player clicks Next

And to Undo this move:

[3] Player clicks Undo

[4] Player clicks Next

And we are back were we started.


But I wanted to create a flow mode where I put the action results on the same page as the “room”.


The flow mode looks something like this:

[1] Player clicks broad dark stain

And the player is shown the action message, and the new contents of the room.

And to Undo this move:

[2] Player clicks Undo

And we are back were we started. Half the number of clicks needed (but we need more screen space, so potentially more scrolling if played on mobile devices with limited screen space.)


Technically the page mode is working as it should, but that is due to the fact the widget is always executed exactly once. I merely tried to get the same effect for the flow mode.

What you are trying to do is basically Stretch Text, and Sugarcube is not designed for that.

Many people have experimented with ways to do it, but the short answer is … don’t :slight_smile:

There is a format designed for stretch text, Paloma, but it’s not very maintained.

Agreed. Although it might be possible to find a way to get it working, I rather spend time on getting other things working, so I dropped the flow mode idea for now. I do like the widget mechanics, I consider them ‘lightweight passages’ and they allow me to put related actions together in the same file.

Having said “don’t”, have you looked at CTP at all?

It’s designed for putting multiple blocks of text in one passage, with clicking to advance/reverse through that list.

It might scratch some of the itch?

I have a version more suited to branching within a single passage