0 votes
by (120 points)
I am new to  twine. I have created a Twine2 Game in Harlowe with an extensive inventory type of system. That system is based on datamaps. I have tried to create an option to Save the game using the save and load game macros. However, it does not seem to store the values in the datamaps.

 

For example: if I had picked up a Gun and in the datamap have marked (set: $Gun's isCarried to true) and then save. When the save is loaded, it does not seem to recognize that $Gun's isCarried to true.

I am looking for some advice or direction from someone who has created a twine game in Harlowe with datamaps and has successfully created a Save and Load game option for that twine story.

 

Not sure it matters, but I use twine that has been downloaded to my computer and has the graybox icon.

2 Answers

+1 vote
by (159k points)

This issue is caused by a bug in how Halrowe's (load:) macro deserialises Data-map (Javascript Map) objects that are contains in past moments of History, it incorrectly converts the Map objects into Javascript Array objects. This results in errors when you try to access the named elements of the original Map objects.

I strongly suggest you create a new issue on the Harlowe project's repository about this bug.

WARNING: The following background information has been simplified, so while it is conceptually correct it isn't 100% technically correct.

Lets assume you have a story project containing the following three passage.

1. Your story's startup tagged special Passage.

(set: $Gun to (datamap: "isCarried", false))

2. Your story's starting point Passage, I named mine "First"

original isCarried: (print: $Gun's isCarried)

(set: $Gun's isCarried to true)\
current isCarried: (print: $Gun's isCarried)

(link: "save")[(save-game: "Slot isCarried")]

(link: "load")[(load-game: "Slot isCarried")]

[[Second]]

3. The "Second" Passage.

current isCarried: (print: $Gun's isCarried)

(link: "save")[(save-game: "Slot isCarried")]

(link: "load")[(load-game: "Slot isCarried")]

If you used some Javascript to hack into the Harlowe engine you would learn that it has an internal variable named variables, and that this variable is used to store the current values of all story variables referenced by the current Passage. (and indirectly all previous passages in History)

If you looked the value of variables just after the startup tagged Passage was executed it would look something like the following.

<Object> {
	Gun: <Map> [
		0: {
			key:	"isCarried",
			value:	false
		}
	],
	__proto__: <Object> []
}

note: the __proto__ property in the above example has special meaning in Javascript, it is a reference to the objects Parent definition and this will become important later on. Currently it is referencing the definition of an empty Javascript Generic Object.

If you looked at the value just after the isCarried property of the $Gun story variable was changed it would be.

<Object> {
	Gun: <Map> [
		0: {
			key:	"isCarried",
			value:	true
		}
	],
	__proto__: <Object> []
}

If you look at the value after doing a Save and Load while still viewing the "First" passage then it would still look the same as the above example, you will also notice that the Gun property stills has a data type of Map.

If you move to the "Second" Passage and then look at the value you will notice that it's structure has changed to the following.

<Object> {

	__proto__: <Object> {
		Gun: <Map> [
			0: {
				key:	"isCarried",
				value:	true
			}
		],
		__proto__: <Object> []
	}
}

note: The value of the variables variable has been replaced with a new empty Object, and the properties of the  original Object referenced by the variables variable has been assigned as properties of the new Object's __proto__ property. This creates a parent / child relationship between the original Object and the new Object, and this is how Harlowe's variable History system works. You will notice that the Gun property stills has a data type of Map.

If you did a Load while viewing the "Second" Passage you would be automatically returned to the "First" Passage and you would notice that the value of variables is reverted back to the original structure like so due to History being rolled back one step.

<Object> {
	Gun: <Map> [
		0: {
			key:	"isCarried",
			value:	false
		}
	],
	__proto__: <Object> []
}

If you moved to the "Second" Passage again and this time did a Save & Load while viewing the "Second" Passage the value of variables would look like the following.

<Object> {

	__proto__: <Object> {
		Gun: <Array> [
			0: {
				key:	"isCarried",
				value:	true
			}
		],
		__proto__: <Object> []
	}
}

... and if you look closely you will notice that the data type of the Gun property is now Array and not Map, and that change in data type is what is causing all the issues.

0 votes
by (1.6k points)
My experience is that saving and loading does mince datamaps if you're testing within the downloaded Twine application -- but functions correctly if you publish the game to a file and run it in a browser.
...