Help with Sugarcube and Javascript arrays

Twine Version: 2.3.13
Story Format: Sugarcube 2.34.1

Hello,

I’m using twine to create a tool where users can generate emails to job applicants by clicking through various options for each phrase (with the cycle macro). Depending on their choices the resulting email could range from an enthusiastic job offer to a brutal rejection.

That part all works fine. Once the user has finalised their email I want them to be able to click a button (or similar) to make it shorter. For example:

Dear candidate,

Thank you for your interest in working with us. We are writing to inform you that you didn’t get the job.

becomes:

Dear candidate,

You didn’t get the job.

I’ve got this working in Javascript but am having trouble fitting it into Twine. My code manipulates the array of variables but I’m not sure how to pass these through the function and back. Here are my attempts so far:

<<set $email to [$a, '<br><br>', $b, $c, $d, '<br><br>', $e, $f, $g, $h, $i]>>
<<set $email = State.variables.baseEmail>>
<<set $shortemail = State.variables.shortEmail>>
<<button "make it shorter" "make it shorter">><<script>>shorten();<</script>><</button>>
—
<<for _i=0; _i<$shortemail.length; _i++>>
<<print $shortemail[_i]>>
—

Then in the JS section:

window.shorten = function shortenEmail () {

//create new modified strings to insert into new array

var stringA = baseEmail[4];
newStringA = stringA[0].toUpperCase() + stringA.slice(1);

var stringB = baseEmail[7];
newStringB = stringB[0].toUpperCase() + stringB.slice(1, -1);

//make edits

var shortEmail = [...baseEmail]

    shortEmail.splice(2,3, newStringA);
    shortEmail.splice(4,2, newStringB);
    shortEmail.splice(4,2, newStringB);
    shortEmail.splice(6,0);

    return shortEmail;

}

Apologies if this is terrible code, I’ve only been learning for a few weeks. I did look into using Sugarcube’s array methods but couldn’t see an equivalent to splice.

Any suggestions would be greatly appreciated. Cheers!

This seems so overly complicated that I feel I’m missing something. Wouldn’t it be easier to hardcode both versions of the emails (maybe in an array) and select whichever one is more appropriate?


Also, since you’re new to code, I hope you don’t mind me making some suggestions to your coding style to prevent future issues.

  • Having a variable named $shortemail and $shortEmail is a bad idea ($shortEmail is a refence of State.variables.shortEmail). Even though it’s valid, you don’t really want to have two variables with the same name but different case because it causes confusion and makes debugging difficult.

  • The line window.shorten = function shortenEmail is a bit reduntant. shortenEmail is already being added to the window object (that’s where variables and functions go by default), so you’re creating the function there and then passing the reference to shorten. Instead you can just do window.shorten = function() {.

  • Related, but it’s considered bad practice to add variables and functions to the window object. Sugarcube provides the setup object for you so you can do setup.shorten = function(). The downside is that you have to call setup.shorten() instead of just shorten(), so it’s all matter of preference, I guess.

  • Assuming it wasn’t just a bad copy and paste, be sure to indent your code blocks correctly. For example, everything in shortenEmail() down to the shortEmail.splice should be indented one more level. You’ll hate yourself later if your code isn’t indented correctly because you’ll miss logic errors.

Anyway, those are just some tips. They’re only for your benefit and I’m not trying to impose anything on you. Feel free to ignore the suggestions if you like. :sweat_smile:

Yeah maybe I didn’t explain it well, the user chooses from different options for each phrase at first, so I need to pull in the variables after they’ve chosen each part (see this game for an example of the mechanic). Then I can’t just copy the array with a few bits deleted because to get it to read like a regular email I need to capitalize bits and remove full stops, etc.

So this is probably a dumb question, but where should I reference the State.variables bits so they get passed through to the function? I’m a bit hazy on how and when each part interacts with the game engine.

All good points, thank you! I’ll tidy up the names and use the setup object instead.

Hahah it was a bad copy and paste but the original code wasn’t perfectly indented either. I’ll try not to form a bad habit there!

Not imposing at all! Thanks a lot, I really appreciate all the suggestions.

1 Like

You can either hardcode it in the function like this:

// function
var baseEmail = State.variables.baseEmail;
var stringA = baseEmail[4];

// twine
<<set $shortemail = shorten()>>

or you can pass the array as an argument:

// function
window.shorten = function (baseEmail) {
	var stringA = baseEmail[4];

// twine
<<set $shortemail = shorten($basEmail)>> 
1 Like

Did the previous answer give you the help you need?

I have a couple of things to add:

<<set $email to [$a, '<br><br>', $b, $c, $d, '<br><br>', $e, $f, $g, $h, $i]>>
<<set $email = State.variables.baseEmail>>

I’m not sure why you set $email twice in a row. Doesn’t that mean that the first version (the array) doesn’t get used because it’s immediately over-written by State.variables.baseEmail?

<<for _i=0; _i<$shortemail.length; _i++>>
<<print $shortemail[_i]>>

You should be able to replace this with join() :

<<print $shortemail.join('')>>

It took me many years of JavaScripting to find join(), so hopefully I can accelerate the path for you :slight_smile:

You can read more about it here: Array.prototype.join() - JavaScript | MDN

2 Likes

Huh! It use join all the time but I never thought to use it with an empty string. What a great trick! :open_mouth:

1 Like

I’m glad it helped :slight_smile:

:white_check_mark: Good deed for the day

1 Like

Sorry I never followed up at the time, thanks to both of you for your help! I got it working by passing the array as an argument. And array.join() was a great tip. Thanks again :slightly_smiling_face:

1 Like