Summer Roguelike project

 from Red Blob Games
28 Jun 2022

Each summer r/roguelikedev has a summer event[1] in which we all make a simple roguelike, roughly following the libtcod roguelike tutorial. For this year’s summer event I’d like to do something more “fortress mode” and less “adventure mode”. This may prove too ambitious; we shall see. (Yes, it was too ambitious)

Click game for keyboard focus

Icons from game-icons.net[2], CC BY 3.0. Code repository on github[3]. I spent 32 hours on this, which isn’t a lot per week.

I’ve participated in the event several times and actually finished in 2020, using rot.js[4] and Javascript. That year I kept the scope down by implementing only the topics from the tutorial. Then in 2021 I implemented topics I wanted to try: thin walls, graphics, animations, openable doors, new map generator. I had many more things I wanted to try but I was too ambitious, and didn’t finish. These projects for me are not about making a complete, fun, polished game. This year it’s about exploring what a “fortress mode” game might involve. I had lots of things I wanted to try: friendly NPCs with skills and needs, jobs, room building, multi-agent pathfinding, resource management, crafting, day/night cycle, sleep/hunger/leisure/work cycles, farming, clothing, furniture, leisure. But it was all too much.

One reason the general advice is to “start small” is that if I do too many things at once it’s hard to learn any of them well. Around halfway through this project I realized that at my current learning rate, I need to have a much smaller project, so I reduced the scope quite a bit. And then towards the end I had to reduce the scope to be … a chicken simulator! 😂

 0  🖥 Setting up#

Compared to a Python project, I feel the set up is simpler in Javascript. I started with game.html:

<canvas id="game" width="660" height="660" />
<script src="build/_bundle.js"></script>

If I were using rot.js, there’s be one more <script> line. This year I’m not using rot.js. I’m using Typescript instead of Javascript. Here’s roguelike-dev.ts:

const canvas = document.querySelector("#game") 
               as HTMLCanvasElement;

The Typescript file has to be compiled into Javascript before the browser will run it. I’ve been using esbuild[5] for that. I really like it. I have lots of small projects and have standardized on having a file called ./build.sh that will build the project:

#!/bin/sh
mkdir -p build/
esbuild roguelike-dev.ts --bundle --outfile=build/_bundle.js

There’s a proposal to allow but ignore types in Javascript’s syntax[6], similar to what Python does. If that passes, then I won’t need this build step. But it only takes 0.06 seconds to run and I run it automatically on file save so it’s only a minor annoyance right now.

For the rest of this project I’m going to try to follow the same section numbers as the original Python tutorial and the r/roguelikedev summer event, but some topics won’t make sense in fortress mode so I will do something else instead.

 1  ✅ Moving around#

 1.1. Rendering

I’m using <canvas> this year, not <svg>. Conveniently, the game-icons svg icons are all a single<path>, and paths can be drawn to a <canvas> using Path2D[7]. To load the icon into memory, I can use esbuild’s loaders to make importing an svg work:

import person from "./game-icons/delapouite/person.svg";
esbuild --loader:.svg=text

This will make person the contents of the svg file.

Given a Path2D I can draw using any fill and outline style I want (including gradients, line widths, and shadows). I imported sprites in but I somewhere I need to store what style to draw them in. WebGL would open up more possibilities but I’m trying to follow DoTheSimplestThingThatCouldPossiblyWork[8] so I’m going to stick to regular <canvas>.

 1.2. Keyboard

For the keyboard I’m mostly following what I did two years ago. I add a tabindex to the <canvas> to allow it to receive keyboard focus. When it doesn’t have focus, I display a message telling the player to click on the game to give it focus. I think the alternative is to make the entire window’s keyboard events go to the game, but I like this approach better because it allows me to use the arrow keys for scrolling the page, or use an <input> box that receives keyboard focus.

When the canvas has keyboard focus it will get keydown events with each key pressed. I might want to get both keyup and keydown because I think it might be useful for building to hold down a direction key and then press a letter to build something in that direction. I’ll experiment with that when I get to building.

 2  ✅ Entity, render, map#

 2.1. Entities

