Hi, I'm a new Twine user with a programming background, which is getting me in trouble

. I'm having a problem with JavaScript objects in Twine. I suspect I am brutally abusing Twine in trying to do this, but I'm not really sure why it isn't working. My problem may have something to do with an aspect of JavaScript that's obfuscated by the fact that I don't know the Twine back-end. I've searched through the forum and known issues list, and I don't think this behavior is described anywhere else.
When I create an object like this:
<br />::Start<br /><<init>><br /><<print $MyObject.name>> <<print $MyObject.sayHello>><br /><br />::Init<br /><<set $MyObject = {<br /> name: "I am a JavaScript Object!",<br /> sayHello: function() { return "Hello world!"; }<br />}>><br />
I will get output like:
I am a JavaScript Object! Hello world!And if I create a new passage [[DebugMyObject]] as follows:
<br />::DebugMyObject<br /><<print $MyObject.name>> <<print $MyObject.sayHello>><br />
and display <<DebugMyObject>> in the Start passage, I get the same output.
BUT, if I add a link [[DebugMyObject]] and follow it, I get:
I am a JavaScript Object! <<print>> bad expression: Property 'sayHi' of object #<Object> is not a functionI'm baffled by the fact that the name property retains its value, while sayHi stops working. I suspect that for some reason, the value is actually still there, but something Twine does in between passages makes JavaScript forget it's a function.
I could make all my functions global (I guess this would mean attaching them to window, which I haven't tried but I just saw someone suggest in another forum thread) so you'd have a
function sayHi(obj)
instead of
$MyObject.sayHi().
That approach has some issues, though, so I'd prefer to avoid it if possible.
Am I doing something wrong or is this a bug or am I just trying to do something Twine is really not intended to be able to handle?
Thanks in advance to anyone who can help me out. I know this is a bit outside the common usage of Twine variables.
I've attached the tweecode export of my short sample story.
Comments
You might try attaching variables to state directly, as that may mean you are using the same variables each page, rather than a copy. That will impact on what happens if the player goes back I would guess.
I take it that part of the point of copying the variables property from one passage to the next in the
<b>state.history</b>
array is to allow true undo with reversion of variable states. In that case it sounds as if I need to decide on a per-object basis between having member functions and Twine's undo/back feature working properly.Is
<b>state</b>
the right place to attach globals? I read something in the Google group that suggested global functions should be attached to<b>page</b>
, but if<b>state</b>
represents the story state, story-related globals would reasonably go there. I'm not sure what the pros and cons of each approach would be. I'm thinking that the best option is to put a hash of variables somewhere, to minimize the possibility of accidentally giving something the same name as an existing property!JavaScript is quite different to other OO languages I am familiar with, with prototypes, rather than inheritance, for example, so this is why I suspect methods are not copied.
I am only guessing about state. I have checked that variables on state will get saved and loaded properly (I am using SugarCube though, no guarantee other formats will be the same). I have not come across a page object; do you mean passage? That will change when the player goes to a new page.
Global functions should be attached to the window object. They can then be called like other functions
In a script passage
window.sayHi = function() { return "Hello world!"; }
In a normal passage:
<<print sayHi()>>
You've answered my question, I believe. I'll have to mock up a new version of the test story and make sure it works the way I think it should, but it seems that if I create a hash on the
state
and create a retrieval function on thewindow
I can have a set of global passage-independent objects that can have methods but won't revert properly on undo; for objects that need undo functionality, I'll have to make them "dumb" data objects. Or I suppose I could have functions that restore their methods at the beginning of each new passage, but that's probably not necessary.Thank you for your help! This gets me unstuck on a number of things
If I modify your example slightly to use the long version of the 'display' macro and build it using SugarCude (SugarCube does not support the short version) you will see that it will retain your function because when the variables are cloned between passages (both formats do this) the SugarCube format also clones the function pointers.
NOTE: I did not need to change the other passages within your example. NOTE:
1. SugarCube has a StoryInit special passage which is automatically called when your story starts, and this would replace your 'Init' passage.
2. Function pointers are NOT stored in 'saves', so need to be re-added to the object prototypes via the 'after load' feature.
L, you're awesome! (And I managed to miss until just now that you put out a new beta version of 1.4.2 yesterday, which I will go ahead and start using instead of 1.4.1 - I hadn't downloaded beta 2 as it looked to be a couple months old and I figured I'd wait for the next one)
The 1.4.2 beta supports StoryInit in its included story formats as well.