0 votes
by (150 points)

I have a passage (Dungeon Status) set up that when called, lets me print the entries in a Javascript array (dungeon) into a string (dungeonList):

<script>
	dungeonList = "";
	if (dungeon.length == 0) {
		dungeonList = "empty";
	} else {
		for (i=0; i < dungeon.length; i++) {
			dungeonList += dungeon[i] + ", ";
		};
	};
</script>
[(print: dungeonList)]

dungeon and dungeonList are both originally defined in my startup passage as an empty array and an empty string respectively.

The problem I have is that if I run (display:"Dungeon Status") on the starting passage, I get an error that dungeonList is undefined, which, I assume, means that it's trying to (print: dungeonList) before it runs my definitions. No matter where I put the definitions, it won't run them before it tries to (print:).

However, if I open a different passage first, then open one that runs (display: "Dungeon Status"), I don't get the undefined error, but I do get an empty string, which I take to mean that it's not running the script yet, but it has had time to run my definition.

It works perfectly fine when I call it after adding a new entry to the array, like so:

(link-repeat: "room")[
	<script>dungeon.push("room");</script>
	(replace: ?dungeonlist)[$dunPrint]
]

I've figured out how to get around it by adding in a (live:) that waits 1ms, then calls (display: "Dungeon Status") and then stops, but I feel like that's an odd hack I'd like to avoid if possible.

1 Answer

0 votes
by (63.1k points)
edited by
 
Best answer

Script elements do indeed seem to be run late, after macro code is parsed. 

You can abuse the set macro to run scripts as they're encountered: 

(set: _dummy to (function () {
    // your code here 
}()))

This also allows you to get at some of Harlowe's internal APIs, like the variable store: 

(set: $var to 1) 

$var

(set: _dummy to (function () {
    State.variables.var = 10;
}())) 

$var

This is not an officially supported feature though, so ymmv. 

And this is also an odd hack, haha. 

Also, you can massively simplify your code using <array>.join()

(set: _dummy to (function () {
    dungeonList = "";
    if (dungeon.length < 1) {
        dungeonList = "empty";
    } else {
        dungeonList = dungeon.join(', ');
    }
}()))

EDIT. Or, maybe even better:

(set: $dungeonList to (function () {
    if (dungeon.length < 1) {
        return "empty";
    }
    return dungeon.join(', ');
}()))
(print: $dungeonList)

Warning. I didn't test this code, so I may have made a syntax error or something. 

by (150 points)
edited by
Trying that on the variable declarations didn't seem to help, and when I tried it on the dungeonList code it gave me a "missing ) after ArgumentList" error that I'm not clever enough to root out. It doesn't give me an error when in the regular <script> tags like I already had.

Edit: Hadn't seen your edits, will take a look at them.

Edit again: As far as I can see, using one of the (set:) macros to run the function just makes that function now report that dungeon is undefined, and no matter where I seem to put the definition, even if I do it in a (set:), it won't run the definition before it runs that passage.

The only way I got it to keep from giving me an error on loading was to use your last code example, but instead of using the dungeon JS variable, use a $dungeon TS variable and do State.variables.dungeon.length, and so on.
by (63.1k points)
edited by

Where and how are you declaring these variables?

Edit: I just tested all the code, and as long as you're defining your variables in your story JavaScript area and defining them globally, it should work.  It's working for me.

Complete code:

JavaScript:

window.dungeon = ['blue', 'hello'];
window.dungeonList = '';

passage code:

(set: $dungeonList to (function () {
    if (dungeon.length < 1) {
        return "empty";
    }
    return dungeon.join(', ');
}()))
(print: $dungeonList)

(set: _dummy to (function () {
    if (dungeon.length < 1) {
        dungeonList = "empty";
    } else {
    	dungeonList = dungeon.join(', ');
	}
}()))
(print: dungeonList)

Result:


blue, hello


blue, hello

 

by (150 points)

Currently, I'm declaring them like so, in a passage tagged startup:

<script>
	var dungeon = [];
	var dungeonList = "";
</script>

 

by (63.1k points)
You shouldn't really declare variables that way.  Put them in your story JavaScript, and attach them to window (or another object / a custom object).
by (150 points)

Changing them to

window.dungeon = [];
window.dungeonList = "";

fixed the undeclared issue. I'll have to see how the (live:) thing I did and the (set:) thing you did compare once I get the other bits working, but I'm satisfied with where it's at for now.

...