Help with Generate Buttons Macro in Sugarcube 2.31.1

So I was trying to make a function in javascript that would take in a list of strings as an argument, and for each string in the list, it would make a button and assign the string[i] text to the button’s name.

and then add a macro to call the function and pass the parameters to the function in twine.

I would love to show you where I have gotten so far, but nothing has worked even a little bit, and I feel like there’s something fundamental I am not getting.

If I could pseudo code my idea it would go like this:

funtion Make_Buttons(list_length, button_txt[])
	for (i = 0; i <= arg[0]; i++){
		<create a button>
		text = button_text[i];
		</button>



Macro.add('button list', {
    handler: function()
    {
       setup.Make_Buttons(this.args[0], this.args[1]);
    }

obviously super over simplified, but just to illustrate.
I’m looking for:
A. How do I make a function or macro that will streamline my dialog buttons

I am down to make it a learning experience, I am not trying to be lazy, but if this isn’t some giant task i would really appreciate help :smiley:

I code c# and python at like an intermediate level.
But HTML and JS confuse me (Why I like to make twine games, it is good practice.)
Just so you know teaching me technical things isn’t hopeless! just struggling with this!
Thanks!
Sorry for the long post!

If you aren’t good with HTML or JavaScript, you could do it by creating a widget if you want to stick with SugarCube macros.

Just create a “widget” passage, with “widget” and “nobr” tags and the following code inside:

<<widget "MakeButtons">>
	<<for _i = 0; _i < $args[0].length; _i++>>
		<<set _txt = $args[0][_i]>>
		<<capture _txt>>
			<<button _txt>>
				<<set $text = _txt>>
			<</button>>
		<</capture>>
	<</for>>
<</widget>>

Note that the <<capture>> macro is necessary so that, instead of using the value the variable has at the time the button is clicked (which wouldn’t be what you want), the button uses the value the variable had at the point where the button was created.

Now you just need to do something like <<MakeButtons _buttonTextArray>> in your passages to create a row of buttons from the array you passed to that widget.

You should be able to use that code as the basis for your own button widget if you need it to do something different.

Have fun! :slight_smile:

1 Like

Is a widget basically the twine equivalent of a user-made function?

Much cleaner than before [: Especially when there are 3 or four options in the passage. much easier to keep track of. Definitely gonna’ tweak the base design, thanks again for showing me!

<<set _dockRoot = ['*Talk to Ferryman.*', '*Go into town.*']>>\

<div class="btn-group">
	<div class="container">
		<<MakeButtons _dockRoot>>
	</div>
</div>

Yeah, SugarCube widgets are kind of like functions, except that they don’t really have a return value.

Also, you could tweak the widget like this:

<<widget "MakeButtons">>
	<div class="btn-group">
		<div class="container">
			<<for _i = 0; _i < $args[0].length; _i++>>
				<<set _txt = $args[0][_i]>>
				<<capture _txt>>
					<<button _txt>>
						<<set $text = _txt>>
					<</button>>
				<</capture>>
			<</for>>
		</div>
	</div>
<</widget>>

putting the <div>s inside the widget, and then in your passages you could just call it like this:

<<MakeButtons `['*Talk to Ferryman.*', '*Go into town.*']`>>

The “backquotes” (the accent mark on the tilde “~” key on the upper-left of most keyboards) around the array causes SugarCube to pass the contents within those backquotes as a single value.

Enjoy! :slight_smile:

I did this tweek! :smiley: as well as adding a second argument for passage. I had to leave container out, as the loop made a separate container for each button. and still iterating [:

<<widget "MakeButtons">>
	<<for _i = 0; _i < $args[0].length; _i++>>
		<<set _txt = $args[0][_i]>>
		<<set _psg = $args[1][_i]>>
		<<capture _txt>>
			<div class="btn-group">
				<<button _txt _psg>>
					<<set $text = _txt>>
					<<set $passage = _psg>>
				<</button>>
			</div>
		<</capture>>
	<</for>>
<</widget>>

Thanks again [: This makes so many of my passages waaaaay less stressful to look at and manage.

You’re going to need to change:

<<capture _txt>>

to:

<<capture _txt _psg>>

otherwise those buttons are going to set $passage to the wrong value.

Remember, the <<capture>> macro is what makes sure that the value used when the button is clicked is the value that it had at the time that the button was created. Without that, it will use whatever the last value of _psg was instead, regardless of which button was clicked.

1 Like

Right you are. Thanks again.

A Widget is a means to create a custom Macro, using TwineScript instead of JavaScript.

Neither Widgets or Macros ‘return’ a value. They are a means to encapsulate code so it can be called repeatably, and are often used to inject content into the current web-page.

1 Like

I am trying to expand on this code. I have some buttons in the game (HiEv also helped me with.) that, when you press the button (To ask a question in game), it will not only answer it, but it will spawn other buttons with relevant questions to the npcs answer. that’s all good and well and figured out. Here is an example of that code:

		<<button 'Do you live in Drogg Harbor?'>>
			<<replace '#cap_name'>>\
				<<SeaCaptain>>Ay. Ever since I was a boy.<</SeaCaptain>>\
			<</replace>>
			<<run $('#hidbtn').css('display', 'initial')>>
		<</button>>\
		<span id="hidbtn" style="display: none">\
			  <<button 'What did you do there back then?'>>  
				<<replace '#cap_name'>>\
					<<SeaCaptain>>Fished with me father. Ran the beaches and coves all summer, and sat at the lighthouse watchin' storms all winter. Back then it was ... peaceful.<</SeaCaptain>>\
				<</replace>>
			  <</button>>\  
		</span>\

What i’d like to do is make a widget that makes one base button. no for loop needed . button 1 response, but then the for loop will generate how many sub questions you want clicking on that button to make. here is an example of my code that is not working to hopefully give an idea:

<<widget "GenerateSubQuestion">>
		<<set _dialogue = $args[0]>>
		<<set _response = $args[1]>>
		<<set _id = $args[2]>>
		<<set _hideSpanID = $args[3]>>
		<<capture _txt _response _id>>
			<div class="btn-group">
				<<button _dialogue>>
					<<set $text = _dialogue>>
					<<set $id = _id>>
					<<set $response = _response>>
					<<replace $id>>
						$response
					<</replace>>
					<<run $(_hideSpanID).css('display', 'initial')>>
				<</button>>
			</div>
		<</capture>>
		
	<span id= _hideSpanID style="display: none">\	
		<<for _i = 0; _i < $args[4].length; _i++>>
			
			<<set _sqDialogue = $args[4][i]>>
			<<set _sqResponse = $args[5][i]>>
	
			<<capture _sqDialogue _sqResponse _id _hideSpanID>>
				<div class="btn-group">
					<<button _sqDialogue>>
						<<set $text = _sqDialog>>
						<<set $id = _id>>
						<<set $response = _sqResponse>>
							<<replace $id>>
								$response
							<</replace>>
					<</button>>
				</div>
			<</capture>>
		<</for>>
	</span>
<</widget>>

If there is a more sophisticated way of handling this I am definitely open to it :smiley:

Two lines of code which won’t work the way you want. First:

<<capture _txt _response _id>>

will need to be changed to:

<<capture _txt _response _id _hideSpanID>>

because _hideSpanID is also used within the button’s code, and if you’re using the macro more than once in a passage, then the value of _hideSpanID may change. That said, you don’t need the _hideSpanID in the second <<capture>>, since that variable isn’t used within the button’s code there.

Second:

<span id= _hideSpanID style="display: none">\

will need to be changed to:

<span @id="_hideSpanID" style="display: none">\

because you can’t use raw SugarCube variables in HTML elements, you have to use a SugarCube attribute directive instead. You do that by putting an @ in front of the attribute name, which tells SugarCube to evaluate the contents of that attribute, and then the attribute will be set to the result of that evaluation.

Also, the lines:

<div class="btn-group">
...
</div>

should probably be outside of the <<for>> loop, instead of inside it, that way all of the buttons produced by the loop would be within that one “btn-group” <div>.

I haven’t tested it, but if that doesn’t work, it should at least get you closer to what you’re trying to do.

Have fun! :slight_smile:

1 Like

So right now I am getting the error message
Error: <>: errors within widget contents (Error: <>: bad evaluation: i is not defined; Error: <>: bad evaluation: i is not defined; Error: <>: bad evaluation: i is not defined; Error: <>: bad evaluation: i is not defined)

<<GenerateSubQuestions _testDialog _testResponses _currentId _sqDialog _sqResp _sqSpan>>

and my code is:

<<widget "GenerateSubQuestions">>
		<<set _dialogue = $args[0]>>
		<<set _response = $args[1]>>
		<<set _id = $args[2]>>
		<<set _hideSpanID = $args[3]>>
		<<capture _txt _response _id _hidespanID>>
			<div class="btn-group">
				<<button _dialogue>>
					<<set $text = _dialogue>>
					<<set $id = _id>>
					<<set $response = _response>>
					<<replace $id>>
						$response
					<</replace>>
					<<run $(_hideSpanID).css('display', 'initial')>>
				<</button>>
			</div>
		<</capture>>
		
	<span @id= _hideSpanID style="display: none">\	
	<div class="btn-group">
		<<for _i = 0; _i < $args[4].length; _i++>>
			<<set _sqDialogue = $args[4][i]>>
			<<set _sqResponse = $args[5][i]>>
			<<capture _sqDialogue _sqResponse _id _hideSpanID>>
					<<button _sqDialogue>>
						<<set $text = _sqDialog>>
						<<set $id = _id>>
						<<set $response = _sqResponse>>
							<<replace $id>>
								$response
							<</replace>>
					<</button>>
			<</capture>>
		<</for>>
	</div>
	</span>
<</widget>>


and this is how I am trying to call it incase that’s the issue:

	<<GenerateSubQuestions _testDialog _testResponses _currentId _sqDialog _sqResp _sqSpan>>

You need to put any macro names within “preformatted text” blocks here (the </> button in the editor toolbar), otherwise this forum tends to eat them, as you can see above.

That said, I think the error is here:

			<<set _sqDialogue = $args[4][i]>>
			<<set _sqResponse = $args[5][i]>>

The "i"s should be "_i"s instead. That’s why you’re getting the i is not defined error.