I made an Entity class just like in the Python tutorial[9], but with two changes:

  1. I changed x and y being separate fields to a single Location object {x, y}. This is because I want to be able to pass the location around and also have special values like “not on the map” or “in inventory”. I will later extend Location to be an enum to hold special values.
  2. I changed char and color being their own fields to a single Appearance object {sprite}. I think the sprite characteristics should not be per-entity, but stored elsewhere. I’ll then extend Appearance to have modifiers to override the color, size, etc. per entity.
export type Location = {x: number, y: number};
export type Appearance = {sprite: string};

export class Entity {
    constructor (public location: Location, 
                 public appearance: Appearance) {
    }

    moveBy(dx: number, dy: number) {
        this.location = {x: this.location.x + dx,
                         y: this.location.y + dy};
    }
}

 2.2. Engine

As Python and Javascript modules act like objects, I didn’t see a strong need to make an Engine class just yet. The fields of the Engine in the Python tutorial become module-level variables in my project. If there’s a need (either to have multiple instances, or to pass it around to other modules), I’ll change it later.

 2.3. Map

The Python tutorial[10] for the map goes into NumPy dtype and ndarray, but I don’t need that in Javascript. I’m also not planning to have FOV in this project, so I don’t need the transparent and dark data. What do I actually need?

  1. A GameMap will have an array of tiles.
  2. The tile will be static data (the same for every instance of that tile: appearance, properties like walkability, etc.) and varying data (only the tile type right now, but maybe more later like water depth)

So I think for now the GameMap can be an array of strings, and the strings will be a index into the static data.

I put in some dummy data for now to test my renderer. Generating the map will be the next section.

Dummy data for rendering

 3  ✅ Generating a map#

 3.1. Terrain generation

Since I’m making a “fortress style” game, I want the main map to be a wilderness map with resources that the player can use to build their own settlement. What terrain do I want to start with?

  1. one river going through the map
  2. grassland near the river
  3. desert away from the river
  4. mountains somewhere

I will start by drawing a meandering river through the map, and then define the other elements by how far they are from the river. Will that be a reasonable starting point? We’ll see.

Map with meandering river

Ok, I think it’s a reasonable river. Let’s add some terrain near it:

Map with river, grass, desert, with pattern textures

I started thinking about sprite graphics. I might want the same sprite shape but different color to mean different things. Some sprites should be smaller than the full tile. And there will be mobile objects like animals drawn on top of static objects like trees drawn on top of terrain floors. I think that’s a lot of information on a tile. I decided to change the floors to flat squares to make it easier to see the foreground objects:

Map with flat textures

I also decided that the starting point will be the west side of the map, with grass and a river. Then to the east will be a desert, and then mountains. This will create a “left to right” game flow as the player levels up and digs deeper for more minerals.

 3.2. Scrolling

I also need to make the map a bit bigger to have room for both wilderness and building. Since the map will be bigger than what’s on the screen, I need to have scrolling. Normally in a “fortress style” game I’d scroll with the mouse or with dragging, but since I still have a player character walking around, I made it scroll with the player character. To center the view on the player, wherever I’m drawing to the window with:

display.draw(x, y, ch, color);

I need to instead offset that position:

display.draw(x - player.location.x + WINDOW_WIDTH/2, 
             y - player.location.y + WINDOW_HEIGHT/2, 
             ch, color);

The way to think of this is in two steps:

  1. Subtracting the player position moves the player to 0,0
  2. Adding the width/2, height/2 moves from 0,0 to the center of the screen

A bonus would be to have a camera that’s separate from the player. Let’s do that:

let camera = {x: player.location.x, y: player.location.y};

…
camera.x = player.location.x;
camera.y = player.location.y;
…
display.draw(x - camera.x + WINDOW_WIDTH/2, 
             y - camera.y + WINDOW_HEIGHT/2, 
             ch, color);

Ok, it’s a separate object now but it’s always pointed at the player. Let’s change it so that it follows the player but can be behind by 2 tiles:

camera.x = clamp(camera.x, 
                 player.location.x - 2, 
                 player.location.x + 2);
camera.y = clamp(camera.y, 
                 player.location.y - 2, 
                 player.location.y + 2);

We can also make it stop scrolling before reaching the end of the map:

const halfwidth = VIEWWIDTH >> 1;
const halfheight = VIEWHEIGHT >> 1;
camera.x = clamp(camera.x,
                 map.bounds.left + halfwidth,
                 map.bounds.right - halfwidth+ 1);
