Return Object From JavaScript Function

Please specify version and format if asking for help, or apply optional tags above:
Twine Version: 2.3.8
Story Format: Sugarcube 2

I’m setting up an Entity system that uses passages to initialize an Entity that is then processed by functions before being returned to the original call, like so:

  1. <<set $foe to getEntity(“goblin”)>>
  2. This calls a function that uses state.display(“foe_goblin”) to init the new goblin Entity object
  3. Function returns object reference and stores in $foe

However, this doesn’t work. I can confirm that everything works up to the point the object is stored in the $foe variable (it gets an undefined). While I can get around this using other means, I’d like to know why $foe isn’t its value when the function is returning the object.

I come from a Java and C++ background, so I’m iffy on JS and designing object-oriented systems with Twine, FYI.

1 Like

Hey Gary,

State.Display (which is actually deprecated) only renders the contents of the passage (by converting it to HTML and displaying it in the game area). It doesn’t return anything. I have actually done something similar to what you’re trying to do (using passages to store character information, etc.).

Broadly, what you want to do is have each entity passage store its “entity” object (or whatever it is) into a consistently named temporary variable used in every entity passage. Example:

<<set _info to { 
   name: "My Entity Name",
   numberOfArms: 543
}>>

Here’s what you want to do for something like getEntity:

function getEntity(name) {
    // Render passage with the given name into a blank div.
    $("<div></div>").wiki("<<include foe_" + name + ">>");

    // Now, the temporary variable _info will be set to our entity info, so just return that
    return temporary().info;
}

Since the <div> we render into is never actually added to the DOM, anything the passage foe_goblin or whatever passage outputs will never appear, but the results of its assignments will. As long as every entity passage just sets the _info variable, this will work.

Example Usage:

<<set $myFoe to getEntity("goblin")>>
1 Like

Works like a charm, thank you. Definitely cleaner than my other ideas.

1 Like

If you’re not going to use the output, then you don’t need to bother to capture it. I.e.,

$.wiki("<<include foe_" + name + ">>");

SEE: https://www.motoslave.net/sugarcube/2/docs/#methods-jquery-method-wiki

1 Like

Using the markup engine to run some JavaScript is a fairly heavyweight way to initialize an object. If you’re set on using passages to hold your initialization code, then I’d probably suggest simply executing the the JavaScript directly.

Example getEntity function: (goes in Story JavaScript)

window.getEntity = function getEntity(entityName) {
	// Complain if the entity passage does not exist.
	if (!Story.has(entityName)) {
		throw new Error('no such entity: "' + entityName + '"');
	}

	try {
		// Execute the entity passage and return the result.
		return evalJavaScript('(' + Story.get(entityName).text + ')');		
	}
	catch (ex) {
		throw new Error('error initializing entity ("' + entityName + '"):' + ex.message);
	}
};

Example foe_goblin passage:

{
   name: "A common goblin",
   hp: 3
}

Example usage:

/* Init two gobbos. */
<<set
	$gob1 to getEntity("foe_goblin"),
	$gob2 to getEntity("foe_goblin")
>>

/* Init a wooden sword. */
<<set $sword to getEntity("item_wooden_sword")>>

You should get the idea.

2 Likes

That’s a fair point. Not sure about OP, but in my case I was varying the values in the object based on game state such as various variables.

Example: An NPC whose name changes as you learn more about them.

1 Like

Thank you! This will work perfectly.

Also a great idea for more malleable Entities throughout the game’s execution. Thanks again for the help!