0 votes
by (140 points)

I've been trying to figure out how to use datamaps in SugarCube 2.  I think the closest I've gotten is actually an array of arrays:

<<set $clothingset to [
  [1, 't-shirt'],
  [2, 'shorts'],
  [3, 'shoes']
]>>

What I am *trying* to do is set up a wardrobe system with various sets of clothes loadable/saveable.  There are two complications that make a datamap ideal:

1. Clothing can be destroyed/lost permanently.

The key would be an ID (numeric, assigned on item generation), value would be a name backup - so if the item was destroyed, it could look for another "two-handed sword" in the inventory and use that instead.

2. Clothing can cover more than one body part (i.e. a two-handed sword, dress, etc.)

When saving it, this would create multiple references to the same item, which is going to cause problems when I try to wear it again later.  I've tried to just use the array above and $clothingset.pushUnique() - but it still keeps both.

I figure if I am using a datamap, the key would be unique, so if added again it would simply overwrite it.  But I don't think any key-value functions are currently in SugarCube.  I know a little programming, but I'm working with someone who doesn't, so as much of this outside of pure javascript would be ideal.

1 Answer

0 votes
by (149k points)

You would generally use a JavaScript Generic Object to simulate a datamap in SugarCube.

<<set $things to {
	"key1": "value 1",
	"key2": "value 2",
	"key3": "value 3"
}>>

... and then use an Array of keys to track a set of these things.

<<set $have to ["key2"]>>
<<run $have.pushUnique("key3")>>


So your code could end up looking somthing like:

<<set $clothing to {
	"t-shirt":  {
		id: 3,
		name: "T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"shorts":  {
		id: 14,
		name: "Tight Shorts",
		description: "blah blah blah",
		covers: ["slot2"]
	},
	"shoes":  {
		id: 18,
		name: "Black shoes",
		description: "blah blah blah",
		covers: ["slot3"]
	},
	"red-dress":  {
		id: 26,
		name: "Red Cocktail Dress",
		description: "blah blah blah",
		covers: ["slot1", "slot2"]
	}
}>>

<<set $wardrobe to ["red-dress", "shoes"]>>

 

by (140 points)
reshown by

That is actually incredibly close to what I have - though my array probably looks more like:

{name:            "t-shirt",
 material:        "green",
 price:           1500,
 worn:            ["top"],
 id:              9
 },
{name:            "shorts",
 material:        "blue",
 price:           1200,
 worn:            ["bottom"],
 id:              10
 },
{name:            "dress",
 material:        "pink",
 price:           1500,
 worn:            ["top","bottom"],
 id:              11
 }, etc.

When I have the player wear it, I actually move the object from their inventory to $top, $bottom, etc. so as to track changes to the item before returning it to storage.  The problem is that sometimes it doesn't make it back to storage, gets sold since they saved their set, etc.

My plan was to save $top.id, $top.name; $bottom.id, $bottom.name... so when they go to wear their red shirt from their saved ensemble and it is missing... it can find the first alternative shirt (and notify them).

My plan was to iterate through the keys, if unable to find a matching id, it'd look for a matching name... because they could/would have multiple "shorts" or "shoes"

by (149k points)

Generally you would define every possible (clothing) item aviable in your story, which my $clothing structue was doing, and then use the keys of each of those defined items as needed.

So if you wanted to create different sets of clothing you could do somthing the following

<<set $clothingSets to {}>>

/* Add a 'two-piece' set. */
<<set $clothingSets["two-piece"] to ["t-shirt", "shorts", "shoes"] >>

/* Add a 'nice-dress' set. */
<<set $clothingSets["nice-dress"] to ["red-dress", "shoes"] >>

If you wanted to display a simple clothing sets list you could do something like

Clothes Sets: (simple)
<<nobr>>
<ul>
<<for _key range Object.keys($clothingSets)>>
	\<li>_key : <<= $clothingSets[_key].join(", ")>></li>
<</for>>
</ul>
<</nobr>>

If you wanted that list to be a little more detailed you could do

Clothes Sets: (detailed)
<<nobr>>
<ul>
<<for _key range Object.keys($clothingSets)>>
	\<li>_key : <<= 
		$clothingSets[_key]
			.map(function (item) {
				return State.variables.clothing[item].name;
	 		})
			.join(", ")>>
	</li>
<</for>>
</ul>
<</nobr>>

...although you would ideally create your own custom functions to retrieve the Name of each of the relevant items from where those items are defined, and your own custom widgets/macros to display that information.

If you wanted to track what the player is currently wearing you could do something like.

