The city template
A city is a level drawn freely, as Riverbend is, from a spec without a grid of the map's axes. The spec is documented in The city spec; this is how the code does it. The solver here resolves a spec into a plan at once, without Blender (the platform answers PUT /drafts/:id/spec with its issues or its map); Blender then builds the plan (assets/blender/levels/city.py, handed it by generate.py --plan).
| File | What it does |
|---|---|
spec.ts | The spec's schema (CitySpec, version 1): the points in their five forms, the roads (six classes, ends onto roads, lines, rings, closes, climbs), the grids, the rivers, the hills, the roundabouts, the districts and their styles (CITY_STYLES), the parks, the spawn, the places. |
lines.ts | Points and polylines on the map, ported operation for operation from citygen.py and network.py (the spline, rounded corners, eased bends, snap), so a road lies where the Python would draw it; Area, a polygon indexed by rows. In the map's frame (Blender's: X east, Y north). |
roads.ts | A road (Road: its centre line, cross-section, bridges), the classes' cross-sections, the junction finder (findJunctions, citygen's) and the bridges (spanOver, Riverbend's: a span between the points a road crosses a river's banks, over every river at once). |
resolve.ts | resolveNetwork: every point found, every road drawn in the order its references need (a cycle is an issue), each the way Net drew Riverbend's; the grids' lines between their family's ends or to the road they name; the rivers and their banks; the roundabouts' arcs; the rings cut into parts; the parks' outlines; the bridges. A road that cannot be drawn is said once, at its own path. |
checks.ts | checkNetwork: network.py's four checks and what Blender let through: a road leaving the map, an end short of a road, two roads with no pavement between them, a river crossed at a shallow angle, an end in the water, roads meeting over it, a street climbing a hill, a climb too steep. |
districts.ts | layDistricts: the town's 4 m cells (inside its ring, off the water and the hills), each the district whose outline holds it, else the nearest seed on the same land. |
map.ts | cityMap: the city as text, north up, east to the right. |
plan.ts | CityPlan: what Blender builds, in the game's frame. |
solve.ts | buildCity: the spec parsed, resolved, checked, its districts laid, its spawn and places found; then the plan (the harbour's cranes on a docks district's quay, the jetties along each river) and the map. |
draw.ts | drawCitySpec: random cities for the generators' gates (@foi/catalogue/testing). |
styles.json | The thirteen styles a district is built in: Riverbend's quarters (their mix of buildings, density, trees, pavements' furniture, yards, roofs, car parks), read by city.py. |
Deterministic: the solver makes no random choice; Blender's are random.Random(seed).
The tests: riverbend.test.ts (Riverbend's spec, fixtures/main-riverbend.spec.json, solves to the roads riverbend.py draws, fixtures/riverbend-network.json: every road within 5 cm, the same 131 junctions and 7 bridges), solve.test.ts (test-city's spec, each refusal), lines.test.ts, draws.test.ts (two hundred random cities on every pull request); apps/tools/src/city-level.integration.test.ts holds the junctions found here to city.py's where Blender runs; test-city's contract is packages/world/src/levels-city.test.ts.