The city spec
A city is a level drawn freely, the way the flagship Riverbend is: streets that curve, meet at any angle, ring the town and cross a river, quarters turned off the map's axes, roundabouts, crescents and closes, a hill with a road up to its lookout. Its spec ("template": "city") is data: roads drawn through points, and the rest named. The platform's solver (packages/catalogue/src/city/, its README) resolves it at once, without Blender, into a plan, and answers with the problems at their paths in the spec, or with a map of the city in text. The generator (assets/blender/levels/city.py) then builds the plan: the ground, the pavements, the bridges, the buildings of every district in its style, the street furniture, the parks and the life of the streets and the water.
Through the MCP server: level_spec_set with a city spec answers with its issues or its map; draft_build builds it. Locally: npm run level:city -- <spec.json> --check runs the solver alone; without --check it builds the level's kit and recipe into the kits folder, then level:recipe and level:export.
The frame
Everything is in metres, in the game's frame, the map's middle at [0, 0]:
- a point is
[x, z]: x grows westward (east is −x) and z northward. The map runs from −size/2 to size/2 both ways. - a bearing is a compass's, in degrees: 0 north, 90 east, 180 south, 270 west. Bearings are the map's everywhere, a grid's turn included (a roundabout's arms are not turned with a grid).
So [-100, 50] is 100 m east of the middle and 50 m north of it.
Names
- Roads, grids, rivers, hills, roundabouts: lower-case words of letters and digits joined by dashes (
bridge-street), 32 characters at most. - Districts and parks: lower-case words joined by underscores or dashes (
old_town). They name places, whose ids take them with underscores (old_town.cafe_1, the parkeast_green). - Places: lower-case words joined by underscores, parts by dots (
bridge_foot).
The fields
| Field | What it says |
|---|---|
template, name | city, and the level's name (test-<words>, main-<words>; the platform names a draft's itself). |
seed | Every random choice comes from it. |
size | The map's side, 240 to 1000 m (600 by default). |
climate, clock | The weather's odds and the room clock, as for every template (see Time and weather). |
grids | Turned grids of streets. |
water | Rivers (two at most): a level below the streets, bridges where roads cross them. |
hills | Hills outside the town. |
roundabouts | Roundabouts and their islands. |
roads | Every other road. |
districts | At least one: each a style and a seed or an outline. |
parks | Parks, greens and squares. |
spawn | The road the players start on, and where along it. |
places | Named points for missions. |
The town
The town is where the districts are built: inside the closed road that is its boundary when one is (beyond it is open country: woods, meadows, rocks), else the whole map less 12 m round its edge; never on the water, nor within a hill's reach (and 2 m more). A hill stands outside the town even without a boundary. Only one closed road bounds the town.
Points
Wherever a spec wants a point it takes one of these:
| Form | The point |
|---|---|
[x, z] | Those coordinates. |
{"grid": "mid", "at": [u, v]} | A point of a grid, in its own frame: u metres toward its east, v toward its north. |
{"roundabout": "circle", "arm": 90} | Where the roundabout's arm at that bearing leaves it (the arm nearest, within 10°). |
{"road": "quay", "at": "start"} | A road's start, its end, or a distance along it in metres from its start. |
{"hill": "knoll"} | A hill's top. |
A point may name anything, drawn before or after it: the solver draws what each needs first. A road that needs itself (two roads each ending on the other) is an issue.
Roads
{
"name": "bridge-street",
"class": "street",
"from": { "roundabout": "circle", "arm": 0 },
"through": [
[-84, 0],
[-80, 110]
],
"to": { "onto": "bank-road" }
}class:parkway(16 m, two lanes each way, fast),avenue(16 m),boulevard(14 m),street(11 m, a lane each way),lane(8 m),one-way(7.5 m, one lane, its traffic running from its start to its end).from,through,to: where it starts, what it runs through (optional), where it ends; the road runs through all its points in that order. An end is a point, or{"onto": "<road>"}: the road is cut where it crosses that road, or its end stretch is lengthened (60 m at most, at its start as at its end) until it meets it. A roundabout's name means its whole circle, a closed road's name its whole ring.throughtakes points and bank stretches:{"bank": "river", "side": "left", "from": point, "to": point, "back": 14}runs along the river's bank from where the river passes nearestfromto where it passes nearestto,backmetres (4 to 60, 14 by default) from the water's edge to the road's centre line: a quay, an embankment. Thesideis the bank's looking downstream: a river flowing south has its left bank on its east.line:spline(a smooth curve through the points, the default),straight(straight between them: a corner is a bend as tight as it is sharp, so a street's corner needsrounded),rounded(straight, each corner an arc ofradius: one radius, or one per corner; 12 m by default) oreased(straight, each bend eased until no bend is tighter thanradius, 12 m by default).leaving,arriving: the bearing a spline leaves its start in, or arrives at its end in (a road leaving a roundabout straight out, say).closed: a ring through its points, back to the first, aspline,straightorrounded(every corner); it takes nofromorto. It is drawn as parts (<name>-1,<name>-2…) of about 500 m, three at least.boundary: truemakes it the town's edge.turningCircle: a close: its end is a paved turning circle.cycle: a cycle lane painted along it.to: {"hill": "knoll"}: the road climbs the hill to its top at one grade, over its whole length from its start: 12 % at most, so draw it long enough (aroundedserpentine's corners shorten it). A lookout stands at the top. It is the only road that climbs: the town's streets are flat.step: how far apart its points are, 2 to 10 m (6 for a parkway, an avenue and a boulevard, 5 for the rest); 4 suits a tight serpentine.
Roads that cross meet in a junction, with rounded kerbs, crossings and, where an avenue or a parkway meets a street squarely, traffic lights. A road should start and end on something (a junction: onto, a roundabout's arm, a point of another road; a turning circle; a hill's top) or well clear of every other road: an end within a pavement or two of a road it does not join is an issue. A crescent runs from a road onto the same road; its ends may stop short of it (they are lengthened) or run past it (they are cut).
Grids
{
"name": "mid",
"origin": [40, -50],
"bearing": 15,
"class": "street",
"u": [{ "at": -50, "to": { "onto": "quay" } }, { "at": 0, "class": "avenue" }, 50],
"v": [-45, 5, 55]
}A grid is a turned frame: its origin and the bearing its own north points to. u lists the streets running toward its north, by how far east of the origin they stand; v the streets running toward its east, by how far north. A line runs from the first to the last line of the other family unless it says otherwise: from is its south end (a u line) or its west end (a v line), in the grid's own frame, and to its other end; each takes a coordinate of the other axis, or {"onto": "<road>"} (the line runs on, however far, until it first meets that road). A line may be of another class than its grid's. Its streets are named <grid>-u<i> and <grid>-v<j>, i and j counting the lists from 0, so another road can end on them.
Rivers, hills, roundabouts
- A river:
through(points upstream to downstream, a smooth curve; draw it across the map and beyond),width(8 to 120 m, 30 by default), andwiden:{"from": 0.6, "to": 0.8, "width": 90}, the shares of its length (all of it, drawn beyond the map too) between which it widens to that width; it stays that wide downstream, as an estuary or a harbour opening to the sea. Its water is a level below the streets. A road crossing it gets a bridge, if it crosses at 30° or more; roads never meet over the water. - A hill: its top
at, itsheight(2 to 40 m) andreach(20 to 200 m: how far its slopes go). Nothing of the town stands on it; woods and rocks do. - A roundabout: its middle
at, itsradius(9 to 40 m, 22 by default: its carriageway's centre line), itsarms(bearings, or{"toward": point}: the arm pointing at that point; three to eight),lanes(2 by default, or 1 for a small one) and itsisland:green(a monument, flower beds, trees; the default),market(a paved square with a fountain and stalls, its rim of bollards),plain. Roads start and end at its arms ({"roundabout": "circle", "arm": 0}); an arm no road takes is only where two arcs meet. A road may also endontoits circle, between two arms.
Districts and their styles
Each district is a style and either a seed at (a lot belongs to the nearest seed on the same side of the water) or an outline area, which wins over the seeds and needs no at: a list of points, or any of a park's areas ({"polygon"}, {"ellipse"}, {"circle"}, {"block"}). Seeds share the town by distance alone: for a district in the middle with others round it, give it an outline, or set the others' seeds evenly round it. A district's title is what the minimap shows; its landmarks are buildings it must have, by the catalogue's building archetypes or the tags they carry (town-hall, amenity=pub); without them it has its style's own (an old town's town hall, library and church), and "landmarks": [] gives it none.
| Style | What it is built and dressed as |
|---|---|
old-town | Townhouses, shops, cafés, pubs and guest houses round lanes; a town hall, a library, a church. |
civic | Public offices, a courthouse, a police and a fire station, colleges, a museum. |
financial | Glass and stone towers, banks, a hotel, a multi-storey car park. |
midtown | Shops, apartments and offices; a mall, a hospital, a supermarket, a bus station. |
townhouses | Apartments and townhouses with gardens; a school, a doctor, a bakery. |
suburb | Houses and townhouses on lawns, a chapel, a pub. |
docks | Depots, warehouses, factories and car trades, container yards, cranes on the quay nearest the water. |
waterfront | Hotels, glass towers, restaurants and bars by the water, boathouses, pools on the roofs. |
high-street | Shops and flats over them, pubs; a hospital and a sports centre. |
garden-suburb | Houses in gardens, fruit trees, a school. |
works | Warehouses, factories, a brewery, storage, yards of scrap and skips. |
parkland | Parkland with a few cafés and chalets; a stadium. |
hillside | Villas, chalets and guest houses on lawns, a chapel. |
They are Riverbend's thirteen quarters (packages/catalogue/src/city/styles.json): each style carries its mix of buildings, its density, its trees, the furniture of its pavements, its yards and its roofs.
Parks, the spawn, places
- A park (
kind:parkorgreen, lawn and trees, the defaultpark;square, paved) is anarea:{"polygon": [points]},{"ellipse": {"at", "size": [a, b], "bearing"}}(aandbits half-axes toward its own east and north, turned tobearing),{"circle": {"at", "radius"}}, or{"block": {"grid", "from": [u, v], "to": [u, v]}}(a block of a grid between its streets, inside their pavements). Its place is its name. spawn:{"road", "near"}: four spawn points on that road (a town street, 30 m long at least) near that point (its middle by default), two each way.places:[{"name": "bridge_foot", "at": point}].
The level also names every roundabout (its name, dashes as underscores), every hill's top, every bridge's middle (bridge.<road>, by the piece of road it carries) and every district's buildings and their anchors (<district>.<building>_<n>).
What the solver refuses
The solver checks in stages: the spec's fields first; then every name and every road drawn (a road that cannot be drawn is said once, at its own path, not again for the roads that need it); then the network and the districts. A stage with problems keeps the next from running, so fixing them may bring others. Each problem comes back at its path in the spec (roads[3].to, grids[0].u[1]), saying where on the map it is:
- a name used twice, a name that names nothing, a road that needs itself;
- an end
ontoa road it neither crosses nor reaches within 60 m; - a road shorter than it is wide, or bending tighter than its width allows (half its width and 2 m: 10 m for a parkway, 7.5 m for a street, 6 m for a lane);
- two junctions under 16 m apart, roads meeting at under 35°;
- a road leaving the map (its centre line keeps 2 m inside the edge);
- an end that stops short of a road without joining it (say
onto, or keep it clear); - two roads so near that no pavement fits between their kerbs, where they do not meet;
- a river crossed at under 30°, a road ending or running in the water, roads meeting over it;
- a street of the town climbing a hill (only a road ending at its top climbs), a climb over 12 %;
- a district's seed in the water, outside the town or on a hill, a district left with no lot;
- a spawn on a roundabout, on a climbing road or on a road under 30 m;
- a city drawn as more pieces of road than a level holds (256: every road, grid line, roundabout's arc and closed road's part is one), said at
roadsbefore Blender runs.
A build passes with a warning, not an error, when a district's landmark found no lot along its streets: it stands nowhere, and the warning names it (give the district more street, or name fewer landmarks).
What version 1 does not build: sloping streets and buildings inside the town, tunnels, water at two heights, lakes, compounds.
The map
A spec the solver accepts comes back as text, north up and east to the right, a character every 8 to 20 m: the districts by their letters, # parkway, = avenue or boulevard, + street, - lane or one-way, o roundabout, ~ water, ^ hill, * park or square, S the spawn, . outside the town; under it the legend, then the spec's roads, grid lines and roundabouts, the pieces of road they are drawn as (a roundabout's arcs and a closed road's parts are pieces of their own), the length of road, the junctions and the bridges.
An example
test-city (its spec is apps/level-editor/assets/kits/test-city.spec.json, and the catalogue's template.city example): a 320 m town on a river's south bank, a grid of two by two blocks turned 15°, its avenue running east to a roundabout of three arms, a street from it over the river's one bridge, a quay along the south bank and a road along the north one, a crescent, a close, a knoll with a lane climbing to its lookout, three districts (an old town, the docks, the north bank) and a paved square.