camera.y = clamp(camera.y,
                 map.bounds.top + halfheight,
                 map.bounds.bottom - halfheight + 1);

I think there are more fun things that I could do but I’ll leave that for later.

 3.3. Building

3.3.1. UI

The generated map is only the wilderness. In this game the player will be building the rooms. I need to figure out the UI for that. If I had a facing direction then the player could build in front of themselves. My first idea was to have you pick up building materials and then place them for building. But the keystrokes for that are annoying. If you want to build a wall on tiles 1, 2, 3:

  1  2  3
  4  5  6

You have to first pick up the stone, then move to 4, press Up to face towards 1, press E to build, then press Right to face right, then press Right to move right, then press Up to face towards towards 2, press E to build, etc.

It’d be simpler if you didn’t have to pick up the materials, but that’s not the real problem. It’s three keystrokes per tile. I want to reduce the number of keystrokes per tile.

The next idea was to have a build key B to switch to build mode, like vim’s insert vs normal mode. In build mode you can move the cursor around freely to build things. Then press B again to go back to the player mode. What are the keys in build mode? I guess you’d have to choose what to build: W for wall, S for stockpile, etc. In the example, you would move to 4, press B to switch modes, then Up to move to 1, W for wall, Right to move to 2, W for wall, Right to move to 3, W for wall, then B to exit build mode, which would return you back to the player at 4.

This is two keystrokes per tile.

The next idea was to have commands inside build mode. In the example, you would move to 4, press B to start build mode, W to start wall-building mode, then Up to move to 1 and build a wall there, then Right to move to 2 and build there, then Right to move to 3 and build there, then W to exit wall-building mode, then B to exit build mode, which would return you back to the player at 4.

This is down to one keystroke per tile. Other modes like stockpile-drawing mode might use arrow keys to mark a rectangle instead of a line. And others like placing an object might use one or two keystrokes to choose the object, but then return you to build mode. It’s looking like this:

Player and build and specialized modes

It’d be simpler if you could directly go into wall-building mode by pressing W from the player mode. But that means you can’t start a wall anywhere, but only next to where the player can walk. So I still need build mode to be able to choose where to start the wall.

Stepping back a bit, I had been starting from the “adventure mode” style movement for your player character, but I have to admit, I haven’t come up with any ideas for what you’d actually do in player mode. So maybe I should drop player mode, and treat build mode as the normal mode. Then you’d press the verb (W for walls, S for stockpiles, D for doors, etc.) which would take you into a specialized mode for creating that type of object, and then you’d return to the main mode to move around.

I think this means I can’t use the noun-verb approach[11] I wanted to try last year. Traditional roguelikes use verb-noun, where you choose the action first and then you choose the object. You might choose throw, and then choose from a list of throwable items. With noun-verb you first select an object and then you pick the action to apply. You might first choose a potion and then the action could be throw or drink. Modern games, modern programming languages, and modern UIs typically use noun-verb. For example in a text editor, you first select some text and then you pick an action like cut. Since I want a different UI depending on which verb is used, I think I have to use verb-noun instead of noun-verb.

Other thoughts: Should I switch to the mouse? Dragging a line or rectangle probably makes more sense with a mouse. But I want to see how far I can get with the keyboard. I should also take another look at Dwarf Fortress keys[12].

3.3.2. Implementation

I refactored the code a little bit, moving keyboard handling into its own module. It’s starting to have a bit to deal with:

And I want it to do more:

For the different modes, I created a key map object:

actions = {
    key_ArrowRight() { playerMoveBy(+1, 0); },
    key_ArrowLeft()  { playerMoveBy(-1, 0); },
    key_ArrowDown()  { playerMoveBy(0, +1); },
    key_ArrowUp()    { playerMoveBy(0, -1); },
};

In Javascript, { … } works both as a map (Python dict) and an object. When a key 'ArrowRight' comes in, I can call actions["key_" + event.key]() to run the corresponding action. When I switch to wall-building mode, I’ll assign a different key map to actions.

There’s some duplicate code in there to handle the arrow keys for each of the building modes, but I’ll clean that up later once I have something that works.

Wall building

I implemented the beginnings of wall building but the UI is pretty rough. I need a way to preview the walls that are going to be put down. That means actions isn’t sufficient. I need to maintain state, and also hook into the rendering. Hm.

