sindicate 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,118 @@
1
+ # Changelog
2
+
3
+ ## 0.16.0 — driving on it
4
+
5
+ ### A car (new: `vehicle/`)
6
+
7
+ - `vehicle/car.js` — a wheel at each corner asking the world for its own ground, so pitch,
8
+ roll and hanging a wheel over an edge all fall out of it. Spring-damped suspension, a
9
+ power-limited engine (0–60 in 4.6 s, not 0–123 in three), brakes, and a handbrake that
10
+ genuinely breaks grip: the body points one way and travels another when it slides. It also
11
+ owns where to probe for obstacles and what to do about a hit — the game only answers a ray.
12
+ - `vehicle/carAudio.js` — the classic four-loop engine: acceleration and deceleration at low
13
+ and high revs, crossfaded by revs and throttle, with tyres that protest in proportion to
14
+ the slide.
15
+
16
+ The header of `car.js` lists the faults it exists to prevent, each found the hard way: a body
17
+ snapped to the ground is glued to it; an airborne test against one frame of gravity flickers
18
+ on contact noise and sags the car to a crawl; one ray down the nose lets the body pass through
19
+ anything not hit head-on; a rail alongside must never bleed speed.
20
+
21
+ ### Roads as a world-build step
22
+
23
+ - `world/roadWorld.js` — one call that runs the road build in the only order that works and
24
+ hands back a `heightAt` for `buildHeightfield`, geometry to stamp before the BVH bake, and
25
+ the solved network. Site, seat each piece at ITS grade, wrap the ground with pads, THEN
26
+ generate links over that padded ground, then wrap again with their corridors.
27
+ - `world/roadAssert.js` — what must be true of a road world: no ground above a road, no road
28
+ floating over a gap, every link meeting both sockets, every link drivable at its grade,
29
+ every junction seated on its own ground. Cheap enough for every cold build. It found three
30
+ real faults on its first run.
31
+ - `world/roadNetwork.js` — `planLoop`/`loopTangents` for closed circuits.
32
+ - `world/roadLink.js` — `buildDeck`, and rail lines (median and shoulders) reported with the
33
+ pitch of the ground they sit on.
34
+ - `world/roadTerrain.js` — the ground now guarantees it never sits above a road, pads fade
35
+ into the landscape rather than ending at a rim, `heightAt.road`/`heightAt.deck` answer where
36
+ the driveable surface is, and the whole wrapper is bucketed (25× → 10.7× over bare terrain
37
+ across a 1.7 M-vertex lattice).
38
+
39
+ ### Tools
40
+
41
+ - `tools/unityPackageExtract.mjs` — pull named assets out of a `.unitypackage` without staging
42
+ the whole thing.
43
+ - `tools/fbxToGlb.mjs`, `tools/roadKitAnalyze.mjs`, `tools/buildKiwiRoadsPack.mjs`.
44
+
45
+ ### Fixed
46
+
47
+ - **Trains ran beside the track, not on it.** `train.js` still reached for a county's railway
48
+ data through `import.meta.glob`, which inside the engine resolves against the engine's own
49
+ folder and finds nothing — so the fleet silently graded its own polyline. The solved survey
50
+ goes in through `setTrainContent`.
51
+
52
+ ## 0.15.0 — the world-building release
53
+
54
+ Everything between the last published version (0.13.0) and here. 0.14.0 was bumped in the
55
+ repo but never published, so its contents ship as part of this release.
56
+
57
+ ### The world, split out of the games (0.14.0's work)
58
+
59
+ The terrain and stamper machinery both games carried privately now belongs to the engine, and
60
+ each game keeps only its own content:
61
+
62
+ - `world/geo.js` — the noise/curve toolkit (fbm, ridged, terrace, segDist, smoothstep).
63
+ - `world/railFormation.js` — the rail solver: max-grade Lipschitz envelopes and vertical curves.
64
+ - `world/bridgeSplice.js`, `world/waterLevels.js`, `world/waterMeshes.js` — bridges and water.
65
+ - `world/heightfield.js` — the chunked terrain mesh and its cook (one height lattice, central-
66
+ difference normals so chunk borders cannot seam, cooked to a single self-writing bin).
67
+ - `world/kit.js` — the kit registries, doors and ladders, `setKitContent`, `buildArea`,
68
+ `buildTown`, `dressTownCenter`.
69
+ - `world/placement.js` — the cook, samplers, resident kits and occupancy.
70
+ - `world/worldBase.js` — the world facade's engine half, with eight hooks a game fills in.
71
+ - `world/permanentWay.js` — the track-laying machinery.
72
+
73
+ ### The World Designer
74
+
75
+ - `tools/designer.js` + `tools/devPlugin.js` — a visual editor over the game's real terrain:
76
+ place, move and remove prefabs, draw road polylines, fly the camera, save to JSON.
77
+ - `tools/designer-mcp.mjs` — an MCP server so a Claude session can drive the designer while
78
+ you watch it.
79
+ - `world/design.js` — `buildDesign()`, the layout-JSON → live-scene bridge.
80
+
81
+ ### The road system (new)
82
+
83
+ Junctions are rigid prefab pieces; the roads between them are generated. Both halves, plus the
84
+ earthworks that make the ground agree with them:
85
+
86
+ - `world/roadKit.js` — reads a pack's measured kit. Lanes are stored by which side of the
87
+ centre line they sit on, so **left/right-hand drive is a runtime switch**, not baked data.
88
+ `gradeLevel`/`seatY` sink a multi-level junction until the roads that meet the outside world
89
+ are at ground level.
90
+ - `world/roadLink.js` — the road between two sockets: a cubic Bezier through both arms'
91
+ tangents so seams are tangential, cross-sections matching each socket's measured width and
92
+ paint, and a vertical profile squeezed into the reachable-at-max-grade envelope then relaxed
93
+ into vertical curves. Connecting is non-negotiable: if two sockets cannot be joined within
94
+ the requested grade the road still meets both ends and reports `feasible: false`.
95
+ - `world/roadTerrain.js` — pads under junctions, cut/fill corridors under links, both using a
96
+ batter slope so a road crossing a valley gets an embankment that reaches it. `pickSites` and
97
+ `pickManySites` put junctions on the flats.
98
+ - `world/roadNetwork.js` — sites in, road system out: a spanning tree plus a few short extra
99
+ links so the map has loops, and for each site the junction piece and rotation whose arms best
100
+ face the neighbours it must serve.
101
+
102
+ ### Pack tooling
103
+
104
+ - `tools/unityMeshToGlb.mjs`, `tools/unityPrefabToAssembly.mjs`, `tools/truthToAssemblies.mjs`,
105
+ `tools/fbxToGlb.mjs`, `tools/roadKitAnalyze.mjs`, `tools/buildKiwiRoadsPack.mjs` — recovering
106
+ a Unity road library into an engine pack: meshes, Unity-resolved transforms and materials,
107
+ per-file FBX import scale, measured sockets and cross-sections.
108
+ - `tools/ExportKiwiRoadMaterials.cs`, `tools/ExportKiwiRoadRefs.cs` — the Unity-side exports.
109
+
110
+ ### Notes
111
+
112
+ - `world/roadGraph.js` (A\* routing for the map) and the road piece system are different
113
+ things that share a word.
114
+ - The road modules are new and may still move before 1.0.
115
+
116
+ ## 0.13.0
117
+
118
+ World splits steps 1–3a; the package catalog and one-click install.
@@ -0,0 +1,97 @@
1
+ # Kiwi Road System — Recon & Re-Implementation Brief (for Sindicate/three.js)
2
+
3
+ > Recon of /Users/nick/Desktop/Kiwi (Unity). Kiwi runs TWO parallel road systems:
4
+ > **A) City grid roads** — Manhattan grid of cardinal segments built from prefab junction
5
+ > pieces + ONE repeated 2.5m straight prefab, a procedural "curved road" mesh overlay, and
6
+ > EasyRoads3D-style terrain benching. **B) Inter-city roads** — procedural spline-extruded
7
+ > meshes (motorway/dual carriageway/country) + generated-then-saved interchange prefabs
8
+ > (roundabouts, overpass, dumbbell). For Sindicate, A's prefab+socket+junction logic and
9
+ > the benching are the model; B is math to mine, not to copy.
10
+
11
+ ## 1. Piece taxonomy + sockets
12
+ - Junction prefabs (TerrainGeneratorWindow_CityJunctions.cs:25-31): Junction_4Way/T/LCorner
13
+ (City_Simpligon + Countryside variants). City straight: RoadSegment_Simpligon (2.5m).
14
+ - Synty source pieces referenced (POLYGON City/Town kit): Road_01/02/03, Straight_01/02/03,
15
+ Corner_01/02, Corner_End_03, Road_Crossing_01, Road_Flyover_01(+_Support), Road_Bare_01,
16
+ Road_Lines_01, Road_YellowLines_02; props Barrier_01, HighwayGuardrail, signs.
17
+ - Interchanges are GENERATED then saved as prefabs (InterCityRoadWindow.cs:48-52, 964-1188).
18
+ - **Kiwi has NO true smallest-cut piece set** — one straight + junction prefabs + curved
19
+ overlay. Turn/ramp/grade PIECES are net-new design for Sindicate.
20
+ - Sockets, simple form (RoadConnectorPoints.cs:46-177): child anchors named Connector_*;
21
+ road runs along local +Z; Connector_A at minZ (back), Connector_B at maxZ (front), at
22
+ road-surface height (mesh maxY), x=0. Junctions: Connector_{N,S,E,W} at ±JUNCTION_RADIUS.
23
+ - Rich form — ADOPT THIS (Runtime/Roads/JunctionConnectionPoints.cs:15-50):
24
+ ConnectionPoint { armId "N"/"S"/"E"/"W"/...; local position (road-width centre at arm
25
+ end); outward direction; RoadType {CountryRoad, SideRoad, DualCarriageway, Motorway};
26
+ laneCount; speedLimit }.
27
+
28
+ ## 2. Network data model
29
+ - ONE constant drives everything (ManhattanGridGenerator.cs:48-65): BLOCK_SIZE=50m →
30
+ ROAD_PIECE_LENGTH=2.5m, JUNCTION_SIZE=20m, junction spacing 0.4–1.2×block; gates
31
+ WATER_LEVEL=5m, MAX_SLOPE=22.5°.
32
+ - Serialized graph (CityData.cs): CityData { junctions[], roadSegments[], blocks[],
33
+ rawRoads[], seed }; JunctionData { id, position(incl terrain Y), JunctionType
34
+ {DeadEnd,Straight,TJunction,Crossroads,Complex}, connectedRoadIds, rotationY,
35
+ flattenRadius }; RoadSegmentData { start/endJunctionId, width, roadType, length,
36
+ heightDifference, splineWaypoints }. JSON-exported (_Cities.cs:2554).
37
+ - Runtime graph (RoadNetworkBuilder.cs): RoadNode merged within 1m; type classified purely
38
+ by CONNECTION COUNT (:184-208).
39
+
40
+ ## 3. Placement math
41
+ - Grid gen (ManhattanGridGenerator.Generate :412-667): population flood-fill (cells =
42
+ pop/200), per-cell water/slope reject, per-edge span validity sampled every ~5m,
43
+ dead-end → perpendicular ray → nearest road → T connector (:791-888), merge verticals,
44
+ largest-connected-component BFS filter (:673-778).
45
+ - Turns: the grid is CARDINAL-ONLY (±5°); curvature comes from the procedural curved-mesh
46
+ overlay along a straight→quadratic-bézier→straight "blue spline" (SampleModifiedSpline,
47
+ _Cities.cs:1640-1682) — because Kiwi lacks turn pieces.
48
+ - Piece fitting: numPieces = floor(dist/pieceLength), walk by pieceLength, orient by
49
+ LookRotation(direction), Y from terrain +0.1 (_Cities.cs:1559-1637).
50
+ - SplinePlacer.cs VARIABLE-SIZE mode (:123-253): grow a piece until tangent deviation
51
+ exceeds angleDeviance (3°), then CUT and scale the piece to the chord — the template for
52
+ turn-aware smallest-cut placement.
53
+ - Weld trick TO AVOID: clone each piece mesh, flatten back edge, SnapVerticesToTargets to
54
+ the junction's edge verts (_Cities.cs:1863-2062) — incompatible with InstancedMesh.
55
+ - Grades: cities are BENCHED (junction at avg terrain height in its box, :248-285; steep
56
+ segments REJECTED not ramped). Only inter-city splines carry true 3D elevation.
57
+
58
+ ## 4. Terrain stamping
59
+ - FlattenTerrainUnderCurvedRoads (…_CityTerrainFlattening.cs:35-352): ROAD_BED_OFFSET=1m,
60
+ INDENT_WIDTH=2m, SURROUNDING_WIDTH=3m. Extract road footprint POLYGONS (vert rings →
61
+ left/right edge lists with per-ring heights, :357-457) + junction polygons (72 angular
62
+ sectors +1m, :462-535); 50m spatial grid; per heightmap pixel: inside → per-edge
63
+ INTERPOLATED roadHeight − offset (grade preserved), indent → edge height − offset, skirt
64
+ → smoothstep back to terrain. Cut AND fill; region-partial writes; pixel-error 2.
65
+ - TerrainStamp.cs: R-channel heightmap stamps with blend modes (cliffs/mesas, not roads).
66
+
67
+ ## 5. Junction resolution (_CityJunctions.cs:36-404)
68
+ - Tally cardinal connections (±5°): 4 → 4-way rot 0°; 3 → T rotated so the MISSING arm
69
+ faces north (:257-260); 2 opposite → two back-to-back straight pieces (first keeps the
70
+ junction's load-bearing name); 2 perpendicular → L-corner rotated by present dirs
71
+ (:289-293); 1 → dead-end straight; 0 → marker. Junction mesh bottom seated at avg
72
+ terrain height. Names "City_X_Junction_Y" are parsed by road placement (load-bearing).
73
+
74
+ ## 6. Editor UX
75
+ - TerrainGeneratorWindow (partial class over ~15 files): Preview Cities → Generate per
76
+ city: ExportCityDataToJson → PlaceJunctionsAndFlattenTerrain → ClearGrassInCity →
77
+ PlaceRoadsAndFlattenTerrain → RoadSplineEditor blue-lines → CurvedRoadMeshGenerator →
78
+ FlattenTerrainUnderCurvedRoads → diagnostics. InterCityRoadWindow: tabbed per road type,
79
+ straight/spline gen, per-type + batch prefab saving.
80
+
81
+ ## 7. Copy vs avoid for Sindicate
82
+ COPY: centralized config; cardinal grid pipeline (flood-fill, gating, dead-end→T,
83
+ largest-component); the RICH socket model (armId+pos+dir+roadType+laneCount) with socket
84
+ transforms precomputed per prefab type; floor(dist/len) fitting + SplinePlacer
85
+ angle-deviance cutting; junction resolution by connection count + 90° rotation; EasyRoads
86
+ benching (indent+skirt, cut+fill, per-edge interpolated heights); CityData-style JSON.
87
+ AVOID: per-instance mesh cloning/vert snapping (author pieces so sockets meet EXACTLY on a
88
+ shared grid instead); three overlapping road systems (pick prefab pieces, add REAL
89
+ turn/ramp pieces); bench-only grades (Sindicate wants ramps — and already owns better
90
+ grade math: the rail formation solver); /tmp logging in loops; per-piece MeshColliders;
91
+ RoadPieceData prefab hacks.
92
+
93
+ Key files: ManhattanGridGenerator.cs, CityData.cs, RoadConnectorPoints.cs,
94
+ JunctionConnectionPoints.cs, TerrainGeneratorWindow_{Cities,CityJunctions,
95
+ CityTerrainFlattening,InterJunctionRoads}.cs, CurvedRoadMeshGenerator.cs,
96
+ RoadNetworkBuilder.cs, SplinePlacer.cs, PROCEDURAL_MAP_GENERATOR_PLAN.md (Phase 8 §4729,
97
+ postmortems §8336-8420).
@@ -0,0 +1,142 @@
1
+ # SINDICATE PREFAB ROADS — design for Nick's review (NO CODE UNTIL SIGNED OFF)
2
+
3
+ > Nick: "roads using prefabs too where we cut the smallest bit of them to allow drawing
4
+ > them in in single instances etc and allow turns and uphill downhill with them etc."
5
+ > Foundation: docs/plans/kiwi-road-recon.md (what Kiwi proved + what it never solved).
6
+
7
+ ## The one-system rule (Kiwi's lesson #1)
8
+
9
+ Kiwi grew three overlapping road systems because its grid had no real turn pieces.
10
+ Sindicate builds ONE: a PIECE GRAPH — typed prefab pieces joined socket-to-socket,
11
+ rendered as InstancedMesh batches (one per piece type), never editing geometry per
12
+ instance. Pieces are AUTHORED to mate exactly; welding-by-vertex-snap is banned.
13
+
14
+ ## 1. The piece library (per road kit, registered like setKitContent)
15
+
16
+ A game registers a ROAD KIT — content, not engine:
17
+ ```js
18
+ setRoadKit({
19
+ dir, atlas,
20
+ pieces: {
21
+ straight: [{ file, len }], // several lengths; shortest = the CUT quantum
22
+ corner: [{ file, radius, angle }], // 90° (and 45° where the pack has one)
23
+ tee: { file }, cross: { file },
24
+ ramp: [{ file, len, rise }], // grade variants (see §4)
25
+ endcap: { file },
26
+ },
27
+ width, surfaceY, // measured off the meshes once
28
+ props: { barrier, lamp, sign... }, // optional dressing hooks
29
+ })
30
+ ```
31
+ - GTA game: POLYGON_City kit (Kiwi's own source pieces — Road_01/Straight_01/Corner_01/
32
+ Road_Crossing_01/Flyover_01 family).
33
+ - Fable: no modern roads in Fantasy Kingdom — its "roads" stay the painted/benched
34
+ terrain lanes we have (the survey), optionally dressed with the pack's cobble/path
35
+ floor pieces later. The piece system is for kits that HAVE road prefabs.
36
+ - Western: stays on painted dirt roads (correct for 1880).
37
+
38
+ Sockets are NOT hand-authored: at kit registration the engine measures each piece
39
+ (bounds; road along local +Z; back socket at minZ, front at maxZ, surface at maxY —
40
+ Kiwi's convention) and builds { armId, pos, dir, laneCount } tables per type.
41
+
42
+ ## 2. The network model (designer-facing)
43
+
44
+ The DESIGNER draws what Kiwi called blue lines: polylines + junction nodes, per road
45
+ type. The layout JSON stores INTENT, the solver output stores RESULT:
46
+ ```js
47
+ roadsV2: {
48
+ nodes: [{ id, x, z }], // junction/endpoint nodes
49
+ links: [{ a, b, type, via: [{x,z}...] }], // optional via = curve hints
50
+ }
51
+ // solved (cached in the cook, not hand-edited):
52
+ // pieces: [{ type, file, x, y, z, ry, pitch, sz }...] + junction placements
53
+ ```
54
+
55
+ ## 3. The solver (engine — the new module, world/roadGraph.js)
56
+
57
+ 1. NODE RESOLUTION (Kiwi §5, copy): count links per node → cross/tee/corner/straight-
58
+ through/endcap piece + 90°-stepped rotation (T's missing arm rule verbatim).
59
+ 2. LINK FITTING: between the two junction sockets, walk the (optionally curved) path
60
+ with the SplinePlacer law: grow the current straight until tangent deviation > A°
61
+ (tunable, Kiwi used 3°), cut, place the LONGEST library straight that fits, repeat;
62
+ remainder < shortest straight → stretch the last piece via sz (the permanentWay
63
+ adaptive-panel trick — same batch, z-scale in the matrix). Corners consume their
64
+ authored arc; curve hints (via points) become corner+straight sequences.
65
+ 3. GRADES (Sindicate's advantage — Kiwi never solved this): the link's vertical profile
66
+ comes from solveFormation (rail solver: max-grade Lipschitz envelopes + vertical
67
+ curves), pieces pitch via makeChordMatrix (yaw→pitch→z-scale — sleepers stay level,
68
+ Kiwi had nothing like it). Ramp pieces are selected when |grade| exceeds what a
69
+ pitched straight reads well at; junction pads are LEVEL (yards law).
70
+ 4. OUTPUT: per-type matrix lists → panelBatch (scatterDeco road surface — walk-over law)
71
+ + collision stays the benched terrain; junction pieces placed like castings.
72
+
73
+ ## 4. Terrain: the road benches the ground (Kiwi §4 + our survey)
74
+
75
+ The solved centreline + width feeds the SAME machinery our rails use: applyRail-style
76
+ corridor blend to the piece profile (bed = surfaceY − offset), Kiwi's indent+skirt
77
+ (2m flat, 3m smoothstep) as the cross-section, cut AND fill, into heightAt → the
78
+ heightfield/zones cooks re-fingerprint (roadsV2 hash joins the cook names). The ground
79
+ is the road, the pieces dress it — the lesson the railway already proved.
80
+
81
+ ## 5. Designer UX (v4)
82
+
83
+ - ROAD MODE v2: click nodes/links instead of raw polylines; type picker; live solver
84
+ preview (pieces appear as you draw, junction pieces swap as links connect).
85
+ - MCP: designer_road_node / designer_road_link / designer_road_solve.
86
+ - Layout JSON carries roadsV2; buildDesign stamps solved pieces; graduation to a game
87
+ feeds the centrelines into setRoadNetwork for the benching + cook.
88
+
89
+ ## 6. Build order (after sign-off)
90
+
91
+ 1. roadGraph.js solver core (nodes→junction resolution, link fitting, chord matrices) —
92
+ provable headless (piece counts + junction types on synthetic networks).
93
+ 2. POLYGON_City import + road-kit registration + a test design stamped in a scratch
94
+ scene (this is also the GTA game's first world artifact).
95
+ 3. Benching integration (roadsV2 → survey corridor → cook re-fingerprint).
96
+ 4. Designer road mode v2 + MCP verbs.
97
+
98
+ ## Open questions for Nick
99
+ - OK that fable/western keep terrain-painted roads (piece roads = City pack era)?
100
+ - Corner radii: lock to the pack's authored radius only (simplest), or also synthesize
101
+ gentle curves from short straights at deviance-cut angles (Kiwi's overlay did this —
102
+ more flexible, more math)?
103
+ - Junction sprawl: POLYGON_City has flyovers/supports — v1 scope flat junctions only,
104
+ flyovers later?
105
+
106
+ ## 7. Direct salvage from Kiwi (Nick: "there are bits you could take out to reuse ofc")
107
+
108
+ **Algorithms — port near-verbatim (C# → JS is mechanical):**
109
+ - SplinePlacer.cs variable-size loop (:123-253) → the link-fitting cutter in roadGraph.js.
110
+ - ManhattanGridGenerator flood-fill + water/slope gating + dead-end→ray→T + largest-
111
+ component filter → the AUTO-CITY-GRID generator (a designer "generate district" verb —
112
+ the GTA game's city blocks for free).
113
+ - _CityJunctions junction-count→piece+rotation table (incl. the missing-arm-north rule).
114
+ - _CityTerrainFlattening footprint-polygon bench (rings→edge lists, indent+skirt,
115
+ per-edge interpolated heights) → the survey corridor cross-section.
116
+ - SampleModifiedSpline straight→bézier→straight blend → curve hints between nodes.
117
+ - JunctionConnectionPoints data model (armId/pos/dir/roadType/laneCount/speedLimit).
118
+
119
+ **Assets — the tuned meshes themselves (need ONE Unity export pass):**
120
+ - Junction_{4Way,T,LCorner}_City_Simpligon + RoadSegment_Simpligon are Nick's own
121
+ tuned pieces, stored as Unity .asset meshes (Road/Markings/Sidewalk sub-meshes per
122
+ junction — the sub-mesh split is EXACTLY our flattenToGeometry split convention).
123
+ - The generated interchange prefabs (SimpleRoundabout_01, OverpassRoundabout, Dumbbell
124
+ variants in Prefabs/Roads/) are real saved meshes — a whole interchange library.
125
+ - ASK NICK: open Kiwi in Unity once and export these to FBX (adapt the existing
126
+ Editor/ExportMyAssets.cs, or the FBX Exporter package) → we import them as a
127
+ "KiwiRoads" pack through the pack importer and the road kit is instantly armed with
128
+ proven pieces + interchanges. ~10 minutes of Unity time buys the whole piece library.
129
+
130
+ **Data:** BLOCK_SIZE-derived constant table; RoadType/laneCount/speedLimit enums.
131
+
132
+ ## 8. The rest of AIGenerateMap (Nick: "and the other bits too ofc")
133
+
134
+ Beyond roads, the Kiwi world-builder code to mine (all plain C#, no Unity needed):
135
+ - ManhattanGridGenerator + CityData → designer "generate district" verb (city blocks).
136
+ - TerrainStamp (blend-mode heightmap stamps) + VoronoiNoise + FalloffGenerator → port
137
+ into engine geo.js (Voronoi + stamps are real gaps; fbm family already ours).
138
+ - Terrain/Vegetation/GrassDecorator (+Configs) → config-shape inspiration for our
139
+ placement/flora systems (machinery stays ours).
140
+ - TerrainTextureApplicator + colormaps → heightfield paint reference.
141
+ - InterCityRoadWindow spline/interchange generation → mined per-need for the solver.
142
+ - NewMapConfig/GeneratedTerrainData assets → GTA-game map parameter reference.
@@ -0,0 +1,43 @@
1
+ # THE THREE CITIES RUN — Nick's founding request for the World Designer
2
+
3
+ > "come up with a huge map with 3 cities 6 towns and road networks between them all
4
+ > using the prefabs from the packs"
5
+
6
+ ## The pipeline (all pieces SHIPPED as of 0.14)
7
+
8
+ 1. **Author** — an MCP-driven Claude session drives `/designer.html` on fable (5317)
9
+ through the `aldenvale-designer` tools (place/move/road/camera/save) while the human
10
+ watches; or write the JSON directly (it is the same artifact). Layouts save to
11
+ `fable/designs/<name>.json`.
12
+ 2. **Preview** — boot `?autostart&design=<name>`: engine `buildDesign()` stamps the
13
+ layout live (visual-only, post-BVH).
14
+ 3. **Integrate** — stamp the accepted designs in `world.build()` BEFORE the BVH bake
15
+ (collision + grass occupancy for free), and feed the design `roads` polylines into
16
+ the terrain survey (`setRoadNetwork`) so the ground benches under them → re-cook.
17
+
18
+ ## Authoring facts
19
+
20
+ - Palette: POLYGON_Fantasy_Kingdom, 2,186 models. The `SM_Bld_Preset_*_Optimized`
21
+ composites are WHOLE dressed buildings — cities assemble from these, not wall pieces.
22
+ Atlas: `PolygonFantasyKingdom_Texture_01_A.png` (every entry carries `tex`).
23
+ - Layout entry: `{ file, dir, x, z, ry, y?, s?, tex }`; omit `y` to seat on terrain.
24
+ - World: 4320 m square, Aldenvale relief is real — site cities on the flats
25
+ (designer viewport shows the relief; check `heightAt` spread before committing a site).
26
+ - Existing settlements to respect (never overlap): Oakhollow (0,14 r~90), the castle
27
+ (1120,800), Bracklow, VILLAGES/CAMPS lists, Ironkeep (DWARF), Ravenspire (-1400,-1200).
28
+ - Compass: north = −z. Roads: polylines in `roads`, ≥12 m from any building
29
+ (the farm-rect law), gentle bends (the survey benches them).
30
+
31
+ ## Suggested sites (from the relief + the existing map)
32
+
33
+ - City A: the plain SE of Oakhollow (~500, 400) — river-adjacent, flat.
34
+ - City B: the western meadows (~-900, 300).
35
+ - City C: the north coast road (~300, -1100).
36
+ - Six towns strung on the road net between them + the existing villages as waypoints.
37
+ - Budget: city ≈ 40-70 presets + walls/props; town ≈ 12-20. Total ≈ 350 placements —
38
+ trivial for the stamper (49k placements cook in seconds).
39
+
40
+ ## Definition of done
41
+
42
+ Every city/town saved as its own design file (composable), one master road list,
43
+ preview screenshots of each site, Nick signs the look before integration.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sindicate",
3
- "version": "0.13.0",
3
+ "version": "0.16.0",
4
4
  "description": "Sindicate — open-world game engine for three.js WebGPU. Streaming worlds, characters, mounts & vehicles, missions, crowds.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -29,7 +29,8 @@
29
29
  "src",
30
30
  "tools",
31
31
  "docs",
32
- "README.md"
32
+ "README.md",
33
+ "CHANGELOG.md"
33
34
  ],
34
35
  "peerDependencies": {
35
36
  "three": "^0.184.0",
@@ -38,6 +38,13 @@ export function setTrainContent(c = {}) {
38
38
  if (c.colliders) colliders = c.colliders;
39
39
  if (c.dropItemsFor) dropItemsFor = c.dropItemsFor;
40
40
  if (c.railPaths) SURVEY_PATHS = c.railPaths;
41
+ // THE TRACK AUTHOR'S OWN SURVEY. Without it the fleet grades its own polyline and runs
42
+ // beside the rails the county actually laid.
43
+ if (c.railLines) LINES_IN = c.railLines;
44
+ if (c.railLineStops) STOPS_IN = c.railLineStops;
45
+ if (c.levelCrossing) XING_IN = c.levelCrossing;
46
+ if (c.junction) JUNC_IN = c.junction;
47
+ if (c.railTop != null) { RAIL_TOP = c.railTop; RAIL_Y = RAIL_TOP - FLANGE; }
41
48
  if (c.railStops) SURVEY_STOPS = c.railStops;
42
49
  if (c.crossing !== undefined) SURVEY_CROSSING = c.crossing;
43
50
  if (c.halts) RAIL_HALTS = c.halts;
@@ -89,18 +96,21 @@ import { attachTrainCamera } from './trainCamera.js';
89
96
  const _mods = import.meta.glob(['../world/railway.js', '../world/data/railway.js'], { eager: true });
90
97
  const RD = _mods['../world/data/railway.js'] ?? {};
91
98
 
92
- const LINES_IN = RD.RAIL_LINES ?? null; // { main: {nodes,len}, spur: {nodes,len} }
93
- const STOPS_IN = RD.RAIL_STOPS ?? null; // [{ id, name, line, s, platformSide, wait }]
94
- const XING_IN = RD.LEVEL_CROSSING ?? null; // { line, s, x, z }
95
- const JUNC_IN = RD.JUNCTION ?? null; // { line, s, x, z }
99
+ // ...and inside the ENGINE that glob resolves against the engine's own folder, where no
100
+ // county's railway data lives so these MUST be injectable. A game hands its solved survey
101
+ // to setTrainContent; the glob remains for a game that still keeps train.js's neighbours.
102
+ let LINES_IN = RD.RAIL_LINES ?? null; // { main: {nodes,len}, spur: {nodes,len} }
103
+ let STOPS_IN = RD.RAIL_STOPS ?? null; // [{ id, name, line, s, platformSide, wait }]
104
+ let XING_IN = RD.LEVEL_CROSSING ?? null; // { line, s, x, z }
105
+ let JUNC_IN = RD.JUNCTION ?? null; // { line, s, x, z }
96
106
 
97
107
  // The railhead above the formation — his number, not mine. A car's origin sits at RAILHEAD CONTACT
98
108
  // (measured: min.y = 0 on all four models), so this is exactly how high off the ballast it rides,
99
109
  // less a flange: the wheel's lowest point is its flange, not its tread, and standing the tread ON
100
110
  // the rail is what closes the hairline of daylight under a stationary loco.
101
- const RAIL_TOP = RD.RAIL_TOP ?? 0.3285;
111
+ let RAIL_TOP = RD.RAIL_TOP ?? 0.3285;
102
112
  const FLANGE = 0.06;
103
- const RAIL_Y = RAIL_TOP - FLANGE;
113
+ let RAIL_Y = RAIL_TOP - FLANGE;
104
114
 
105
115
  // ── THE FALLBACK ROUTE (only if data/railway.js has not landed) ───────────────────────────────
106
116
  const FALLBACK_PATHS = [