How do you implement Javascript classes in Sugarcube?

I’m trying to implement Javascript classes based on what it says here. But it doesn’t seem to work, instances of classes don’t inherit methods, and they get erased if you load a save.

For example, if I set up a class using the instructions from the docs…

class Character {
    constructor(name) {
        this.name = clone(name);
    }

    exampleMethod() {
        console.log(`Hello, ${this.name}!`);
    }

    clone() {
        return new Character(name);
    }

    toJSON() {
        return JSON.reviveWrapper(String.format(
            'new Character({0})',
            JSON.stringify(this.name),
        ));
    }
}

setup.Character = Character;

And then use it in Sugarcube:

<<link "Create character.">>
    <<set $character to new setup.Character("John Doe")>>
    <<run $character.exampleMethod()>>
<</link>>

The method isn’t inherited by the variable so it doesn’t do anything, and if you save and reload the game, the $character variable vanishes. (I’m using the checkvars macro to confirm this.)

Am I missing something? The docs say that classes are supported if clone() and toJSON() are implemented on the class, so I’m not sure what I’m doing wrong.

(I’m using Sugarcube 2.33.2 with Tweego v2.1.1.)

If you want to create a class, I’d recommend taking a look at the examples at the link you gave, because you didn’t follow the examples there at all.

Furthermore, the type of classes you’re trying to use would break your game in Internet Explorer, since it doesn’t support them. IMHO, there are still enough people out there using IE for this to be important (1 in 40 people in the US still use IE currently; source).

Really though, you usually don’t need classes and, worse, they tend to bloat up the game’s history (which slows down saves, loads, and passage transitions), so I wouldn’t recommend using them.

I’d recommend creating a SugarCube widget to do what you need instead, then you can just do <<makeChar "John Doe">> or whatever to create your characters.

Hope that helps! :grinning:

In addition to the points made by HiEv, you have two issues with your shown revival methods:

Issue 1: You attempted to reference name, rather than the property this.name. Try the following instead:

    clone() {
        return new Character(this.name);
    }

Issue 2: You attempted to reference the identifier Character in the revival string, which will not exist within the context it’s used in. That’s the whole point of assigning your class name to either the setup object, which means you need to reference it that way, or window, so it’s an auto-global that may be referenced from anywhere. Try one of following instead:

On the setup object:

    toJSON() {
        return JSON.reviveWrapper(String.format(
            'new setup.Character({0})',
            JSON.stringify(this.name),
        ));
    }
}

setup.Character = Character;

On the window object:

    toJSON() {
        return JSON.reviveWrapper(String.format(
            'new Character({0})',
            JSON.stringify(this.name),
        ));
    }
}

window.Character = Character;

Hm. Those are fair points, I’ll have to think about it.

Regarding the stats for Internet Explorer, I wonder how many of these are corporate and government computers running legacy apps? I wish it was possible to filter the stats by business vs. personal. IE might be even less popular than you think. ES6 has some truly great features that make me question whether supporting an obsolete browser is worth it.

Edit: Also if you look at worldwide stats instead of just the U.S., the numbers drop to just 1 out of 100 for IE users.

2 Likes

If you want to use ES6 (or later) features in your Twine project’s JavaScript then I suggest running it through a ES6 to ES5 transpiler (like Babel), that way you don’t need to worry about what ES6 features (if any) are fully supported by the end-user’s choice in web-browsers.