+3 votes
by (190 points)
I'm trying to work out the best way to incorporate external data into a story. For example - weather sensors. I'd like to be able to have a function, or macro, that would run on story startup, go grab a bunch of online data, parse it down, feed it into a variable that Twine can access (and use to change story data easily), and ideally, sit a background timer to update the dataset every now and again.

I'm pretty comfortable with JavaScript and happy to set up some window.whatever extensions to make this work, but I'm not too familiar with Twine and Harlowe's lockdown-isation which seems to want to avoid me doing this kind of thing. Which I can understand, but nevertheless, I'm heck-bent on making this happen! Can I map Twine variables or datasets to object properties in JavaScript, which will also update (from JS back to Twine - not too worried if I can't map Twine changes back to JS TBH)

Any thoughts on the best way to approach this (or examples I'm missing in the forum) would be appreciated with open arms (and code shared back, down the line!)

TIA to anyone who's gone down this path and is happy to type about it!

- Dave

1 Answer

+3 votes
by (159k points)
selected by
 
Best answer

You need to state the name and full version number of the story format you are using, as answers can be different for each one. I will assume you are using the default version of Harlowe which is currently 1.2.4

Based on your question I understand you already know the following information, I am only including it here for the sake of anyone else reading this thread.

The developer of the Harlowe story format (1.x and 2.x) has deliberately designed and implemented it in such a way to restrict access to it's engine's internals using Javascript, and the story format currently has no Javascript API documentation.

 

You stated that you are willing to "map" Javascript variables to story format variables, the following example demonstrates that this can be done in a single direction.

note: The following Javascript example is not meant to demonstrate the best way to implement a namespace nor best practices in coding in Javascript, it was just quickly put together to show that something was possible.

1. Javascript used to define some variables with different data-types, this code would be placed in the story's Story Javascript area.

if (! window.GE) {
	window.GE = {};
}
GE.someString = "A Sting";
GE.someNumber = 123;

GE.someArray = ["A", "B", "C"];

GE.someMap = new Map();
GE.someMap.set("name", "aaaa");
GE.someMap.set("health", 20);

 

2. TwineScript used within a passage to assign the values of Javascript based variables into story variables, it demonstrates both the different syntax's that can be used to access the Javascript variables as well as that Harlowe's Array and Datamap features still work.

(set: $string1 to window.GE.someString)string: $string1
(set: $string2 to GE.someString)string: $string2
(set: $string3 to window["GE"]["someString"])string: $string3
(set: $string4 to GE["someString"])string: $string4

(set: $number to GE["someNumber"])number: $number
number X 2: (print: $number * 2)

(set: $array to GE["someArray"])array: $array
array length: (print: $array's length)
first element: (print: $array's 1st)
second element: (print: $array's 2nd)
third element: (print: $array's 3rd)
last element: (print: $array's last)

(set: $map to GE["someMap"])(data)map: (print: $map)
name element: (print: $map's name)
health element: (print: $map's health)

 

by (190 points)

@greyelf, this is extremely helpful - thank you! - particularly around the array access.

I also found this online, at furkleindustries

var _state = State;
window._state = _state;
function getHarloweVariable(prop) {
	if (typeof(prop) === typeof(undefined) ||
			prop === '') {
		return;
	}	
	return prop[0] === '$' ? 
		_state.variables[prop.slice(1, prop.length)] : _state.variables[prop];
}
window.getHarloweVariable = getHarloweVariable;

function setHarloweVariable(prop, val) {
	if (typeof(prop) === typeof(undefined) ||
			prop === '' ||
			typeof(val) === typeof(undefined)) {
		return; 
	}
	if (prop[0] === '$') {
		prop = prop.slice(1, prop.length);  
	}
	_state.variables[prop] = val;
}
window.setHarloweVariable = setHarloweVariable;

which gives me a neat window.setHarloweVariable(variable, value) call I can make from timer-based events or other triggers in JavaScript.

I hope other users find this post of utility! (now to stop cursing about how cross-origin restrictions have taken all the fun out of JavaScript...)

by (159k points)
edited by

I was aware of the furkleindustries solutions.

warning: That solution uses a scope escalation hack to allow access to undocumented internal features/functionality of the Harlowe's engine, this means that any of your code that relies on that hack could possible suddenly stop working if you rebuild your story using a later version of Harlowe if the developer chooses to alter how the engine works internally.

Also note that the Twine 2 application automatically upgrade all story projects to the latest version of the relevant story format bundled with the application, this means that if you update which Twine 2 application release you are using and if that release comes with a later version of the related story format then you will automatically be building your project with that later version of Harlowe.

...