Closing the Loop Between Generator and Editor
EchoQuest’s procedural map generator could already produce a complete dungeon in two seconds — rooms, corridors, walls, doors, chests, traps, even atmospheric flavor like wall torches and gold piles. What it couldn’t do was hand that work off to the rest of the toolchain. You’d generate a beautiful dungeon, save the TMJ, and then realize none of the monster spawn zones existed in the database yet. So you’d open the Spawn Zones admin panel, recreate every zone by hand, and bind monsters to them one at a time. The generator did the easy part. The artist did the boring part. That gap is what v0.28.0 closes.
Persistent Generation
The map generator now takes a persistent flag. When it’s set, every spawn zone the generator emits also gets upserted into the spawn_zones table with a namespaced key (map_slug:local_zone_id) so the same zone always lands in the same row no matter how many times you regenerate. Run the generator once, then jump straight to Spawn Zones and assign monsters. Tune the dungeon, regenerate, and your monster assignments survive because the keys are stable.
This also opened up a new failure mode worth guarding against. The first time I regenerated a TMJ that was already live on a world map, I overwrote a tuned production level with a fresh procedural pass. That hurt enough that I added a refusal. The generator now checks every TMJ for active references before writing, and if any world map points at the file, it returns a 409 with the full list of offenders. The admin UI renders that as a screaming red modal so there’s no missing it. Each row in the Existing Maps sidebar carries a colored bullet now — red means live (regen blocked), amber means draft (regen allowed but warned), green means unassigned (safe).
Map Objects, Editable
The other gap was editing. The generator scatters dungeon objects into a Generators layer in the TMJ — doors, levers, chests, traps, exit zones, flavor zones. Once written, those objects were effectively read-only. If you wanted to move a chest two tiles to the left, you opened the file in Tiled.
The new Map Objects admin panel reads that layer back out, presents every object as a row, and lets you edit name, type, position, size, and custom properties in place. Writes go through an atomic .tmp-then-rename so a half-saved edit can’t corrupt the map. The same cache invalidation that powers generation flushes after each save, so the running game sees changes within seconds.
Sprite Math Was Mostly Wrong
While I was rebuilding the generator pipeline, I noticed the procedurally-placed scenery looked worse than I remembered. Walking behind a pillar would erase its shaft. Chests rendered as a single corner tile. Stairs were a one-tile slab. The wall torches looked like garbage pixels glued to the wall. I assumed I was breaking things as I went. I wasn’t. The bugs had been there all along.
Most of them came down to lying about sprite dimensions. The pillar is a three-tile vertical stack — capital on the top tile, shaft on the middle, base on the bottom — but the placer was splitting it as one-on-top-one-on-bottom. The chest sprite is a 2×2 (lid on top, drawer on bottom); the code placed only the lid tile. The stairs are a 3×3; same story, just the corner showed up. Barrels and pile-of-bones and skull sprites are all 1×2 vertical pieces; they were being scattered as 1×1 entries, which meant only the top half rendered and the bottom looked floating. The wall torch sprite I’d been using wasn’t a wall torch at all — it was an edge slice of an adjacent sprite that happened to look kind of like a torch if you squinted. The real wall torch was eight tiles over.
None of these were complicated to fix once spotted. They were just persistent enough to make every generated dungeon look slightly cursed. The L-room corner walls had a related problem: the corner-tile check required all four neighbors to be walls, which is geometrically impossible for an inside corner of a connected room, so it always fell through to the straight-wall branch. Fixed that to use the correct two-adjacent-cardinals plus opposite-diagonal-floor test. Suddenly L-rooms have corners.
Shipped Alongside
This release also carried the backlog of small but pointed fixes that piled up since the March security sprint. The gambling table’s outcome resolution moved fully server-side using a cryptographic RNG — previously the client could claim its own win/loss, which is exactly the kind of thing that lets someone print gold given enough patience. Skinning a creature no longer crashes the socket if you do it from a freshly-loaded session. The 500 on entering Millhaven Tavern is gone (the NPC indicator query was joining through the wrong column). The "save too large" error that started hitting characters with long inventories is gone too, after raising the server’s body parser limit. The admin portal now supports signing in through EchoForge SSO, which is a small thing for individual players but a big thing for the studio — one set of credentials across every internal tool.
The Steam build picked up the last of its anti-piracy and integrity work: launches check Steam ownership before running, and save files are signed with HMAC-SHA256 to catch tampering at every mutation point. That’s less about chasing pirates and more about making sure honest players never get hit by a corrupted save without us noticing.
What This Buys Us
The reason the generator–editor loop matters is content cadence. Every dungeon, cave, ruin, and town interior in EchoQuest is either hand-built in Tiled or procedurally generated and then tuned. Both flows used to end at "you have a TMJ" and start over in the admin panel. Now they end at "you have a tuned, populated, monster-bound, hazard-laden map" with one round-trip. The difference compounds the further along we get — the further the game stretches into expansion content, the more rooms and zones and objects we need to push through this pipeline.
Next on the EchoQuest list: starting-area expansion (Millhaven and the Whispering Reach, levels 1–10), the Realms infrastructure foundation, and the Balance Center phases 3+ that finish wiring up the multiplier overrides we exposed in v0.28.0. The dungeon generator is finally pulling its weight. Time to put it to work.
— Bruno