3.3.3. UI, part two

I often come up with more ideas during “beer mode”[13], a relaxed state in which I am more creative. This can happen while going for a walk, or going to sleep, or taking a shower (“shower thoughts”). I had a UI thought last night: I’ve been focused on building walls and then the rooms would come later. What if the player marked the room, and then the walls were calculated? I looked through my notes to see what other games did:

Of these, I liked Two Point Hospital and Prison Architect’s approaches quite a bit. They were not only easy to understand what room was going to be there, they displayed what was needed to make the room work. Prison Architect let you build the room and then then you will be told what to do to make it work. Two Point Hospital won’t let you even build the room until you populate it with the needed items.

I think the rooms-first approach works better with thin walls than with thick walls. So if I decide to do this, I need to:

  1. implement thin walls
  2. change the building ui to focus on marking rooms (also figure out how to allow non-rectangular rooms, and modifying existing rooms; see Two Point Hospital[16]’s UI for this)
  3. automatically calculate the needed walls
  4. add a door marking ui

I also realized that I was so focused on building walls that I hadn’t thought ahead to rooms, or what rooms are for, or how the player will interact with rooms, or whether I even need rooms. One advantage of following the roguelikedev summer tutorial is that I don’t have to think about that, and I can focus on implementation. But since I’m trying to make a different style of game, I really need to be thinking farther ahead. So I took a break from implementation and started adding some ideas to the rest of this document.

I slept on this idea and decided I should implement it. Thin walls will probably simplify things for my handling of rooms, even though it’s a little more work to represent them. There will no longer be door tiles, so NPCs will never be standing in a doorway. Instead, every tile will be part of a room, or the outdoors. There will be walls between rooms, and some of those walls will have open doorways in them.

Thin vs thick walls

Of course now I need to go back and update the implementation.

3.3.4. Implementation, part two

Fortunately I already implemented thin walls in last year’s r/roguelikedev project, so I went through that and picked out some useful functions. I represent walls as a tile location and then either W for the western edge of the tile or N for the northern edge of the tile. I don’t need the south or east sides, because those are the north or west sides of the next tile over. I’ve written about this before but it was last year that I got to really put it to the test, and it worked pretty well.

I also had to update the rendering code to handle walls. For now I have only walls and doorways. While I was working on the rendering code, I ran into some inconsistencies in coordinate systems. The tile coordinates are 1 step = 1 tile. The pixel coordinates are 22 pixels = 1 tile (although this might vary if I want to add zooming later). If I want to draw outlines, they are 1/22 of a tile wide. The sprite coordinates are 512 pixels = 1 tile. So their outlines are 23.2 pixels wide. Although it’s fine for the rendering code to keep track of these things, I also want the input-handling code, and maybe other modules, to be able to draw on the screen, and that means they need to know about these scales too. I need to pick a coordinate system and stick to it.

With thin walls, I need to change the input handling code from drawing walls to drawing rooms. To start with, the player will be able to mark a rectangle by pointing at the two corners. But I also want to support non-rectangular rooms so I might make it work by starting with a single rectangle and then adding/subtracting rectangles to it. Maybe: if you start a room in an existing room it adds a new rectangle. Maybe: press shift when starting a room to remove a rectangle.

Rectangular rooms

Minor: I’ve been pretty happy with my right hand on the arrows or numpad, and the left hand on the commands (R for room, W for wall). But Enter is far away from both, so I think I will change that to be a left hand command.

 4  ✅ Field of view#

My initial thought was that field of view doesn’t apply to a fortress-style game. And I don’t think I want fog of war either, as I will keep things simpler for this project by having small maps. But I think I do want to make it so that you only see underground areas after you excavate them. But I don’t think that requires saving per-tile data. Instead, I’ll have a rule that says you can see any tile if it’s open, or if it’s adjacent to a tile that’s open. As you dig into the mountain, you’ll be able to see the tiles adjacent to your corridor, but no deeper.