/* Wear some clothes. */
<<set $wearing to {
	top: "",
	bottom: "",
	feet: ""
}>>
<<set $wearing["top"] to "t-shirt" >>
<<set $wearing["bottom"] to "shorts" >>

If you wanted to list which items the player is currently wearing for could

You are currently wearing:
<<nobr>>
<ul>
<<for _slot range Object.keys($wearing)>>
	\<li>_slot : <<if $wearing[_slot] is "">>Nothing<<else>><<= $clothing[$wearing[_slot]].name >><</if>></li>
<</for>>
</ul>
<</nobr>>


Note: If the definitions of your (clothing) items don't change during the life-cycle of a play-through then you may want to consider moving those definition from a story variable to the special setup object. Doing so reduces the amount of storage required by the History system (and related Save system), You could also do the same with the $clothingSets if there definitions also don't change once they have been initialised within StoryInit.

eg. Using setup instead of $clothing within your StoryInit.

/* Define all known clothing. */
<<set setup.clothing to {
	"t-shirt":  {
		id: 3,
		name: "T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"shorts":  {
		id: 14,
		name: "Tight Shorts",
		description: "blah blah blah",
		covers: ["slot2"]
	},
	"shoes":  {
		id: 18,
		name: "Black shoes",
		description: "blah blah blah",
		covers: ["slot3"]
	},
	"red-dress":  {
		id: 26,
		name: "Red Cocktail Dress",
		description: "blah blah blah",
		covers: ["slot1", "slot2"]
	}
}>>

eg. Using setup instead of $clothing when displaying a detailed list of clothing sets.

Clothes Sets: (detailed using setup)
<<nobr>>
<ul>
<<for _key range Object.keys($clothingSets)>>
	\<li>_key : <<= 
		$clothingSets[_key]
			.map(function (item) {
				return setup.clothing[item].name;
	 		})
			.join(", ")>>
	</li>
<</for>>
</ul>
<</nobr>>

 

by (140 points)
Thank you for the information.  I wish I could use it in this context, but the whole reason for the ids is because there could be 3 or 4 t-shirts in their inventory.  They are able to select a color on creation as well as a few other properties that can change over time (thus each is a unique object).  Imagine items like you'd encounter in Diablo 3 or something - other than a select few items, you really wouldn't want to have a list of every possible combination.

The way the game is set up, they'd want to change regularly for specific purposes (i.e. set ideal for certain events), so I was just going to make a list of the IDs (i.e. 3, 5, 16, 12) etc. - but they may no longer have that specific item because they upgraded or lost it - so I wanted to keep a little extra information to help the game find an appropriate replacement.  Thus the datamap.

I guess I'll just have to make a custom array with the info I want to keep as reference and specifically handle any possible duplication while saving.  Would be so much better if I could do something like $set.main.add($top.id, $top.name).
by (149k points)

@quin2k

> but the whole reason for the ids is because there could be 3 or 4 t-shirts in their inventory.

You can have multiple of both the same item and of the same type of item within your inventory using my method.

<<set $clothing to {
	"t-shirt":  {
		id: 3,
		name: "T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"t-shirt-red":  {
		id: 4,
		name: "Red T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"t-shirt-blue":  {
		id: 5,
		name: "Blue T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"t-shirt-floral":  {
		id: 6,
		name: "Floral T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	/* The rest of the definitions...*/
}>>

\<<set $inventory to []>>
\<<set $inventory.push("t-shirt-red")>>
\<<set $inventory.push("t-shirt-red")>>
Inventory with multiple (red) t-shirts: <<= $inventory.join(", ")>>

\<<set $inventory to []>>
\<<set $inventory.pushUnique("t-shirt-red")>>
\<<set $inventory.pushUnique("t-shirt-floral")>>
Inventory with multiple t-shirts: <<= $inventory.join(", ")>>


note: Ideally the ID of each item would be a String based one inseted of an numeric based one, because a formated Strings is generally easier for you to identify and remember. So if I was using a structure like the above I would write it more like the following.

<<set $clothing to {
	"t-shirt":  {
		id: "t-shirt",
		name: "T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"t-shirt-red":  {
		id: "t-shirt-red",
		name: "Red T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"t-shirt-blue":  {
		id: "t-shirt-blue",
		name: "Blue T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	"t-shirt-floral":  {
		id: "t-shirt-floral,
		name: "Floral T-shirt",
		description: "blah blah blah",
		covers: ["slot1"]
	},
	/* The rest of the definitions...*/
}>>

 

...