Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Keeping complex JS objects in variables?

I'm a pretty experienced JS developer but new to messing with Twine, and I'm trying to build a bit of an RPG system for Twine. I have a number of "complex" JS objects representing various bits of state (with methods and prototypes other than Object) that I want to be able to expose to Twine in $variables, but this is proving problematic as the history/state tracking system doesn't seem to play well with non-JSON-like objects.

I've also tried using SugarCube with history tracking disabled (since for a game it seems like it should be sufficient to store the current variables and the current passage only, which is what that seems to do) but SugarCube's state serialisation mechanism also doesn't preserve the "object-ness" of the objects.

Is there a clever way to do this that's not obvious to me? I have a vague idea of using a window global to store the "interface" objects with the methods defined, but having them keep their actual states in JSON forms in $variables, but haven't managed to get that looking particularly nice yet..

Comments

  • Serialization in Twine headers is primarily done via JSON.  Both SugarCube and the vanilla headers perform some encoding on user functions and certain global object types (e.g. RegExp, Date) and the like to allow them to be (de)serialized via JSON, which is not possible by default, however, that's about the extent of the JSON wrapping that the headers do.

    Can you provide an example of exactly what you're trying to store in $variables?
  • I mean having something like &lt;&lt;set $player = new Player()&gt;&gt; in StoryInit and then proceeding to call methods on it in later places like &lt;&lt;print $player.carriedWeight()&gt;&gt;, having previously used a script passage to define, say:<br /><br /><blockquote><code>window.Player = function () {
    // constructor stuff goes here
    }

    window.Player.prototype.carriedWeight() = function () {
    // calculate some answer and return it
    }


    The only idea I have currently is to make the actual constructed objects window globals as well, and have them store all their state in variables instead, by doing something like:
    window.Player = function (stateObj) {
    this.state = stateObj;
    }

    SugarCube.state.active.variables.player = {};
    window.player = new Player(SugarCube.state.active.variables.player);
    and then making sure that the Player code stores all of its state inside this.state instead of directly on this, in a form that's JSON-serialisable, but this seems kinda gross :(
  • The best thing to do is probably to simply add a toJSON() method to your prototype, which will return a code string that leverages SugarCube's built-in revival code.  That will allow automatic revival of your objects.

    For example, you could do something like this: (you'll need at least SugarCube v0.9.9 for this)

    window.Player = function (stateObj) {
    if (typeof stateObj === "object") {
    this._data = stateObj;
    } else {
    // defaults
    this._data = {
    name : "Bob",
    race : "Human"
    };
    }
    };

    window.Player.prototype.toJSON = function () {
    return JSON.reviveWrapper('new Player(' + JSON.stringify(this._data) + ')');
    };
    All that JSON.reviveWrapper does is wrap your code string in a bit of sugar that makes SugarCube automatically execute your code string on deserialization.

    I've attached an example TWS showing full working example of something like the above.


    PS: Never use the SugarCube global object in code.  It's there to expose some of the internals for developer inspection (debugging).  Code executing inside has absolutely no reason to use it (especially since it doesn't export everything).
  • Ah, neat. I had a look in intrinsics.js to see what that's doing and it makes sense, and the example suggested in your post also avoids needing to manipulate the variables array via the SugarCube global either. I'll try that out and see how it works in practise; thanks a lot!
Sign In or Register to comment.