I’m going to convert this game to be real-time instead of turn-based. I think that makes more sense for a fortress-mode style game. What’s needed for this?

  1. The simulation needs to run every tick, not every key input.
  2. The key inputs need to be queued up and processed during the tick.
  3. I need to pay special attention to keyboard repeat. I think the right way to handle this is to queue up key down/up events, ignore built-in keyboard repeat, keep track of key down/up. But it gets tricky. If you press and release J and then press and release K in the same tick, I want to process J because it happened first, but do I save K for the next tick, or discard it? And what happens if I press and release J and then press but hold K? I’ve played games that get this kind of thing wrong, but I don’t know the right way to handle it.

Unfortunately the current state of the game has no NPCs (friendly or enemy) and that means nothing happens every tick. So switching to real time is not going to make a visible difference right now, and it’s going to be hard to spot errors. I decided to postpone it until I have NPCs.

 5  🐔 Placing enemies#

I want to have friendly NPCs that are performing the fortress tasks (construction, digging, crafting, farming, etc.). I think the focus here will be on pathfinding and job assignment, both algorithm-heavy problems. Given how far beyond a reasonable scope I am, I plan to not have enemy NPCs.

 5.1. Jobs, attempt 1

NPCs need to be assigned a job, and then move towards the job, and then do the job.

  1. Assigning: Goblin Camp uses the Munkres-Kuhn algorithm. It’s O(N³), but N is small so it’s probably ok. RimWorld and Oxygen Not Included have a job priority system that the player can control. Dwarf Fortress starts out with the player manually assigning jobs, but then can hire a “manager” dwarf who will then take over that job from the player. I really like the idea of doing something manually and then outsourcing or automating it.
  2. Moving. Pathfinding with A* is the obvious approach. But pathfinding with BFS will probably be an easier place to start. This may finally be a project where I can try out differential heuristics[17].
  3. Doing. A job takes some time and then has some effect on the world. During that time the job may involve locking some objects so they can’t be used by others.

The more I thought about this the more complicated it got. So I started drawing pictures. Some very smart programmers told me that smart programmers use state diagrams to think through all the cases, so I thought maybe I should try that.

State diagram attempt 3
State diagram attempt 4

Things I want to make sure I handle:

Unfortunately this conflicts with another goal I had: that each tile has at most one object. The problem is that if a job is cancelled, I need to drop the object, and that might be dropped on a tile that already has an object. What to do? Either I need to allow multiple objects on a tile (and figure out how to draw them on screen), or I need to complicate how jobs are handled. And if I allow multiple objects on a tile, a 1⨉1 stockpile can hold everything, and that isn’t great. But Dwarf Fortress allows multiple objects on a tile, and stockpiles are still useful. Hm. I asked on discord and zendarva said to distinguish items stored on a tile, and items dropped on a tile. The stored items would be limited to 1, and the dropped items would be unlimited. I like this idea. But there are still more issues, like what happens if the player builds a wall on a tile that contains objects? They all need to be moved somewhere else somehow.

After several days of working on this I realized that I’m stuck. I need to break this down into much smaller pieces to make progress.

 5.2. NPC movement

I decided a much smaller piece would be to get NPCs moving around randomly, ignoring walls, but in a real time loop instead of turn-based. I know the conventional wisdom in the ECS style is to unify everything but I think that goes too far for my tastes. I’m going to distinguish between agents that can move around on the map and take jobs, and items that can be either on the map or being carried. Both are types of entities but they’ll be in separate tables with different types of attributes. I might have a third category for animals, but will figure that out later.

NPC chicken moving around

The code structure is getting in my way. I had tried splitting things up into many files like I’m “supposed” to do. But it’s unclear to me at this point how I should structure it, and the current structure is messy. Keyboard input triggers a render, so the keyboard needs to know about the UI. The input tracks the cursor, and the UI needs to draw the cursor, so the UI needs to know about keyboard input. The keyboard input needs to pause/resume the simulation so it knows about the simulation module. And the simulation also needs to render. The entities need to know about the map so they can decide whether a movement is in bounds. The map needs to know about entities since there are entities on the map. Lots of circular dependencies.

section-5b-code-structure.png

I probably should’ve left everything in one big file until I had a better sense of what structure I needed. Just as premature optimization gets in the way, premature abstraction and premature organization can also get in the way, and it’s a lesson I keep failing to learn.

No one is ever going to see your first draft. Nobody cares about your first draft. And that’s the thing that you may be agonizing over, but honestly, whatever you’re doing can be fixed. …

