I recently got motivation to work on my Twine (rather, Tweego, which SugarCube is disappointingly losing support for) game again, and now I have completely lost it. I just get so mad and so demotivated when I run into these roadblocks that I'm starting to think I'm just not cut out to make games of any kind whatsoever.
I'm trying to make a text-based RPG, basically. So I figure I'll write a support library in Javascript. I use ES6 with traceur because the features it offers are pretty nice. OOP is always good, right? So I make a few classes with a little bit of inheritance and they're working great!
And then I save my game.
While trying to get functionality restored post-load, I came upon the realization that all of my class information is being discarded when the game is saved. Of course it is. It's impractical to expect that functions and classes and the like would be saved. Dumb objects with lots of nesting will work, but they can't have functions.
The entire way I have structured my code has been destroyed. I have absolutely no motivation to think about this problem right now, so I would like to ask for help with ideas restructuring everything.
Oh, by the way... there's no way to use userlib.js with Tweego. I had to modify the header. Also, I don't really appreciate that the "made with tweego" message got removed.
Anyways, the source for my project is here:
http://tiz.qc.to/pub/shine-0.0.4.zip
Comments
You maybe able to get around this problem by using SugarCubes "config.saves.onLoad" feature to re-add these methods/function to the object when you load a save. The TheMadExile posted about how to solve this issue as a reply to a question on a different forum, which I wont link to here due to the other forum's adult related content.
If what I described above sounds like your problem then PM me and I will supply you a link to the post.
[quote]I am guessing that your problem is that the javascript objects you are creating contain methods/functions and when you load one of these object within a save it is these methods/functions that are gone.
...I already said that.
[quote]You maybe able to get around this problem by using SugarCubes "config.saves.onLoad" feature to re-add these methods/function to the object when you load a save.
I don't know if it's quite that simple. I really don't know what traceur is doing under the hood to make classes work the way they do, let alone what I would need to do to make those objects instances of the various classes once again.
It would probably be a lot easier to just not have functions in my objects but that requires rethinking a lot of stuff.
I copied it there manually, you should be able to get it now. Apologies for all the problems.
Calm. As the author of both, I can assure you that SugarCube is not losing support for TweeGo (or vice versa). I simply haven't pushed a TweeGo update in a while. Both of the above issues have now been addressed.
Without trying to dig through your entire codebase to find out exactly what you're having issues with and assuming that you're saving your objects in $variables.
You can probably get around whatever your object revival problems are by writing
toJSON()
methods which save the objects' data and leverage SugarCube'sJSON.reviveWrapper()
to ensure their revival. Basically, something like the following examples from previous threads:- Re: Keeping complex JS objects in variables?
- Re: Javascript Object Prototype Syntax (SugarCube)
A basic example reusing the constructor:Sorry for coming off all pissy about that. I'm happy to hear that those issues have been fixed; I'll be able to get rid of the external .js this way.
That's correct.
That's interesting... so it's a string containing code that gets executed, that I can use to turn the dumb object back into a smart object, then? I wonder if I can just stringify the object itself and avoid all of that cruft. Or maybe Sugar can help me shortcut it somehow.
EDIT: Your example code seems to work! Now to figure out some way to shortcut it. I can't just use "this" because stringify calls toJSON and it'll get stuck in a loop.
Every attempt I make to shortcut the tedium of specifying every last property results in this: My last attempt was this. I don't expect you to know anything about Sugar.js, but I feel like there's got to be a better way.
Basically. SugarCube serializes its $variable store with JSON, which doesn't handle functions and methods, so to restore objects with attached methods magic must ensure. I'll assume you know about the
toJSON()
method. TheJSON.reviveWrapper()
method wraps the given code string in the boilerplate necessary to have it executed when deserializing it later, thereby reviving an equivalent copy of the original object.If you want to see exactly what's going on, check the bottom of
intrinsics.js
for SugarCube's JSON extensions.Not via JSON. Oh, you can restore the object and its methods, SugarCube will do that by default, however, that will not properly restore two types of relationships: references and scopes. Only by creating a new object with its constructor or a revival method and the requisite data can you ensure that all references and scopes are recreated as they need to be.
Maybe. I haven't really looked at Sugar enough to be able to say.
Beyond the above, my example was simply meant to give you a idea about what would be required. I wouldn't necessarily suggest doing it exactly like that.
For example, if you were only keeping serializable data in
this
(not counting methods, they can be excluded by type), then you could do something like this: As another example, if you were keeping your serializable data in a private-ish member like_data
, then you could do something like this:This approach results in $chars.ellie being the same garbage string. I'm going to presume I made my post while you were elaborating, so I'll repost the garbage string here: If I use Object.select with the keys of Character's prototype, it doesn't give me the garbage string, but the properties of the inherited class are missing, so I would need to go up the chain and grab all those keys.
Can you tell me what causes this kind of thing to happen so I can try to come up with a better fix?
What, exactly, is wrong with that string?
When I load the game, I expect $chars.ellie to be an object, but it is that string instead.
Ah, okay. That would have to be because the code is throwing an exception. There's nothing I see off-hand which would cause an exception, so I'm guessing that it's coming from the
Character()
constructor. Probably the result of grabbing properties you shouldn't be, so you might have to end up culling some properties.You could try the code manually to see what exception is thrown. For example, putting this in a
script
tagged passage:If it is because you're getting properties that you shouldn't be passing to the constructor, then you'll either have to segregate your serializable data from the rest or filter the non-serializable properties out.
For example, using one the examples I gave in a previous reply, you could do something like this to filter:
this._weapon
can be a function, there's little reason to have thereturn
in the middle of the pile (so to speak).So I just realized I'm doing a potentially terrible thing where I store character information in multiple places; there is the $chars variable which should be the one that actually holds all the data, the $player variable which holds the character you are currently playing as (I intend to have the ability to change perspectives), and the $party variable with is an array representing your current battle party.
So... I was under the impression that JavaScript passed objects by reference. if you do obj2 = obj1 and change something on obj2, it changes obj1 as well. If I do something like that in the browser console, that's how it works! But it seems like when actually doing stuff in the story, it doesn't work that way. For example, if you grab any item in the game, it will show a special message for the first time you get something, and set a flag on the player character saying that the message has been shown. That flag is set on $player, but it's not set on $chars.ellie.
I've already got workarounds in mind, namely more getter functions and replacing object references with ids... but as a bit of curiosity, can you explain to me why this happens?
I believe that this would result in the situation where two variables that used to point to the same instance of an object now point to two different instances of a similar object both with the same attribute values. In this case changing the value of an attribute in one instance would not effect the other.