You are browsing as a guest. Sign up (or log in) to start making projects!

Delve

  • 5 Devlogs
  • 26 Total hours

A little roguelike game I make. Runs in the terminal. You're the @, you go down into a dungeon, you fight things and you die a lot. Runs entirely in the terminal, no graphics.

Ship #1

# Delve

This is a little **roguelike** I built in Rust. It runs entirely in the terminal:
no graphics, just coloured letters. You're the `@`, you climb down into a
randomly generated dungeon, fight the things that live there, grab whatever loot
you can, and try to get to the bottom before something eats you. It's twelve
floors deep. Getting there is the whole game.

The only dependency is [`crossterm`](https://crates.io/crates/crossterm) (for raw
terminal mode, colours and key input). **Everything else — the RNG, the map
generator, the field of view, the monster pathfinding, the save format — I wrote
myself.** That was the point: I wanted to actually understand every piece instead
of pulling in a crate for it.

## How a turn works

Delve is turn-based, and the loop is dead simple: **nothing moves until you do.**

1. You press a key → that becomes an `Action` (move, wait, pick up, descend, open
the inventory, …).
2. The game resolves your action. If you walked into a monster, that's an attack.
3. Then *every* monster takes its turn.
4. The field of view is recomputed from your new position, the screen is redrawn,
and it waits for your next key.

Because the world only advances on your input, you can sit and think for as long
as you like — which is very roguelike, and also means the whole thing costs zero
CPU while it waits.

## The pieces

**The dungeon (`mapgen.rs`).** Classic "rooms and corridors": I scatter up to 16
non-overlapping rectangular rooms, then connect each room to the previous one with
an L-shaped tunnel. Up-stairs go in the first room, down-stairs in the last. Every
floor is freshly generated, so no two runs are the same.

**Field of view (`fov.rs`).** You can only see what's near you; everything else is
dark (tiles you've seen before stay dimly remembered). I do it by shooting straight
**Bresenham lines** from the player out to every square on the edge of a box around
them, lighting up tiles until a ray hits a wall. It's not the fancy "shadowcasting"
algorithm everyone uses — I picked the version I could fully understand and it
looks great.

**Monsters that hunt you (`ai.rs` + `pathfind.rs`).** When a monster can see you it
doesn't just stumble around — it runs a real **A\*** search to route around walls
and close the distance. Lose line of sight and it goes back to idling. (The A\* has
a fun gotcha: Rust's `BinaryHeap` is a *max*-heap, but A\* wants the *cheapest* node
first, so I had to reverse the ordering. That bug — it kept expanding the worst
nodes — took me embarrassingly long to spot.)

**Fighting (`combat.rs`).** Simple and readable: roll the attacker's power with a
tiny bit of variance, subtract the defender's defense, and the rest is damage.
Weapons add power, armour adds defense. Kill things for XP, level up for more HP
and power.

**Loot (`items.rs` + `spawn.rs` + `inventory.rs`).** Potions (healing, permanent
strength, permanent max-HP), scrolls (teleport, magic mapping, lightning bolt,
enchant weapon), plus weapons and armour drawn from tables that scale with depth —
so the deeper you go, the better the gear you *might* find, and the nastier the
things guarding it.

**Difficulty curve.** Deeper floors spawn more monsters and meaner ones. The design
is deliberately lopsided: the dungeon always wins eventually. That's the joke the
whole game is built around.

**Saving (`save.rs`).** A save is really a **suspend** — the moment you load it, the
file is deleted, so you can't save-scum your way out of a bad decision. I also
hand-wrote the entire save/load format by hand (parsing and all) instead of reaching
for `serde`, just to prove to myself I could.

**Flavour (`flavor.rs`).** Every death, descent and kill pulls a random one-liner
from a little pool, so the log never reads the same way twice ("You delete the goblin
from the timeline. No undo."). Honestly the most fun part to write.

  • 5 devlogs
  • 26h
  • 14.70x multiplier
  • 388 Stardust
Try project → See source code →
Open comments for this post

2h 41m 43s logged

  • Hooked up input (wasd, arrows, hjkl, all of it)
  • Built the real main loop with terminal setup, and it was playable start to finish
  • Hit a bug where every keypress moved me twice. Some terminals send a press and a release event and I was reacting to both. One-line fix once I understood it
  • Added saving. Hand-wrote the whole save format instead of using serde, dumping the map, monsters, inventory, and even the rng state to a text file
  • Save works suspend-style, so it deletes on load and you can’t save-scum death
  • Had a nasty bug parsing the rng state as an i64 when it’s a u64, so big values silently failed to load. Lost an hour to one wrong type
  • Five evenings and it’s a real, funny little roguelike that saves and runs in any terminal
  • Learned a ton of Rust. Calling it done
0
0
4
Open comments for this post

5h 55m 4s logged

  • Wrote the core Game struct that owns the map, player, monsters, loot, inventory, and depth
  • Built the turn loop. You act, then every monster takes its turn
  • The turn loop fought the borrow checker. Moving one monster while reading all the others is something Rust hates. Split the borrows of self by hand and used a snapshot of which tiles are occupied. Took forever, but it works and monsters chase properly, so I stopped touching it
  • Added descending the stairs
  • Added xp and leveling. Made up the numbers and playtested until they felt okay
  • Added rendering with remembered and visible tiles
  • Added the hud, the message log panel, and the inventory and help screens
  • Went from a bunch of modules to something that looks like a game in one evening
0
0
4
Open comments for this post

8h 18m 10s logged

  • Added line-of-sight using a bresenham ray to check nothing solid blocks the view
  • Added A* pathfinding so monsters route around walls to reach you
  • Monsters keep chasing for a few turns after losing sight, so ducking around a corner won’t save you
  • A* was a pain. Rust’s BinaryHeap is a max-heap, but A* needs the smallest cost first, so it kept finding crazy paths. Flipping the ordering fixed it and monsters got good at cornering me
  • Added loot to make going deeper worth it. Potions (healing, strength, vitality), scrolls (teleport, magic mapping, lightning, enchant), weapons, armor, and gold
  • Added an inventory to carry it all
  • Added spawn tables that put more and nastier stuff deeper down
  • Zapping the nearest monster with a lightning scroll across a room feels great
  • Made a whole file just for the funny one-liners the game throws at you. Best part of the day
0
0
4
Open comments for this post

2h 39m 44s logged

  • Put all the colors in one place
  • Added a scrolling message log at the bottom of the screen
  • Player and monsters use the same struct. The only thing that makes a monster is whether it has an “ai”
  • Gave them hp, power, and defense
  • Combat is simple. Walk into a monster to hit it
  • Damage is attacker power plus a little random, minus defender defense
  • Plays better than expected
  • Added lots of monsters. Rats, bats, and kobolds up top. Orcs, zombies, and wolves lower down. Ogres, trolls, demons, and a dragon deep at the bottom
  • Spent a long time tweaking numbers for a fair difficulty curve
  • Died for the first time
0
0
3
Open comments for this post

6h 48m 38s logged

  • Cargo project set up, only dependency is crossterm
  • Wrote own xorshift RNG (learn how they work + deterministic seeds -> reproducible dungeons)
  • Added points, rectangles, tile types (wall/floor/corridor/stairs)
  • Map = one flat vector indexed as 2D
  • Rooms and corridors generator: scatter nonoverlapping rooms, join with L-shaped halls
  • Ray-cast FOV: see only nearby and visited tiles stay dimly lit
  • No player yet, but world is generating
0
0
4

Delete project?

Are you sure you want to permanently delete this project? This action cannot be undone.

All devlogs, followers, and associated data will be removed.

Followers

Loading…