For now, just get the words out. Get the story down however you can get it down, then fix it.

—Neil Gaiman

I placed a handful of agents in the world and implemented them moving around to random destinations.

Several chickens moving around

These are tiny steps but tiny amounts of progress is what I needed to get un-stuck.

This made me think about pathfinding a bit. Right now they move in straight lines, but ideally they would use pathfinding so that they don’t walk through walls. But what if there’s a room with no doors? I’d need to figure that out so that the agent stops trying to walk into it.

In games we can often redesign some rules to avoid a messy situation. And that made me wonder if I can avoid that messy pathfinding situation. I think I can, with two changes:

  1. Walls need to be built/destroyed instantaneously. The time after a room has been built but before the walls have been updated creates a lot of “churn” that is going to require frequent repathing.
  2. Room editing needs to ensure that every room has a door to the outside or to another room that has a path to the outside.

On one hand, I was excited about rooms being built wall by wall. I think it looks cool and it also creates a source of jobs for the NPCs. On the other hand, it adds a lot of complexity, and I need to cut back as much as I can on this project. So what if I make rooms insta-build but move the focus of my jobs to other things? I think it’ll be less cool of a game, but more likely that I can keep to the schedule. And since the scope my biggest problem right now, I think, sadly, I will probably give up on room building tile by tile.

And that means I can work on NPC jobs without dealing with rooms. I’ll implement outdoor jobs for now, and get back to rooms later.

 5.3. Resources

In addition to the floor types (grass, desert, water) I also want to place objects on top: trees, stones, berries, gems. This will be useful for gathering jobs, especially if I don’t have building jobs. Which of these jobs makes the most sense right now, since I don’t have room building? I think trees and stones are out. Let me work on berries. Food is a rewewable job type, so the NPCs will always have something to do.

  1. Berries grow in the grasslands. I want them to re-grow too, so that means I need to have some kind of growth clock. Maybe it starts out as growth 0 and goes up over time. When the berry gets eaten it goes back to 0. I’ll want to display this visually somehow, maybe with size or color.
  2. NPCs periodically pick a nearby berry to eat. “Periodically”? I think that means I need to add a hunger clock of sorts. Maybe it fed starts out as 100 and goes down over time. When it gets below 30 the NPC can go look for food. And when they eat food it can go up +50.

What do I need to make this happen?

  1. I need to implement Resources. An resource is similar to an Agent, but doesn’t move around, and cannot be moved. I will also at some point need Items. An item is similar to an Agent or Resource, but can be moved, or carried. A berry plant would be a Resource. A berry would be an Item that you can get from a berry plant. But for now, I will only have Resources, and NPCs will directly eat from there.
  2. Berry plants will need a growth value. In an ECS this would probably be a Growable component. I’m using union types instead so I’ll structure it a little differently.
  3. Agents will need a fed value.
  4. Simulation ticks should periodically increase growth on plants and decrease fed on agents.
  5. Simulation ticks should periodically evaluate NPCs to see if they need to eat. If they do, find a nearby berry plant and reserve it (this is the job system I described earlier, without the complicated cancellation system), and then pathfind to it, then eat the berry.

Agents, Resources, and Items sure are similar though. I can see why it might be useful to call them all Entity and treat them the same. There are sometimes tradeoffs in type systems between precision and convenience. For example, should meters be the same type as grams? If I’m purely going for type safety, then no. But in practice? They’re both double in my code. So I’m going to start out with these three being separate types, but I might change my mind later and merge them. Or maybe I’ll change them to Typescript’s union types. It depends on how I end up using these, and I can’t see that far into the future so I’ll do something to make it work now and then plan to change it later.

/* An agent is an NPC that can move around on its own, can only be on
 * the map, and can perform actions.
 */
class Agent {
    dest: Point | null = null;
    fed: number = 100;
    constructor (public id: string,
                 public location: Point,
                 public appearance: Appearance) {
    }
}

/* A resource is something on the ground that cannot move or be moved. */
class Resource {
    growth: number = 0;
    constructor (public id: string,
                 public location: Point,
                 public appearance: Appearance) {
    }
}

/* An item is an object that cannot move around on its own, can be
 * either on the ground or carried by an Agent, and cannot perform
 * actions. */
class Item {
    constructor (public id: string,
                 public location: Location,
                 public appearance: Appearance) {
    }
}

