0 votes
by (470 points)

I know that I can make that with javascript, but I am curious if it is better and more important if there is a possibility to make this work on Sugarcube macros? I have this textbox: 

<<textbox "$hero" $randomNickname autofocus>>

There is randomNickname by default and I want to give the player a button to get another randomNickname which randomly takes one from an array. 

And still, talking about javascript how do I set class or id to this element so I could comfortably interact with it? 

1 Answer

+2 votes
by (159k points)
selected by
Best answer

If you use your web-browser's built-in Web Development Tools to Inspect the HTML generated for your <<textbox>> example you will see that it looks something like the following.

<input id="textbox-hero" name="textbox-hero" type="text" tabindex="0" class="macro-textbox" autofocus="autofocus">

... you will notice that SugarCube has automatically generated an ID for the input element based on the name of the story variable you passed in as the first parameter to your <<textbox>> macro call, and you can use that ID to interact with that element after that element has been created/rendered.

eg. The String literal textbox has been prepended to the story variable name hero to create an ID of textbox-hero.

If you want to programatically alter the value currently being displayed within the input element after it has been rendered then you will need to Javascript to do that, although that Javascript code could be contained within a custom macro.

by (470 points)
Forgot to check that, thank you.

I still have that array in twine editor, can I pass it to jQuery somehow during the game progress?
by (63.1k points)

Assuming $randomNickname is an array of nicknames, you'd want something like: 

<<textbox "$hero" `$randomNickname.random()` autofocus>>

<<button "Random Name">>
    <<run $('#textbox-hero').val(State.variables.randomNickname.random())>>

You probably don't want to pass the entire array to the text box, just one random element, which we can grab with SugarCube's <array>.random() method. I'm using back ticks above in the text box call to pass the function's return value as an argument, which you need to do for macros that accept discreet arguments (as opposed to expressions). 

Then we use a bit of jQuery, and pass the story variable over using its parent object State.variables. In this case, I'd actually recommend a setup variable instead, as the nickname list probably doesn't need to be tracked by the state system. 

That'd mean you'd just refer to the variable this way: 

<<set setup.randomNicknames to [list of names]>>

<<textbox "$hero" `setup.randomNickname.random()` autofocus>>



by (470 points)

@Chapel That is not an array, that is variable that already contains one name: 

<<set $randomNickname = $randomNicknames[random($randomNicknames.length-1)]>>

Looks like I could have done that easier :D

That code that you displayed above is exactly what I needed. Thank you very much! 

Btw what do you mean by "setup variables"? And what do you mean by tracking? You mean to store names at temporary array? That might be a good idea. 

Edit: I still do not understand. What's the difference between setup and normal variables? 

by (63.1k points)
SugarCube's State system keeps track of every story variable ($) and its value in every moment (passage) in your story, and the save system saves those variables and only those variables. You can turn the history tracking off, but you'll lose the ability to go back and forward in the story. Variables that aren't story variables are not tracked by the State system, are not saved, and are not adjusted by history navigation.

So if you have a bunch of data that is essentially constant, immutable, or just doesn't need to be saved, you can just use JavaScript variables. This prevents the State from tracking them but doesn't prevent them from being available to multiple passages like with temporary variables (which also might work here, depending on how you've got it organized).

The setup object is provided by SugarCube for authors to use, meaning it's guaranteed that it'll always be empty and not used by SugarCube itself in future updates, so it's generally a safer place for your JavaScript variables than in the global scope or in your own namespace, but there's nothing otherwise special about it.

As to why you'd want to do this, it's mostly because tracking those variables can (eventually) have a performance cost as stories grow, so it's a good habit to be in, and it keeps your saved data small, which can be important on mobile devices with limited browser storage.

I don't want to overblow the performance or saved data size benefits, it is negligible for most stories. I just think it's a safer way to do things. When in doubt, though, use story variables; it's generally less of a pain to track something you don't need than to need something you didn't track.
by (470 points)
edited by
I totally understand what you are talking about. Why don't use temporary variables? Looks like the same thing. Still, I have moved everything constant to setup object and looks like I have a new problem. (because of that button, I guess I missed it)

Now it doesn't matter if I press those button, it won't save any data that was changed by this technique. The variable $hero for textbox simply stays the same as the first nickname that was entered in it by defaultVariable... Gosh.. Any ideas if that is possible to fix?

I could fix that with jQuery, but can I save information from jQuery script to save file? (maybe you should write that as an answer to a question since people might be looking for that in this topic)
by (63.1k points)
edited by

 Now it doesn't matter if I press those button, it won't save any data that was changed by this technique.

My fault, sorry. You need to manually trigger the change event afterward: 

<<button "Random Name">>
    <<run $('#textbox-hero').val(State.variables.randomNickname.random()).trigger('change')>>

That should fix it. 

Why don't use temporary variables? 

You can / should use temporary variables where appropriate, and I didn't mean to imply otherwise. However, temporary variables are only active in the moment they are created in. Sometimes, you might want a variable to not be tracked by the State, but still persist and be accessible to multiple passages in multiple moments. Without looking at more of your code, it's impossible for me to tell what would be more appropriate, but it's possible temporary variables are the right way to go. 

If you're setting up your array in the same passage as your name text box, a temporary variable will be fine, and you can access it in the jQuery via the State.temporary object. If you're setting up your variable anywhere else, or if you expect to need the list of names again before the story ends, then the setup object would be more appropriate. 

Either way, in this case, I don't think a story variable is the best option. Sorry for any other confusion this caused. 

by (470 points)
No confusion at all. Everything works perfectly now thanks to you! Thank you for all the patience and accurate answers!
by (68.6k points)

Chapel's example code may be simplified a bit to the following:

<<button "Random Name">>
    <<run $('#textbox-hero').val($randomNickname.random()).tigger('change')>>

The reason for that is because, unlike <<script>>, <<run>> does perform TwineScript processing.