You wake up alone on a broken colony ship. You're starving hungry—how long were you asleep?—but the door won't open. You'll need your wits about you to take apart, diagnose and repair the ship's limping systems, stay alive, and figure out what the hell happened.
I was looking back through posts I've made about Adrift on the r/roguelikedev subreddit about Adrift over the last year or so and I found some cool stuff that I'd written, so I wanted to collect it in one place so it's more easily linkable. There's no real theme here other than "things I've thought about Adrift".
If you've not heard me talk your ear off about Adrift already, this is probably the best place as of writing to understand more about what kind of game it is I'm trying to make. The fragments are in chronological order, so the later parts are closer to the current state of the game.
I did a few mockups in REXPaint after seeing Kyzrati's talk at Roguelike Celebration 2018 and having my excitement about developing Adrift rekindled. These mockups are still pretty close to how I want the game to feel, though I no longer like the RPG-style dialog box.
I wrote a little about what I'm going for with map generation:
Adrift has some unique map generation requirements which make it difficult to reuse ideas from other roguelikes. First, the map has to be completely filled. It doesn't make sense for there to be empty space on a spaceship like it does in a dungeon. So every single tile on the map must have a purpose. Second, the map should feel designed, but still unpredictable. A spaceship is a carefully constructed artifact, and the map should follow some kind of discernible logic, while keeping it interesting to replay and focusing on responding to the situation rather than memorizing a premade map.
This still holds true.
My theory at the moment is that constraint programming is a good toolset for generating these sorts of maps. With a constraint solver you can set up variables (e.g. what kind of room is this? Which walls have doors?) and then constrain the values of those variables so that only certain combinations are allowed. I've been using choco-solver, and this week I discovered the choco-graph extension, which allows enforcing constraints like "every room on the map must be reachable", which was a huge breakthrough for this technique.
I'm dividing the world into 5x5 rooms, and the solver operates at the level of the room, not individual tiles. For each room slot, the solver chooses what type of room will go there, and then enforces that the adjacent rooms have to match the kind of walls that the just-placed room has. It keeps going like that until the whole map is full, at which point it return the completed map, or until it runs into a contradiction, which will cause the solver to backtrack and try a different placement. The algorithm is very similar to Wave Function Collapse, but with the added ability to backtrack, and better ability to enforce global constraints such as "connectedness".
The maps still feel pretty random and messy, though, not "designed". I think some additional constraints will be able to help push the map towards a more designed feeling, such as enforcing some kinds of symmetry in local areas, and restricting how corridors can be placed to avoid corridors that just open out into other corridors and similar nonsensical layouts.
I've since played fairly extensively with this approach this and haven't found success with it. But BSP is working great, and I'll be able to reuse the constraint programming stuff elsewhere, I think.
Data with a soul
I wrote about data-driven design early on, and Adrift continues to be a very data-driven design, with the vast majority of content being specified in YAML files.
This week I moved all the info about items, terrain, furniture and so on out of hard-coded Scala and into YAML files. I really like Brian Buckelew's theory that stories are just data with a soul, and I've seen data-driven development be a huge acceleration boost for games like Cataclysm, and I want to do that too. I reused some code from another project for parsing and sampling item-generation tables from JSON/YAML, so it was nice to not have to reinvent that.
I implemented room prefabs in the map generator, all defined in YAML, so now it's super easy to add new rooms and variations, and fill them with stuff. I didn't get to spend much time on actually creating them though, the game's still in a very early state and I'm trying to get all the systems in minimal working order before filling out too much content.
I've since evolved my opinion on YAML, specifically, as a data format. Here's my opinion: YAML sucks. But it sucks much less than having to write a custom editor.
I'll probably not put anything into the game that would require me to write a custom editor. If I can't figure out a way to express some data I need as YAML, I'll cut that feature. Building a custom editor is just too much work for an already extremely ambitious project (compared to the amount of time I have available to dedicate to it).
I had a small epiphany this evening chatting to Nato about storing items and their locations. Previously I've found a lot of tension between having items responsible for knowing where they're located in the game (in a backpack? on the floor? worn?) and having locations be responsible for knowing what items they contain (e.g. a map cell having a list of items it contains). We were exploring some options and we hit on the idea from the world of databases: if all your items were in some sort of SQL database, you could query by item ID or item location, no problem.
SELECT * FROM items WHERE location = (32, 12).
I don't want to go full-blown SQL, but that idea was pretty neat, and it eventually lead me to a solution to the problem of whether to store item-locations on items or in locations. That is: neither! Instead, have a data structure separate from items and locations which stores the item-location pairing, and supports lookup by location or by item.
I'm still using this system and it's working great. Highly recommend.
In which I somehow implemented four foundational systems while packing up my apartment
Apparently I called this "a short week"? But these systems are all fundamental to the game. Time is weird.
The game now supports color! Each category of items now has a foreground and background color from a palette, as well as a character. Eventually I'll implement a lighting system too, so the display will be quite colorful, but I want to keep the "raw" item colors in a pretty narrow range to keep things readable.
I'd forgotten that I wanted to implement a lighting system! I was thinking of an earlier antecedent of Adrift, an experiment that I did as Nintendo DS homebrew called Torch.
I'm not sure if I want to go back to this—I'd been thinking that a more austere graphical approach might be appropriate to the theme—but having recently been looking at Cogmind gifs again, I'm definitely on the fence. Cogmind looks so good!
The automatic doors (you know, like Star Trek) are now broken up into two components: a mounted presence sensor which detects nearby humans and emits an "activate" signal to items in the same tile, and the door itself, which can listen to the "activate" signal and open the door. If you take away the motion sensor (for instance, by dismantling it) then the door will be stuck. Eventually those mechanisms will also require power to work properly, and you'll also be able to hook up doors to other kinds of sensors and buttons.
I think this is going to be an interesting and unique part of Adrift.
I implemented a small message-based behavior system like the one Brian Bucklew described for Sproggiwood/Caves of Qud. I'm not sure I quite like what I have yet, but I'll run with it and see how far I get.
Still running with it. Still not sure I like it.
I started on implementing a system for item damage: sometimes, items have components that are broken, and you'll need to take them apart and fix them. But taking an item apart can also damage it if you're not careful or your tools are sub-par (for example, dismantling a computer display with a hammer is unlikely to leave any delicate components in-tact).
The system I implemented for this was very simplistic, and since Adrift is ultimately intended to be a very repair-focused game, I think it'll end up being expanded a lot. For example, one idea Nato had in this area was that items like doors that make a sound when you activate them should make a different sound if they're broken—and the sound should differ based on how the item is broken! For instance, if the motor in the door is broken, it might make an impotent "whrrrr"ing sound; if the motion sensor is broken it won't make any sound at all (since the door didn't activate); if a panel is bent it might make a "clonk" noise. I love this idea, though I think it would be too much work to apply it everywhere, doing it for some common items like doors would be well worth it.
Items should have a mass and a volume, and you shouldn't be able to pick up things that are too big or too heavy. An item's mass should be simply the sum of the masses of its components (and eventually, once there are containers, its contents), but an item's volume could be bigger than the sum of the volumes of its components.
Still haven't done this, but I still want to.
I want to make a start on the power system. I'm not quite sure how it should work yet, but I have thoughts about transformers and voltages and wires that you can drag across the floor to plug stuff into other stuff.
I implemented a very simplistic power system, just enough to make it so that the ship can run out of power and doors stop opening.
The Parts Have Parts
Adrift is a roguelike without combat. Instead, Adrift focuses very heavily on items, furniture, and environmental elements, as well as the systems of the space ship that is its setting.
I added support for non-rectangular rooms to the map generator, and to test it I added an air mix room with a bunch of gas tanks. None of them do anything yet, but eventually all those tanks will be hooked up to the machines in the center, and the machines will be regulating the atmosphere on the ship.
Nato added an airlock room, and I tweaked the map generation so they only spawn next to space. (Not much point having an airlock elsewhere, I suppose.) The structure of the ship is a spinning cylinder, so the map will wrap around in the X direction, but along the Y direction the ends will be the vacuum of space. I have some thoughts about movement in microgravity for when you travel inwards to the center of the cylinder...
Oh, and those spacesuits Nato added? They're full of parts. And the parts have parts.
I didn't make any headway on adding mass and volume to items yet, but I did start working on power a little. There's a soldering iron item that you can find (which you need to be able to disassemble stuff that's soldered together, like electronics), and I'm in the process of making it so that the soldering iron has a battery which gets depleted when used, and the tool becomes useless once the battery is dead. As part of that I've also had to make it possible for items be partially disassembled, because something has to happen if your soldering iron runs out halfway through taking apart a door.
If you have to repair a broken space ship, there's definitely going to be some messing about with cables.
This week I've been working towards implementing a ship-wide power system that things can plug into, but I found I needed a few other pieces first. In particular, there'd be no point to a power system if you couldn't plug anything in, would there? So I added cables that you can unspool and drag along the floor to plug things together. Right now, plugging things into each other does nothing, but in future you'll be able to connect up machines to each other using power cables. I have some thoughts about doing semi-realistic power transmission systems involving transformers and high-voltage / low-voltage lines, but that's for another day...
I've also started thinking more about how to make the whole ship feel more designed. Right now there's just a random jumble of rooms without much logic or sense to it. I did some good brainstorming with Nato last weekend and came up with what I think is a pretty good 3-layer system for thinking about the map layout:
First, generate a "schematic" for the ship, which shows where the crew sector is, where engineering is, and so on.
Then, lay down "superblocks" that are groups of rooms that are restricted to be laid out in a certain way, without yet specifying exactly which rooms will be placed.
Lastly, place the individual rooms within the superblocks, completing the map.
I tried the "superblock" system out and I didn't like it. I think there's still potentially merit in the layered approach though; I'll probably resurrect it when I start thinking about placing rooms in specific places in the ship according to their type.
I was still keen on this superblock thing.
Last week I said I wanted to work on building out the ship power system and making the map generation feel more 'designed', and I've done some of both of those things. On the map generation front, I've built out the superblock system to the point where it's working, so now the map generator first lays out the schematic, then fills it in with superblocks, and then drops in the specific rooms. Here's the superblock layout and the filled-in map (both of these screenshots show connections between superblocks/rooms rather than the rooms themselves; each character in the filled-in map represents one 5x5 tile room in the final map).
On the power system side, the cables I made a couple of weeks ago do actually now function to pass power between things—there are still a whole ton of bugs here, but it least it sort of works! You can disassemble a soldering iron, plug a power cable into the battery, then plug the other end of the cable into an automatic door to get it working again even when ship power is down. At least, until the battery runs out. Here's a video of me jury-rigging a door: it's stuck open at first, then I disassemble a soldering iron to get its battery, and plug the battery into the door, which returns to working condition (temporarily).
Finally, here you can see a rather poor quality animation of the map generation in action. You can see that in the lower area of the ship it has some trouble choosing what rooms to place and tries a few times before "getting" it and moving on to the top layer. All driven by choco and choco-graph :)
I stopped posting updates on r/roguelikedev around the end of 2018, and also mostly stopped working on Adrift. I picked it back up in late 2019, and started working on it weekly with Nato's help. (Thanks Nato!)
Here's an abridged list of things we've added since then:
- Atmosphere simulation. Each tile on the map has a gas composition, represented as partial pressure of various atmospheric gases (currently CO₂, O₂ and N).
- Plants. They grow, flower, are pollinated (by tiny flying robots), and bear fruit. Plants absorb CO₂ and emit O₂. At some point they'll also interact with some growth medium, i.e. soil or water, as well as require light to grow.
- Heat. The ship starts out very cold, and as you exchange heat with the environment, your core body temperature drops very quickly and you'll eventually die of hypothermia. Clothes can help you maintain homeostasis. We don't model metabolism yet so your body in the game is basically an RTG.
- Cylindrical topology. The ship is constructed as a spinning cylinder, which you walk around on the inside of. So there's no left/right edge of the map, but there is a top/bottom.
- WFC-like room layout generation. The rooms that the higher-level BSP algorithm generates have nonuniform shapes, which makes generating the contents of those rooms challenging. Adrift uses a constraint-based algorithm to assemble rooms of arbitrary shapes from "puzzle pieces".
Nato also has some really cool ideas for how electronics might work:
I really want to get Adrift to the point where it's playable for someone who isn't me, to the point where it's at least barely, just a little, getting across some of what I have in mind for the game.
There are three main parts of the game arc that I think are necessary to show the scope of what I have in mind.
- "Cold & afraid" - you're decanted from your cryopod, and you're naked, starving, and freezing cold. You have to find food and clothes, fast, or you're going to die.
Mechanics needed: hunger, thirst, eating/drinking, death by starvation/thirst, death by temperature excursion. I think it's OK in the first alpha for it to be relatively easy to get out of the cryopod room and find food/clothes, it just has to be the case that you'll die if you don't.
Content needed: preserved food, clothing, more room variety in crew quarters.
- "Survive & thrive" - you've found some warm clothes and a stash of freeze-dried fruit. That's enough to live, for now, but unless you get the hydroponics room online, you're going to find yourself at the end of your supplies in short order. Also, is it just you, or is the ship getting colder?
Mechanics needed: plant growing needs to be balanced & fleshed out (e.g. dirt, light, water requirements), dangers/damage that prevent you from exploring the entire ship, crafting/disassembly, life support system (incl. pipes/wires?), repairable damage to ship systems.
Content needed: life support rooms, item damage design, food preservation machines / methods, variety in rooms in engineering.
- "What happened?" - this is the end-game, and involves finding your way to the bridge.
Mechanics needed: high-level map topology (i.e. cylindrical shape of main deck, multiple levels), functioning elevators.
Content needed: backstory/lore hints, bridge rooms, mid-deck rooms.
That's all for now. I'll close with some inspiration.