I’m going to split this work up into two parts. First, I’ll place Resources.

Resources abound

Then I need to periodically go through all the plants/trees and make them grow.

A thought: my desire to keep everything fast is getting in my way. Premature optimization? No, I am sure I will need to optimize this. But still, it’s getting in my way. In particular, I often want to build an index:

This kind of thing happens a lot. I find it’s simpler if I say that one of the directions is “primary”, and the others are “derived”. Then there’s a question of whether to keep the derived information up-to-date when updating the primary (slower but always up to date), or to rebuild the derived information occasionally (faster but can be out of date). I think I’m at the point where I should be focused on only the primary direction. I need to figure out whether it’s even the right data before I spend time trying to make it fast. Things like this make me wish in-memory databases were more commonplace in programming languages.

Anyway, back to growing plants. I’ll scan the map and increase growth every so often.

Resources grow over time

Next week, I’ll work on the job system where NPCs look for resources.

 6  🍎 Combat#

I don’t have enemies. I have allies. I considered some limited combat in the form of hunting for food, but I need to keep the scope of this project down, so no hunting. Instead, this week I will work on the job system. Initially, the jobs will be to eat food when hungry, as described in the previous section.

I broke this down into smaller parts.

 6.1. Eating

First, I wanted the agents to eat when they’re hungry and they’re walking over food.

Chickens get hungry (orange, red) and die (black)

I decided it wasn’t obvious which plants had food and which didn’t, so I changed the graphics to show up to three berries per plant:

Plants have berries when edible

It was cool to watch the chickens walk past a plant and eat some berries. But all the chickens died, because they don’t walk towards plants when hungry. They’re just walking randomly at this point.

 6.2. Finding food

The next step is to have the chickens decide where to walk. If they’re hungry, they’ll look for nearby food. If they’re not hungry, they’ll pick a random place on the map.

I had to decide where this code lived. My first thought was that the code that increases hunger would see if it exceeded a threshold, and that’s when I would tell the chicken to go look for food. But the other way would be when the chicken is periodically evaluating its options, it can consider hunger as part of its decision-making process. I decided that I would rather keep the entire goal-setting process in one place. That way if there are conflicts, they can be resolved in a single function instead of the logic being spread out in many systems.

I had some bugs that took some time to figure out. The main one was that I would see chickens stop walking and then starve. I switched the random number generator to be deterministic so that I could run the simulation again and see the same chicken starve. I added a line showing where each chicken wants to go. I added text showing the chicken’s id. With seed 12345 I found that chicken 12 would reliably stop moving then starve. I added a “Look” ui where I can move the cursor around and see information about what’s on that tile. I found the bug and fixed it, and then the next problem was that the chickens would starve anyway. They were walking for too long before considering whether they were hungry, so I tweaked the movement range and plant density and got most of the chickens to survive.

Lines to show where chickens are going

 7  🤔 Interface#

{ This section is thoughts on other games, not much about this project }

I used to write simulations and then come up with the interface, but I think that leads to parts of the simulation the player can’t see. These days I instead want to start with the interface. What does the player experience? What can they see? If they can’t see it, is it useful to simulate? What are the decisions they need to make, and what information do they need to make those decisions?

I realized I need to at least start thinking about this early in the project. I looked through my notes for other games I’ve liked: Dwarf Fortress, Rimworld, Oxygen Not Included, Factorio, Prison Architect, SimAirport, Airport CEO, Transport Tycoon, Two Point Hospital, Honey I Joined A Cult, Another Brick in the Mall, Academia School Simulator, Overcrowd, Evil Genius 2.

You’ll start out with a handful of colonists, and some supplies, similar to Dwarf Fortress, Rimworld, ONI. You’re trying to provide food, shelter, dining, entertainment, medicine for your colonists.

One of the things I very much want in a game of this style is gradual progression both in systems and complexity.

So what systems could I implement? With the big caveat that I won’t time for all of them:

Possible room types: bedroom, dining, kitchen, indoor storage, outdoor storage, farms, ranches, workshops of various sorts, recreation.

I don’t think I’ll get far enough this project to build most of the ideas I’ve described in this section. However, there are two I need:

 8  ❌ Items and inventory#

{ I fell behind and didn’t get here }

