Maps: turn the building into a castle (or a map of your own), with a throne, a line of waiting workers and peasants who age as they work - #184
Merged
Conversation
…ve its own walk grid The office's grid is now OFFICE_NAV; walkable/route/nearestWalkable/wayIn/wayHome keep their behavior and signatures. wayFrom/wayTo (off a seat to a point, and from a point to a seat) move onto the class so any floor plan can use them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s working time - shared/maps: MapConfig (plain data) checked and worked out into a MapPlan: seats at tables, board agents' lecterns, the meeting table, the boards, a throne, a line in front of it, the herald who hires, props and what's in the way for walking. Every map places the office's seat ids, so the server, the queue, meetings and saved workers work the same on any of them. - The castle is the first one, all data (shared/maps/castle.ts). - Custom maps: JSON in .agent-office/maps/, optionally extending a built-in one. - server/maps.ts keeps the building's pick in .agent-office/map.json (map.set / map), and seats are checked against the map you're on. - Workers keep count of how long they've worked (workedMs / workingSince). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- world/world.ts: what main.ts needs from a map's world (seats, boards, colliders, walks in and out, confetti, lighting), with the office adapted to it. main.ts shows one world at a time and switches when the building's map changes, like the office and the rooftop. - world/castle.ts builds a castle-style map from its plan: a long hall, pillars and pointed arches, a timber roof, stained glass, a dais with a throne of iron blades, long tables and benches with tomes for laptops, lecterns for the board agents, a round table for meetings, the boards on the side walls, fire and candlelight, great doors, and the Hand of the King. - world/court.ts: workers waiting on someone get up and line up before the throne, longest waiting first, and go back to their seats once answered; new ones sent out by the Hand run to their seat. - On the throne, E is for whoever's first in line (or the Hand when nobody's waiting). - Workers dress as peasants there and wear out as they work: a beard that grows long and grey, dirt and patches, a hunch, droopy eyes and a slower walk, fully after 30 minutes' work. - Settings > Building > Map picks the map for everyone, and lists your own maps (or why they won't load). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d of loading Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he doorway Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, the gong and the windows where the hall has them, ale poured not ground Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A custom map is checked for everything its builder reads: sizes in range, whole numbers of seats, steps and spots in line, known table sides, colors as text, at most 40 tables, 12 seats a side and 400 props, and an id that's already trimmed. A zero-width window or a dais with a billion steps no longer hangs every browser. - null takes away a map's throne, herald or line; with no dais the throne's on the default one, at the height the plan says. - mergeConfig skips __proto__/constructor/prototype keys. - The map changing by a read of the folder (a picked map breaking or coming back) is told like a pick: everyone's seats are forgotten on the server and in every browser, and a toast says why. A failed pick still tells everyone what the read found; 'picked by' only shows for the map in effect. - Nobody sits where someone on the floor already is: the office refuses it (sit.refused), so two people arriving at an empty throne at once don't both sit on it. - Past the folder's first 24 maps, each is listed with why it isn't read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A worker sent out by the Hand (or in off the queue) really runs to its seat: it was left 'seated' where it came in, so it never set off, couldn't dance and left from the wrong place. - Arriving (or reconnecting) on the castle no longer replays every worker walking in: the map switch put seatedAlready back to false in the middle of the welcome. - A worker that left the line is its seat's again for a click (the spot's interactable stayed on it, and could open another worker's terminal); off interactables aren't aimed at. - Up on the roof when the map changes to one without it: down to a floor, retried if the trip fails, and picking a floor meanwhile no longer loops ride/switchFloor forever. - N (or Go to desk) to the worker at the front of the line sits you on the throne if it's free, else beside it, never inside the throne. - The great banner gets the project's name; the office's basketball can't be picked up there; syncStack doesn't put the street back under the hall; walking to someone on another floor keeps walking. - Getting up faces the way it was (no flip on a seat turned past a right angle), footsteps and strides go with the pace (running, shuffling), the tome's cover shuts over its pages, the gong and the herald stand on a raised floor, flames share their shapes, and an edited map of your own frees what it was built of. - planOf answers from its cache without stringifying the config each time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… nothing of the castle Review round 2, how it's put together: - world/styles.ts maps each style (MAP_STYLES) to its builder; main.ts builds whatever a plan's style is. The herald, the room (wall thickness, roofed), the acoustics and dispose are World's, so there's no Castle type or cast left in main.ts. - The plan carries the tables and the meeting table as checked, defaults filled in and the sides with seats worked out, so a builder reads them rather than working them out again. - buildCastle is split into the shell, the dais, the tables and places, the council, the boards and the herald; each prop kind is an entry of an exhaustive PROPS table (a new kind won't compile without its builder). - The herald's question and button, and the throne's label, are the map's (the castle keeps its 'my liege'); the fire glow reuses the costumes' texture; planMap takes unknown. - docs/maps/castle.json is the castle as a map of your own to copy (a test keeps it in step with castle.ts), and docs/maps.md has a map from nothing (tested to load), what's required, what happens when a map breaks, what it can't do yet, and adding a style. - The toast says when a map of your own came back as well as when it broke. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The map changing while you're on your way to a floor (elevator, floor list, ladder) puts you where the new map has you come in once you land (or if the trip fails), instead of at your old spot, which could be outside the castle's walls. Arriving on the throne waits until you're there. - A saved spot outside a hall of its own isn't come back to on a reload. - Holding the basketball when the office goes: it leaves your hands, your character's and everyone else's; Minesweeper and the arcade cabinet stop. - Refused a seat, you're on your feet for everyone, not left on the one you had. - Workers sent out from the herald are remembered per floor and for half a minute, a second one goes to the next free seat rather than the same one, and the map changing forgets them. - Only walking over to someone keeps walking through a floor switch; a walk to someone on a roof the map doesn't have stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Ale at the cask no longer schedules the unused grinder in the past (a RangeError just after the sound starts); the pour just starts. - N / Go to desk on a map of its own lands on open floor, not in a pillar; to a worker still on its way to the line, at its spot in the line. - The round meeting table's collider fits inside its top. - Court keeps the line's interactables (and the clickable models of who's in it) up to date when someone takes or leaves a spot, not every frame, and isn't raycast twice. - Boards, their windows and the arcade name seats the way the map you're on does. - A map change on a ride down to the garage is caught when the doors open. - A map's benches and board agents have to fit in its hall too. - Tests: the default dais, and every walk back to a seat as well as away from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- K on the throne speaks to the Hand of the King whoever's in line, and the throne's hint says so (and how to get up). Hand him an issue card and he sends someone out for it. His hint says when the office is full or the budget's spent. - A worker sent out by the Hand runs from beside him for everyone, not just whoever sent it (worker.spawn / WorkerInfo via: 'herald'); a second one goes to the next free seat. - You stay on the throne through a floor switch and a reload (if nobody's taken it). - No stained-glass window inside the hearth's chimney; the docs' example moves the issues board and its scribe to an empty bay; a lectern's rotY is documented the way it works. - A map's line, herald, spawn and door have to be clear of its furniture. - The toasts name a map as it's called (🏰 Castle), not 'the the barn'. - 📚 Docs in the ☰ menu off the office, where there's no bookshelf. - A reconnect's old connection doesn't refuse you your own seat; the fridge doesn't clunk in the hall; the elevator doors open when a map change catches you arriving; a failed trip off a roof the map doesn't have is tried again; the boards take the new map's seat names at once; Go to desk never lands outside a hall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e maps branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
webdevcody
added a commit
that referenced
this pull request
Sep 29, 2026
…) into the desk signs branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Maps: the building can be something other than the office — starting with a castle
Under ⚙️ Settings → 🏢 Building → Map, anyone can change what the whole building looks like inside, for everyone on every floor, and back again. Everything that makes the office work comes along unchanged: workers and their terminals, the issues / PR / queue / services boards and their board agents, the task queue, meetings, the merge gong, PRs, budget and limits.
🏰 The castle
Maps are configuration
MapConfig, insrc/shared/maps/). The castle itself is justsrc/shared/maps/castle.ts..agent-office/maps/. You can either:{ "id": "my-hall", "name": "My hall", "extends": "castle", "boards": { "issues": { "z": 3 } }, "stations": { "issues": { "z": 3 } } }; ordocs/maps/castle.json, a full copy of the castle to edit, which a test keeps in sync.src/client/world/styles.tsthat returns aWorld. New prop kinds go in an exhaustive table, so a missing builder won't compile. Details are indocs/maps.md.How it's built
src/shared/maps/:planMap, which validates every field the builder reads and works out where every seat, lectern, line spot and board goes, plus what's in the way;extendsresolution and custom-map checking.src/shared/nav.ts: refactored into aNavGridclass, so any map gets its own walk grid. The office's behaviour and API are unchanged (checked against the old version on 12k random calls).src/server/maps.ts:.agent-office/map.json;map.set/mapmessages;sit.refused).src/server/workers.ts: keepsworkedMs/workingSincefor every worker.src/client/world/world.ts: theWorldinterface thatmain.tsuses (seats, boards, colliders, walks in and out, confetti, room, sounds, lighting, herald, dispose). The office is adapted to it.main.ts: swaps worlds at runtime, the way it already swaps in the rooftop. Office-only features (elevator, garage, roof, ladder and poles, balcony, jukebox, dog, pictures, basketball) are gated to the office.src/client/world/castle.ts, the builder;court.ts, workers walking between their seats and the line;Worker;tomestyle for the laptop;Sky.setIndoors;Reviews
Three review rounds, each with independent reviewers, and every finding fixed:
Notable fixes from the reviews:
ride/switchFloorloop.Testing
npm run typecheck,npm run buildandnpm testall pass (294 tests).tests/maps.test.tscovers:extendsmerging andnullremoval;🤖 Generated with Claude Code