[Sugarcube2] How to make object defined in JS modules use variables() data

Twine Version: 2.3.16
Story Format: Sugarcube

Hi there!

I have some complex game on hands, so I isolated my problem in a much simplier example following

In example game we have two JS files:

Gladiator.js

window.Gladiator = class Gladiator {
	constructor(name) {
        this.name = name;
        this.hp = 10;
        this.skill = 0;
    }
}

and Ludus.js

window.Ludus = class Ludus {

    constructor() {

        this.name = 'My Ludus';

        this.gold = 0;

        this.jobs = {

            list: ['fight', 'train'],

            options: {

                'train': {

                    jobName: 'Make gladiators train',

                    execute() {

                        variables().gladiator.hp--

                        variables().gladiator.skill++

                    },

                },                

                'fight': {

                    jobName: 'Make gladiators fight',

                    execute() {

                        variables().gladiator.hp--

                        this.ludus.gold += variables().gladiator.skill

                    },

                },

            },

        };

    }

}

and three Passages in Twine .xml

Start

<<script>>

importScripts(
	["C:/dev/arena/Gladiator.js",
	"C:/dev/arena/Ludus.js",
	]
);

<</script>>

[[makeGladiators]]

makeGladiators

<<script>>
	variables().gladiator = new Gladiator('Alice');
<</script>>	

<<script>>
	variables().ludus = new Ludus();
<</script>>	

<<goto 'Ludus'>>

and Ludus

Ludus gold: $ludus.gold

$gladiator.name
HP: $gladiator.hp
Skill: $gladiator.skill

<<link '$ludus.jobs.options.train.jobName' 'Ludus'>>
	<<= $ludus.jobs.options.train.execute()>>
<</link>>
<<link '$ludus.jobs.options.fight.jobName' 'Ludus'>>
	<<= $ludus.jobs.options.fight.execute()>>
<</link>>

I got error when trying to exeute() job. “variables not defined”. As far as I know this is due to JS modules have no information about Twine variables.

Yes, I know that this code is excesive to make such a simple task, but as I said before it’s just an example and in my actual game I really need multiple JS modules work tougether. So, how can I make State.variables to be visible to JS modules here? Or maybe get another walkaround this problem.

I allso would be glad to use Twine either() function inside my JS modules (not to write it myself from scratch in a module file). And this also can be solved if JS modules could see Twine variables and methods.

I don’t have a solution to your JavaScript Scope issue, short of either pasting the contents of your two external JS files into the Story JavaScript area of your project, or using a TWEE complier like TweeGo to embed those files within the generated Story HTML file.

However if you look at the source code for the variables() function you will see that it returns a reference to the State.variables object.

So to save yourself the extra overhead of making an unnecessary function call I suggest you switch to directly using State.variables in your code.

State.variables is longer so I use variables(). It isn’t work either way with JS objects (

Can you suggest how exactly do I do this? I tried to before, but Story JavaScript is as much incompatteble with JS modules as scrip in passages.

Can you suggest how exactly do I do this?

Open Story JavaScript area of your Twine 2.x applicaiton project, then copy the contents of the two JS files into the editor window, the result should look something like the following…

window.Gladiator = class Gladiator {
	constructor(name) {
		this.name = name;
		this.hp = 10;
		this.skill = 0;
	}
}

window.Ludus = class Ludus {
	constructor() {
		this.name = 'My Ludus';
		this.gold = 0;
		this.jobs = {
			list: ['fight', 'train'],
			options: {
				'train': {
					jobName: 'Make gladiators train',
					execute() {
						variables().gladiator.hp--
						variables().gladiator.skill++
					},
				},
				'fight': {
					jobName: 'Make gladiators fight',
					execute() {
						variables().gladiator.hp--
						this.ludus.gold += variables().gladiator.skill
					},
				},
			},
		};
	}
}

WARNING: There are a number of issues with the above code:

  1. The execute() function of the “fight” option makes refence to this.ludus.gold however in that context the this keyword is referencing the function itself and that function doesn’t have a ludus property, thus you can’t access the gold property of it.
  2. You need to add the clone() and toJSON() functions described in the Non-generic object types (a.k.a. classes) section of the SugarCube documentation, so your classes will support the serialisation process used by SugarCube’s Progress, History, and Save systems.

I then suggest making the following changes to the content of your three Passages…
(example written using TWEE Notation)

:: Start
[[makeGladiators]]


:: makeGladiators
<<set $gladiator = new Gladiator('Alice')>>
<<set $ludus = new Ludus()>>
<<goto "Ludus">>


:: Ludus
Ludus gold: <<= $ludus.gold>>

<<= $gladiator.name>>
HP: <<= $gladiator.hp>>
Skill: <<= $gladiator.skill>>

<<link $ludus.jobs.options.train.jobName 'Ludus'>>
	<<run $ludus.jobs.options.train.execute()>>
<</link>>
<<link $ludus.jobs.options.fight.jobName 'Ludus'>>
	<<run $ludus.jobs.options.fight.execute()>>
<</link>>

You will notice I made some corrections to your original code:

  1. You were using <<script>> macros to initialise the $gladiator and $ludus Story Variables in your makeGladiators Passage, where the <<set>> macro is the correct macro to use.
  2. You wrapped the variables being used as the Label Text of each <> within quotes, when there is no need to do that.
  3. You were using one of the <<print>> family of macros to execute the function associated with the “option”, where the <<run>> macro is a better choice, because those functions don’t return a value to be outputted to the page. And even if the function did return such a value the <<link>> macro is what’s known as “silent”, as in its body doesn’t output anything.

There is also an issue in the way you’re using the makeGladiators Passage as an “invisible” Passage, by forcing a Passage Transition once the variables have been initialised. This adds an extra Moment to the History which pads unnecessary and it could cause interesting outcomes if the end-user uses the Undo button.

Thank you. I will try it.