I have gotten rid of the player character so there’s no player inventory. But the NPCs could be carrying things. And there are things on the map. I think I need to figure out stockpiles and also NPC inventory. The simplest thing would be for NPCs to carry only one thing at a time, or only as much as is needed for the current job.

The bigger topic this week is that I need to revisit rooms. I think some rooms will have walls and some will not. Rooms with walls need doors. I need pathfinding to not take NPCs through walls. I’d like non-rectangular rooms but that might be something to add later. Do rooms have floors? What happens to trees and plants when you place a room somewhere? What are the room types, and what are they for?

 9  ❌ Ranged scrolls and targeting#

{ I fell behind and didn’t get here }

I won’t have scrolls or targeting. Instead, I need to figure out pathfinding. I have a bunch of ideas for making it fast, but I need to get all of that out of my brain and first make pathfinding work at all, even if it’s slow.

 10  👎 Saving and loading#

I am planning to skip this. It’s not hard, but it’s not something I’m interested in spending my time on for this project. I need to keep the scope down for my first fortress style game, and can revisit this the next time I work on a game like this.

 11  ❌ Dungeon levels#

I don’t plan to have z-levels.

 12  ❌ Increasing difficulty#

I want the player to learn more systems as they scale up. This could be more advanced tech, something to support larger populations, or something involving increased skills. But I won’t have any ideas about what this might actually involve until I get the basic gameplay down.

{ I never got the basic gameplay down, so this didn’t come up }

 13  ❌ Gearing up#

Tools could qualify. A tool-making workshop could give NPCs tools, and then the tools would make them more effective. But I think that’s a stretch.

 14  😀 Conclusion#

This was a fun project. I got nowhere near as much done as I had hoped to, but I also learned a lot and am better prepared for the next time I attempt something like this.

I copied the grid drawing and event handling from a previous project, so I could have something running right away. I was pretty happy with my map generator, not using Simplex/Perlin noise but instead having a simple left-to-right pattern.

I was hoping that I could find a use for a player running around, like in Factorio, but I should’ve thought about this harder before implementing that player. I ended up removing it. Next time I will start with a cursor instead of a player.

Converting the roguelikedev engine from turn based to real time was surprisingly easy, except for handling keyboard repeat. I have some ideas for that but I didn’t implement it. That’s something I would’ve done better if I had started with real time instead of starting with turn based and then converting it.

The room building turned out to be more complicated than I thought, especially to interact with jobs. As the room is built, paths would get invalidated. Instead of getting to this point, I thought a little bit ahead and realized this problem before I got to it. I tried switching to “thin walls” and I think it might make things simpler, but I can’t be sure. I think room building is complicated enough that it might be worth making a separate prototype where I can try out a few different ideas. I need to figure out connectivity, disconnected rooms, room editing, door placement, etc. I didn’t realize room building would be complicated, so this was probably my first sign that I had too large of a scope.

The jobs system turned out to be far more complicated than I thought. The main thing I was trying to deal with was minimizing special cases. For example, if the player cancelled a room construction, the NPC carrying building materials to the site would drop them, but what if there’s already an object there? They might have to find another place to drop the item, and pathfind there. But what if a different NPC has dropped an item there in the meantime? Thanks to some helpful people on discord, I decided I should allow unlimited dropped items but only one intentionally placed item on each tile. But I never got around to implementing this.

Splitting the code into modules slowed me down quite a bit more than I expected. I should’ve left most everything as one big file until I had a better sense of the natural module boundaries.

I should’ve thought more about the core game loop at the beginning of this project. There were many times when I didn’t know what to implement because I didn’t know where I was heading. Around halfway through the project I started thinking about it, but I was demoralized by the room and job systems both being much more complicated than I had wanted. I ended up reducing the scope quite a bit. I was hoping to have some NPCs moving around doing simple jobs only. But I had to reduce the scope even more, so I ended up with a chicken simulator. I’m reasonably happy with the chicken simulator but I feel like I need to do this project three to five more times to really get a hang of it.

The next time I do a project like this I would like to really think carefully about the job system and the room system — data structures, states, algorithms — before I code anything up. State machines in particular have been helpful for me to understand all the corner cases, and ideally find a design that avoids them.

Maybe next year!

Email me , or tweet @redblobgames, or comment: