2026 Summer Roguelike project, Part 8

 from Red Blob Games
18 Aug 2026

Part 8 adds the user interface. I had a lot to work on this week:

The Python tutorial spends some time refactoring earlier parts, but I skipped that. If I need to change things, I can use Jujutsu to go back to an earlier Part and change it there. The Python tutorial adds an exception thrown if a turn isn’t taken. Javascript’s exception handling is nowhere near as nice as Python’s, and I decided to just return false instead.

The interesting code in Part 8 is how I treat items.

The Python tutorial does what most games do: it stores inventory information twice:

  1. An item points to its “parent”
  2. A “container” holds the item

When an item is on the map:

When an item is held in inventory:

To pick up an item we modify three things:

self.engine.game_map.entities.remove(item)
item.parent = self.entity.inventory
inventory.items.append(item)

And similarly to drop an item we modify at least four things.

But I want to make the spreadsheet editable. Having to modify things in three places that have to be in sync is not great for making the spreadsheet editable. I don’t want to edit something in three places. I’m using a different representation strategy. I’m storing the information in only one place by having the item’s location be a union:

There’s no x,y when the item is held in inventory. I use void when an item is destroyed/consumed.

But that’s only one direction. We do want the other direction! We want to know which entities are on the map, on a tile, or in someone’s inventory. I have a “spreadsheet formula” that calculates the inventory given by running query. I think it’s less error prone than manually maintaining the information. Indexes allow me to find the answer quickly without searching the entire table.

With the information in only one place, I made the location editable in the spreadsheet. You can click on the location of a potion and change it from map 3,5 to held by 1 to pick it up. You don’t have to edit multiple pieces of information to keep them in sync.

The next big thing was to add a dialog box to select an item from the inventory.

I learned a trick from /u/Admirable-Evening128 on Reddit[3]: I can use Javascript’s async and await for the event loop. This worked out great. In the “use item” code, I can wait for an answer:

let entity = await Layer.inventory.waitForAnswer();
if (entity === null) {
    return false; // action cancelled
}

I don’t have to write a series of classes with callbacks. The await pauses execution until the dialog box gets an answer, and then resumes execution on the next line.

Part 6 had introduced the idea of event handlers, and I had decided to wait until later to decide how I wanted to implement them. I’m glad I waited, because along with the async/await, I found a nice way to handle them. I created an object with multiple layer objects inside it. The top level event handler asks each of those whether that layer is visible. If it is, it will pass events to it. Those layers objects also control whether the HTML is visible, so that the event handlers and HTML stay in sync.

Here’s the game at the end of Part 8:

Email me , or comment here: