In this tutorial, you'll expand your twine inventory system to use both arrays and datamaps to build an advanced inventory system

Since focusing on Twine in the last year, Jezner has acquired a lot of visitors looking to create Twine inventory systems. In fact, my tutorial on creating a basic inventory is one of my most viewed articles.

And while this tutorial will expand on that tutorial in every way, this solution is just one solution in a sea of them. That’s the beauty of code. There is a multitude of solutions yet, that’s also a pain point. After all, of all the solutions, there are few that will fit your story like a well-tailored glove.

This is why it is important to understand the mechanics of any code-based system instead of blindly copying and pasting code. Doing so, may solve your immediate problem, but like introducing an invasive species. It may solve one problem, but cause a whole lot of other problems.

Inventory approach

For your inventory, you will create data maps for each object in the inventory. This isn’t a big problem since we have only five objects. When a player adds an item, we put it in an inventory array.

You don’t have to worry about players dropping items, but that’s something you could always add to the hard mode.

While other Twine inventory systems print out the inventory on-demand, you’ll print out the player’s inventory with each passage. When they click on an item, they will be taken to the item description and then back to their original location.

And we want to display their inventory on select passages. Meaning, you don’t want to see the inventory on the item description or introduction passages.

Finally, you need to place objects. In easy mode, items are placed in a fixed location but in hard mode, the items are all random.

That’s a big laundry list. So download the starter project (or use your story in progress) and meet me in the next section.

Defining Objects

When we last left off, we define a single object for the car keys. We defined it like such:

(set: $carKeys to (dm:
  "id", 1,
  "name", "keys",
  "description", "These look like Bernie's keys to his pickup.",

Now we want to define four other objects: a flashlight, batteries, a gas can, and a chili recipe. Open the startup passage and add the following:

(set: $flashlight to (dm:
  "id", 2,
  "name", "a worn flashlight",
  "description", "The flashlight is worn and well used. Quite useful in dark areas.",
(set: $battery to (dm:
  "id", 3,
  "name", "flashlight batteries", 
  "description", "A package of batteries. Perfect for a flashlight.",
(set: $gasCan to (dm:
  "id", 4,
  "name", "Can of gas",
  "description", "a gallon of the good stuff. Should be enough to escape.",
(set: $recipe to (dm:
  "id", 5,
  "name", "Bernie's chili recipe",
  "description", "Bernie's receipe is scribbled instructions of a gas receipt. It's best left in the same warehouse which houses the lost ark.",

While there are not a lot of objects, that was a lot of code. Right away, you can see some of the disadvantages of this inventory solution. Believe it or not, all this code actually saves you from writing more code.

Right now, all those statements are going to add unwanted link breaks. Wrap the entire stater passage in braces: {}. This will remove any unwanted breaks.

Here’s the opening brace:

Adding an open brace to remove white space

And here’s the closing brace:

Adding an closing brace to remove white space

We have one more adjustment. We won’t be using the player’s name. Open the Introduction passage. Delete the following code:

{(set: $name to 
  (prompt: "What is your name?", "Vegetarian Zombie")

And update the first sentence to remove the player’s name. Your introduction should look like the following:

A screenshot of a passage with all the code removed.

Nice job! Now it’s time for some upgrades!

Upgrading your inventory

Right now, you have the inventory code appearing just in one passage. You could copy and paste it to each individual passage but that’s creating a lot of unnecessary work for you. Rather, a great place is to use a footer passage.

The footer passage is a special passage that is added to the end of every passage in your story.

Start by creating a new passage by clicking the green +Passage button.

A screenshot creating a new passage

Give it the name: Inventory. Make sure you give it the tag: footer. It should look like the following:

Adding a footer tag to the screenshot.

By applying the footer tag, this passage will be displayed after every passage. Okay, add the following code to the Inventory passage.

{ You are carrying: (for: each _item, ...$inventory) [
  (print: _item's name)(if: $inventory's last is not _item)[, ]

It should look like the following:

The code showing how to print out inventory contents

This code does a few things. First, it starts with an HTML line break. This just adds a line break under any content to give it padding. The braces {} remove any line breaks between them.

The code then loops through each item in the player’s inventory and print’s out the item’s name. Finally, if the item isn’t the last item, we add a comma after it to make it easier to read.

Now let’s add some items to the player’s inventory. Open up the starter passage, and add the following at the bottom of the passage before the closing brace:

(set: $inventory to $inventory + (a: $flashlight, $battery))}

Now play your story. You’ll notice that our inventory is displayed in the introduction passage.

A screenshot showing the inventory being printed out in the introduction

But, if you play through the story, it appears where it should:

You only want this to appear in a story passage. This is where tags come into play. The idea is to tag story passages.

Open each of the following passages. Add a tag to each passage called, Inventory

A screenshot showing all the targeted passages where they inventory will appear.

Each passage should have the following tag:

A passage showing inventory tag

Now for the code. Open up your Inventory passage. As you know, this passage will be run on every single passage but you only want it to run on passages with the Inventory tag. This is a great use case for the (if:) macro.

Add the following:

{ (if: (passage:)'s tags contains "Inventory") [
You are carrying: (for: each _item, ...$inventory) [
  (print: _item's name)(if: $inventory's last is not _item)[, ]

It should look like the following:

A screenshot showing the inventory system.

The (passage:) macro returns the current passage. The passage is a type of data map and the tags property is an array of tags. So this code checks to see if the Inventory tag is contained in the passage’s tag list. If it is, it prints out the player’s inventory.

One last thing – you need to clean up the Camp Entrance passage. Open the passage, and delete all the code at the bottom:

Congrats! You now have a nice-looking inventory appearing on each page.

Where to go from here

Believe it or not – you still have work to do with your twine inventory. At this point, players can see what they are carrying but they can’t inspect items or even pick up items. You also have to sprinkle the items throughout the story world. You also have a villain to create. So there are still lots of things left to do.

Again there are lots of different Twine inventory systems out there so if you don’t the approach detailed in this tutorial, then give it a Google search. There are lots of different ways to do this.

This is a good time to take a break, have a nice cup of java, and meet me in the next tutorial.

Leave a Reply