Changing an object's property within another object's declaration? [SugarCube 2]

I’m trying to create object “templates” that I can reuse throughout my story. I have character objects and item objects, each with their own unique template of default properties. And in many cases, my templates have other objects/templates as properties themselves.

So, trimming down to a very simple example (my actual objects have many more properties)… If I have deputies and outlaws, each has their own template. Each character also has a weapon, another object with its own properties. Like so:

<<set $sixshooter = {
     rounds: 6,
     damage: 10
}>>

<<set $cowboy = {
     name: "",
     weapon: clone($sixshooter)
}>>

<<set $outlaw = {
     name: "",
     weapon: clone($sixshooter)
}>>

<<set $outlaw1 = clone($outlaw)>>
<<set $outlaw1.name = "Butch Cassidy">>

So far, this works as intended. I can create characters and everyone automatically gets a six shooter. But what if I want the outlaws to have an advantage, and every outlaw’s gun does 12 damage instead of 10?

I’d like to have code like shown below, which creates outlaws with six shooters (and each gun with 6 bullets), but then changing the damage to 12. (None of this below works):

<<set $outlaw = {
     name: "",
     weapon: clone($sixshooter)

     /* Then change the damage, using: */
     weapon.damage: 12,
     /* OR */
     this.weapon.damage: 12,
     /* OR */
     weapon: {
          damage: 12
     } /* while still preserving "rounds" */
}>>

Since none of this works, I’m having to awkwardly add the code I want after the object is created.

<set $outlaw.weapon.damage = 12>>

Is there any way to keep this entirely within the object declaration?

Thanks!

If you have “templates” of items which will never change, it’s best to make them on the setup object in the JavaScript section, like this:

setup.sixshooter = { rounds: 6, damage: 10 };

And then your SugarCube code would look like this:

<<set $cowboy = {
	name: "",
	weapon: clone(setup.sixshooter)
}>>

That keeps you from wasting space in your game’s history by storing the unchanging $sixshooter variable at every point in the game’s history. This should prevent unnecessary slowdown during passage transitions, saves, and loads.

If you want a version where you can control properties of the object during declaration, then you could make a function for that instead. For example, you could put this in your JavaScript section:

setup.weapon = function (rounds, dmg) {
	return { rounds: rounds, damage: dmg };
};

and then your SugarCube code would look like this:

<<set $outlaw = {
	name: "Butch Cassidy",
	weapon: setup.weapon(6, 12)
}>>

Or you could add this to your JavaScript section too:

setup.outlaw = function (name, rounds, dmg) {
	return { name: name, weapon: setup.weapon(rounds, dmg) };
};

and then your SugarCube code would look like this:

<<set $outlaw = setup.outlaw("Butch Cassidy", 6, 12)>>

If you need any further explanation of any of the above, just ask.

Hope that helps! :slight_smile:

Yes, that makes sense. I was hoping that I was missing an obvious TwineScript function call or something, so I could keep things all encapsulated neatly in the passage without going into JavaScript.

Your last example would serve best in this situation, as I would only need to pass the damage as a single argument, and return the defaults for everything else.

One part that I’m still unclear on is how memory is used during runtime. Why is it that the JavaScript code would save on space, as opposed to the Twine passage? And if I start putting lots of code into the JavaScript file, is there a way within the Twine IDE to break it into multiple files, using something like an <<include>> macro? (Primarily to help with organization?)

Thanks, HiEv!

HiEv is not talking about memory so much as actual storage space. Twine uses your browser’s localstorage to save and it’s a very finite amount of space that differs from browser to browser.

Every time you navigate to a new passage, Twine creates a copy of every one of your variables plus Twine’s own variables for the history. If you have your game set for 10 history entries and you have an array of 50 items, that array is now taking up the space of 500 items after you navigate 10 pages.

You can lower the amount of history entries to decrease the impact of this, but as HiEv suggested, if the data you are using is unchanging, then it makes way more sense to declare it in javascript and referenced at runtime instead of having it stored directly in your variable and taking up unneeded space. I hope that all made sense.

As for including JS files, I don’t believe you can do that with the normal Twine editor. However, you can do this with tweego, which is a command line compiler for Twine. It’s developed by TheMadExile, the developer of the Sugarcube format. The downside is that there is no visual layout of your passages this way. You’ll have to use a text editor.

The TL;DR version of what tayruh said is that, data in story variables have to be recorded in the game’s history, and the larger the game’s history is, the slower that loads, saves, and passage transitions will be for that game. So, any data which doesn’t have to be stored in the history (such as data which never changes) would be better stored elsewhere. The setup object provided by SugarCube is the preferred place to do this, since data stored on it is available in most contexts within a Twine/SugarCube game.

Regarding having multiple JavaScript files in Twine v2, you can load JavaScript files using the SugarCube importScripts() function. However, that code won’t be within the same scope as the SugarCube functions, which can make coding a bit more complicated. If you need to do this for some reason, the Tweego route mentioned by tayruh is recommended.

That said, I tend to just edit all of my JavaScript in one file in VSCode, and then copy-paste it into the JavaScript section in the Twine editor, which effectively produces the same results as the Tweego route.

Hope that clears things up! :slight_smile:

Yes, thank you both! It’s helpful to know how things are working on the back end. I’ll take a look at VSCode. It would be nice to have a better editor for JavaScript. Thanks!

If you’re trying out VSCode, I’d recommend the following extensions for JavaScript:

  • DeepScan - JavaScript checker (note: requires that you be online)
  • ESLint - JavaScript checker (note: requires that NPM is installed, which is included with Node.js, which is also required)
  • JSHint - JavaScript checker (note: incorrectly identifies a few things as problems and is slow on extremely large files, but catches a few things the other two don’t)
  • Numbered Bookmarks - Makes jumping around your code easier
  • Trailing Spaces - Highlights unnecessary spaces

There are a ton of other extensions for JavaScript and other things, such as CSS, so you might want to browse through the list of VSCode extensions.

Have fun! :slight_smile: