@jgengine/core 0.26.0 → 0.27.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/game/playableGame.d.ts +19 -2
  3. package/dist/gameplay.d.ts +5 -0
  4. package/dist/gameplay.js +5 -0
  5. package/dist/inventory/carryLoad.d.ts +48 -0
  6. package/dist/inventory/carryLoad.js +44 -0
  7. package/dist/inventory/equipment.d.ts +95 -0
  8. package/dist/inventory/equipment.js +169 -0
  9. package/dist/inventory/inventoryModel.d.ts +15 -6
  10. package/dist/inventory/inventoryModel.js +54 -5
  11. package/dist/inventory/shapedGrid.d.ts +26 -0
  12. package/dist/inventory/shapedGrid.js +47 -0
  13. package/dist/loot/lootGen.d.ts +186 -0
  14. package/dist/loot/lootGen.js +437 -0
  15. package/dist/loot/lootWeapon.d.ts +35 -0
  16. package/dist/loot/lootWeapon.js +70 -0
  17. package/dist/meta/changelog.d.ts +1 -1
  18. package/dist/meta/changelog.js +37 -1
  19. package/dist/nav/laneGraph.d.ts +178 -0
  20. package/dist/nav/laneGraph.js +671 -0
  21. package/dist/nav/traffic.d.ts +223 -0
  22. package/dist/nav/traffic.js +919 -0
  23. package/dist/physics/kinematicVehicle.d.ts +36 -0
  24. package/dist/physics/kinematicVehicle.js +15 -1
  25. package/dist/physics/vehicleDamage.d.ts +136 -0
  26. package/dist/physics/vehicleDamage.js +214 -0
  27. package/dist/physics/vehicleParts.d.ts +62 -0
  28. package/dist/physics/vehicleParts.js +65 -0
  29. package/dist/progression/gearScore.d.ts +56 -0
  30. package/dist/progression/gearScore.js +59 -0
  31. package/dist/progression/skillTree.d.ts +129 -0
  32. package/dist/progression/skillTree.js +265 -0
  33. package/dist/progression/xpCurve.d.ts +60 -0
  34. package/dist/progression/xpCurve.js +141 -0
  35. package/dist/scene/builtinSceneKinds.js +2 -0
  36. package/dist/world/decalKind.d.ts +48 -0
  37. package/dist/world/decalKind.js +86 -0
  38. package/dist/world/features.d.ts +3 -1
  39. package/dist/world/features.js +1 -0
  40. package/dist/world/grassKind.d.ts +5 -0
  41. package/dist/world/grassKind.js +3 -0
  42. package/dist/world/terraform.d.ts +8 -0
  43. package/dist/world.d.ts +5 -1
  44. package/dist/world.js +4 -0
  45. package/package.json +1 -1
  46. package/skills/jgengine/api.md +2 -2
  47. package/skills/jgengine-combat/SKILL.md +3 -1
  48. package/skills/jgengine-gameplay/SKILL.md +31 -0
  49. package/skills/jgengine-gameplay/api.md +150 -6
  50. package/skills/jgengine-gameplay/capabilities.md +48 -3
  51. package/skills/jgengine-gameplay/reference-systems.md +118 -0
  52. package/skills/jgengine-world/SKILL.md +4 -0
  53. package/skills/jgengine-world/api.md +115 -5
  54. package/skills/jgengine-world/capabilities.md +20 -0
  55. package/skills/jgengine-world/recipes/vehicle-feel.md +5 -0
@@ -0,0 +1,35 @@
1
+ import { type WeaponDefinition } from "../combat/weaponDefinition.js";
2
+ import type { LootItem } from "./lootGen.js";
3
+ /**
4
+ * Which rolled stat feeds which part of a {@link WeaponDefinition}. Every field is a stat id from the
5
+ * item; an omitted field keeps the template's value, so a catalog only maps what it rolls.
6
+ */
7
+ export interface LootWeaponStatMap {
8
+ roundsPerMinute?: string;
9
+ damage?: string;
10
+ pellets?: string;
11
+ /** Requires the template to declare a magazine. */
12
+ magazineCapacity?: string;
13
+ reloadMs?: string;
14
+ /** Spread half-angle in radians, clamped to `0..PI/2`. */
15
+ spreadBase?: string;
16
+ /** Hitscan range, or a projectile's hard distance cap. */
17
+ maxRange?: string;
18
+ /** Projectile launch speed. */
19
+ projectileSpeed?: string;
20
+ }
21
+ /** A base weapon definition plus the stat map a rolled item overrides. */
22
+ export interface LootWeaponProjection {
23
+ template: WeaponDefinition;
24
+ stats: LootWeaponStatMap;
25
+ /** Definition id override; defaults to `loot.<baseId>.<rarity>`. */
26
+ id?: string;
27
+ }
28
+ /**
29
+ * Project a rolled item onto a plain weapon definition: copy the template, then write the mapped
30
+ * stats into rate, damage, magazine, spread, range and projectile speed. The result is a
31
+ * {@link WeaponDefinition}, so `assertWeaponDefinition` and `createWeaponSystem` accept it unchanged.
32
+ *
33
+ * @capability loot-weapon feed a rolled item into a weapon definition so generated gear fires through the weapon system
34
+ */
35
+ export declare function lootWeaponDefinition(item: LootItem, projection: LootWeaponProjection): WeaponDefinition;
@@ -0,0 +1,70 @@
1
+ import { MAX_WEAPON_PELLETS_PER_SHOT, } from "../combat/weaponDefinition.js";
2
+ function statOf(item, statId) {
3
+ if (statId === undefined)
4
+ return undefined;
5
+ const value = item.stats[statId];
6
+ if (value === undefined)
7
+ throw new RangeError(`Loot weapon projection names stat "${statId}" but the item does not roll it`);
8
+ if (!Number.isFinite(value))
9
+ throw new RangeError(`Loot weapon projection stat "${statId}" is ${String(value)}`);
10
+ return value;
11
+ }
12
+ function clamp(value, min, max) {
13
+ return Math.min(max, Math.max(min, value));
14
+ }
15
+ /**
16
+ * Project a rolled item onto a plain weapon definition: copy the template, then write the mapped
17
+ * stats into rate, damage, magazine, spread, range and projectile speed. The result is a
18
+ * {@link WeaponDefinition}, so `assertWeaponDefinition` and `createWeaponSystem` accept it unchanged.
19
+ *
20
+ * @capability loot-weapon feed a rolled item into a weapon definition so generated gear fires through the weapon system
21
+ */
22
+ export function lootWeaponDefinition(item, projection) {
23
+ const template = projection.template;
24
+ const map = projection.stats;
25
+ const definition = {
26
+ ...template,
27
+ id: projection.id ?? `loot.${item.baseId}.${item.rarity}`,
28
+ delivery: { ...template.delivery },
29
+ handling: { ...template.handling, spread: { ...template.handling.spread } },
30
+ damage: { ...template.damage },
31
+ ...(template.magazine === undefined ? {} : { magazine: { ...template.magazine } }),
32
+ ...(template.burst === undefined ? {} : { burst: { ...template.burst } }),
33
+ ...(template.falloff === undefined ? {} : { falloff: { ...template.falloff } }),
34
+ };
35
+ const rate = statOf(item, map.roundsPerMinute);
36
+ if (rate !== undefined)
37
+ definition.roundsPerMinute = Math.max(Number.MIN_VALUE, rate);
38
+ const damage = statOf(item, map.damage);
39
+ if (damage !== undefined)
40
+ definition.damage.base = Math.max(Number.MIN_VALUE, damage);
41
+ const pellets = statOf(item, map.pellets);
42
+ if (pellets !== undefined) {
43
+ definition.pellets = clamp(Math.round(pellets), 1, MAX_WEAPON_PELLETS_PER_SHOT);
44
+ }
45
+ const capacity = statOf(item, map.magazineCapacity);
46
+ const reloadMs = statOf(item, map.reloadMs);
47
+ if (capacity !== undefined || reloadMs !== undefined) {
48
+ if (definition.magazine === undefined) {
49
+ throw new RangeError('Loot weapon projection maps magazineCapacity or reloadMs but the template has no magazine');
50
+ }
51
+ if (capacity !== undefined)
52
+ definition.magazine.capacity = Math.max(1, Math.round(capacity));
53
+ if (reloadMs !== undefined)
54
+ definition.magazine.reloadMs = Math.max(0, reloadMs);
55
+ }
56
+ const spread = statOf(item, map.spreadBase);
57
+ if (spread !== undefined)
58
+ definition.handling.spread.base = clamp(spread, 0, Math.PI / 2);
59
+ const range = statOf(item, map.maxRange);
60
+ if (range !== undefined)
61
+ definition.delivery.maxRange = Math.max(Number.MIN_VALUE, range);
62
+ const speed = statOf(item, map.projectileSpeed);
63
+ if (speed !== undefined) {
64
+ if (definition.delivery.kind !== "projectile") {
65
+ throw new RangeError('Loot weapon projection maps projectileSpeed but the template is hitscan');
66
+ }
67
+ definition.delivery.speed = Math.max(Number.MIN_VALUE, speed);
68
+ }
69
+ return definition;
70
+ }
@@ -1,5 +1,5 @@
1
1
  /** Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating. */
2
- export declare const VERSION = "0.26.0";
2
+ export declare const VERSION = "0.27.0";
3
3
  /** One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md). */
4
4
  export interface ChangelogEntry {
5
5
  migrate: readonly string[];
@@ -1,7 +1,43 @@
1
1
  /** Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating. */
2
- export const VERSION = "0.26.0";
2
+ export const VERSION = "0.27.0";
3
3
  /** Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`). */
4
4
  export const CHANGELOG = {
5
+ "0.27.0": {
6
+ migrate: [
7
+ "Bump lockstep SDK packages to `^0.27.0`: `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.24.0`; `@jgengine/github` is `0.14.0`.",
8
+ ],
9
+ added: [
10
+ "`rollLoot` (`@jgengine/core/loot/lootGen`) rolls a named item from a caller `LootCatalog`: bases with mount slots, parts, affix pools, weighted rarity tiers, manufacturer personalities and a `levelScale` curve. Stats are `base × rarity.statScale × levelScale`, then part stats/multipliers, maker `bias`/`scale`, then additive and multiplicative affixes; additive affix rolls scale with level, multiplicative ones do not.",
11
+ "`createLootRoller` wraps a validated catalog with roll counts per rarity, `snapshot()`/`restore()`/`reset()` and an optional devtools probe; `assertLootCatalog` rejects duplicate ids, unresolvable pools, empty item slots and a non-ascending level curve at construction.",
12
+ "`validateLootItem` returns coded issues (`unknown-base`, `manufacturer-base`, `missing-slot-part`, `part-category`, `unknown-affix`, `empty-name`, …) and `compareLootItems` returns sorted per-stat deltas plus a weighted score with caller-named `lowerIsBetter` stats.",
13
+ "`lootWeaponDefinition` (`@jgengine/core/loot/lootWeapon`) projects a rolled item onto a `WeaponDefinition` through a caller stat map, so a generated gun runs in `createWeaponSystem` after `assertWeaponDefinition`.",
14
+ "`defineXpCurve` (`@jgengine/core/gameplay`) resolves one authored growth rule (`flat`, `linear`, `power`, `geometric`, `table`) plus a `maxLevel` cap into per-level thresholds, lifetime totals, a plain-words rule string and a `leveling` track.",
15
+ "`createSkillTree` spends and refunds points over authored nodes with `cost`, `requires`, `excludes`, passives, actives and multi-node `synergies`; `resolved()` returns the stat modifiers and actions, and state snapshots/restores as `{ points, ranks }`.",
16
+ "`gearScore` / `compareGearSets` score a whole loadout from stat weights, level, rarity and slot coverage.",
17
+ "`lootStatContributions` and `scoreLootStats` expose the weight policy `compareLootItems` already used.",
18
+ "`createEquipment` (`@jgengine/core/inventory/equipment`) wears gear by slot: slot defs declare accepted kinds and the slots they block, item defs declare the slots an item may occupy, a filled slot swaps and hands back the replaced item, and a two-handed item and the slot it blocks cannot both be filled. `state()` / `restore()` round-trip the worn set; a devtools probe reports filled slots.",
19
+ "`characterLoad` and `canCarry` (`@jgengine/core/inventory/carryLoad`) weigh carried inventories plus worn gear against a capacity and hand back the shared encumbrance bands (tier, `moveMultiplier`, sprint/jump gates, `remaining`), so a stash left out of `carried` adds no weight.",
20
+ "`fitShapedItem` (`@jgengine/core/inventory/shapedGrid`) places a shaped item at its first fit or names why not: `too-large` (with both sizes), `no-space`, `budget-exceeded`.",
21
+ "`InventorySet.snapshot()` / `restore()` / `reset()` (`@jgengine/core/inventory/inventoryModel`) save and reload every declared inventory as detached plain data, so a bag and a stash never share live slots.",
22
+ "`splitStack` takes an optional stack `limit`, rejecting `over-stack` instead of merging past the max stack.",
23
+ "`createLaneGraph` and `laneGraphFromDocument` (`@jgengine/core/world`) read an authored lane graph out of the scene document: `traffic_lane` paths are one directed span each, `road` paths (the street bake) become a lane per direction split where streets cross, `traffic_intersection` markers carry signal phases and right-of-way in `meta`, and `traffic_parking` markers place kerbside spots.",
24
+ "`createTraffic` (`@jgengine/core/world`) runs cars on that graph: IDM car following and braking, one car at a time through each intersection box behind a signal phase or first-come right-of-way, lane changes across authored neighbours, parking that reserves a spot, deterministic injected variety, `snapshot`/`restore`, and a devtools probe.",
25
+ "`Traffic.setDemand(count)` is the fleet target; the time-of-day curve stays the caller's policy.",
26
+ "`applyVehicleParts` (`@jgengine/core/world`) composes data-driven tuning parts onto a `KinematicVehicleTuning` — stat multipliers/addends applied in install order plus an optional tire grip curve — and returns the composed tuning, the installed ids, and a `VehicleFeel` (top speed, drive, brakes, turn rate, handbrake grip, and the grip curve's peak/slide). `withPart` swaps a part within its slot and `feelOf` reads the numbers out of any tuning.",
27
+ "`createVehicleDamage` (`@jgengine/core/world`) is fixed-zone crash damage wired to handling and visuals: impacts accumulate into discrete stages, each stage costs grip/drive/brakes/top speed/drag and pulls the steering (`effect`), and each zone shows one bounded dent (`deformation`) that never exceeds its authored `deform`. Parts can detach at a set stage, a total impulse can disable the vehicle, and `snapshot`/`restore` resume mid-crash. `damagedTuning` and `damagedAxis` fold the effect into a tuning or an input, and `modifiers()` carries it as per-tick overrides.",
28
+ "`KinematicVehicleTuning.weightTransfer` (`@jgengine/core/world`) adds load-transfer grip: longitudinal transfer under braking/traction and lateral transfer under cornering cut the tire grip curve (tire load sensitivity), so braking into a corner holds less slide than coasting through it. Each tick reports `weightTransfer` and `loadGrip`; omitting the block keeps the previous flat-grip model exactly.",
29
+ "`KinematicVehicleModifiers.gripScale` scales the grip curve for one tick, mirroring `VehicleDynamicsModifiers.gripScale` so a damage layer drives either sim.",
30
+ "`decal` scene kind (`@jgengine/core/world/decalKind`) and its renderer (`@jgengine/shell/scene/DecalRenderer`): dirt, cracks, paint and blood decals authored as placed markers, draped on the live terrain or standing on an authored tilt, with a deterministic procedural texture per style+seed.",
31
+ "`TerrainMaterialLayer.material` (`@jgengine/core/world/terraform`): a material library name whose scanned color map paints that terrain layer. The runtime samples up to four layer maps at each layer's `tiling` and blends them through the authored mask weights (`createTerrainDetailMaterial`'s `layerMaps`), so painted terrain blends between authored layers instead of tiling one flat map.",
32
+ "`grass_field` volumes now paint a ground-cover mat under their blades (`groundCover` rule, `@jgengine/shell/terrain/GrassCover`), so an authored patch reads as turf rather than bare ground with stubble. `GrassEnvironmentConfig.groundCover` does the same for `grass()` patches.",
33
+ "`ModelConfig.wind` (`@jgengine/core/game/playableGame`) and `applyModelWind` (`@jgengine/shell/render/modelWind`): per-model foliage sway with height-ramped travel, shared gust clock and per-model strength/speed/direction.",
34
+ ],
35
+ changed: [
36
+ "The player shell re-renders only for the ctx slices it reads — the possessed entity and the touch control mode — instead of on every `ctx` write. Subscribing the shell root to the raw ctx version reconciled the canvas, the whole actor tree and the HUD on each per-frame entity pose write, which is what `subscribeMembership` and the markers' imperative `useFrame` pose reads exist to avoid (#625). Scrap Signal: 86.6 ms → 16.7 ms per frame at the same 621 draws; Harbor Heat: 102.8 ms → 46.2 ms at the same ~7.2k draws.",
37
+ "The `ocean()` environment feature passes the live ground to its shader, so the sea shades shallow→deep and its shoreline foam band lands where the bed meets the surface.",
38
+ ],
39
+ removed: [],
40
+ },
5
41
  "0.26.0": {
6
42
  migrate: [
7
43
  "Bump lockstep SDK packages to `^0.26.0`: `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.23.0`; `@jgengine/github` is `0.13.0`.",
@@ -0,0 +1,178 @@
1
+ import type { EditorDocument } from "../editor/types.js";
2
+ import type { Waypoint } from "./pathFollow.js";
3
+ /** A junction where lane spans meet. */
4
+ export interface LaneNode {
5
+ id: string;
6
+ at: Waypoint;
7
+ }
8
+ /** One directed lane span to resolve into the graph. */
9
+ export interface LaneEdgeConfig {
10
+ id: string;
11
+ /** World-space centerline, in travel order. */
12
+ points: readonly Waypoint[];
13
+ /** Junction ids; omitted endpoints are joined geometrically by {@link LaneGraphConfig.joinRadius}. */
14
+ from?: string;
15
+ to?: string;
16
+ /** Speed limit in world units per second. */
17
+ speed?: number;
18
+ width?: number;
19
+ /** Ids of the parallel lanes on either side, used for lane changes. */
20
+ left?: string;
21
+ right?: string;
22
+ /** Arc positions, in metres, of parking spots on this lane. */
23
+ parking?: readonly number[];
24
+ /** Allowed as a traffic spawn point. */
25
+ entry?: boolean;
26
+ /** Allowed as a traffic removal point. */
27
+ exit?: boolean;
28
+ }
29
+ /** Signal timing for one intersection; phases list the in-lanes each green window serves. */
30
+ export interface LaneSignalProgram {
31
+ greenSeconds: number;
32
+ yellowSeconds: number;
33
+ /** Red-for-all interval between phases. Default 0. */
34
+ allRedSeconds?: number;
35
+ /**
36
+ * In-lane ids served by each green phase, in cycle order. Omitted lets the traffic runtime derive
37
+ * them: one green per line of travel, so opposite approaches share a window.
38
+ */
39
+ phases?: readonly (readonly string[])[];
40
+ }
41
+ /** How an intersection resolves which approach may enter next. */
42
+ export type LaneRightOfWay = "signal" | "priority";
43
+ /** Right-of-way and signal timing for one authored crossing. */
44
+ export interface LaneIntersectionConfig {
45
+ node: string;
46
+ /** Default `"priority"` (first come, first served) unless a signal program is given. */
47
+ kind?: LaneRightOfWay;
48
+ signal?: LaneSignalProgram;
49
+ /** Metres before the lane end where a car holds at the stop line. Default 6. */
50
+ stopLine?: number;
51
+ /** Seconds the signal cycle is shifted, to unsynchronize a street of intersections. Default 0. */
52
+ offsetSeconds?: number;
53
+ }
54
+ /** A resolved lane span: centerline, arc lengths, limit and adjacency. */
55
+ export interface LaneSpan {
56
+ id: string;
57
+ from: string;
58
+ to: string;
59
+ points: readonly Waypoint[];
60
+ /** Per-segment lengths; one fewer than `points`. */
61
+ segmentLengths: readonly number[];
62
+ length: number;
63
+ speed: number;
64
+ width: number;
65
+ left: string | null;
66
+ right: string | null;
67
+ parking: readonly number[];
68
+ entry: boolean;
69
+ exit: boolean;
70
+ }
71
+ /** A resolved crossing: the lanes that feed it, the lanes that leave it, and its right-of-way. */
72
+ export interface LaneIntersection {
73
+ node: string;
74
+ at: Waypoint;
75
+ kind: LaneRightOfWay;
76
+ inLanes: readonly string[];
77
+ outLanes: readonly string[];
78
+ stopLine: number;
79
+ signal: LaneSignalProgram | null;
80
+ offsetSeconds: number;
81
+ }
82
+ /** A parking spot projected onto one lane at an arc position. */
83
+ export interface LaneSpot {
84
+ id: string;
85
+ lane: string;
86
+ /** Arc, in metres, along the lane where a car stands parked. */
87
+ arc: number;
88
+ }
89
+ /** World pose of a lane at an arc position. */
90
+ export interface LanePose {
91
+ laneId: string;
92
+ arc: number;
93
+ position: Waypoint;
94
+ /** Yaw of travel, matching engine `rotationY`. */
95
+ heading: number;
96
+ }
97
+ /** The authored lane graph: junctions, directed spans, crossings and parking spots. */
98
+ export interface LaneGraphConfig {
99
+ /** Junction positions; lanes may name them in `from`/`to` instead of joining by geometry. */
100
+ nodes?: readonly LaneNode[];
101
+ lanes: readonly LaneEdgeConfig[];
102
+ intersections?: readonly LaneIntersectionConfig[];
103
+ spots?: readonly LaneSpot[];
104
+ /** Distance within which two lane endpoints are the same junction. Default 3. */
105
+ joinRadius?: number;
106
+ }
107
+ /**
108
+ * Authored lane topology: junctions, directed spans with speed limits and arc lengths, crossings with
109
+ * their right-of-way, and parking spots. Positions come from the spans themselves, so a traffic
110
+ * runtime never re-derives geometry or runs a path search per vehicle.
111
+ */
112
+ export interface LaneGraph {
113
+ node(id: string): LaneNode | null;
114
+ lane(id: string): LaneSpan | null;
115
+ laneIds(): readonly string[];
116
+ nodeIds(): readonly string[];
117
+ outLanes(nodeId: string): readonly string[];
118
+ inLanes(nodeId: string): readonly string[];
119
+ intersection(nodeId: string): LaneIntersection | null;
120
+ intersections(): readonly LaneIntersection[];
121
+ spots(): readonly LaneSpot[];
122
+ spot(id: string): LaneSpot | null;
123
+ /** Lanes a car may spawn on: an authored entry, or a lane whose start has no inbound traffic. */
124
+ entries(): readonly string[];
125
+ /** Lanes that leave the graph: an authored exit, or a lane that ends where nothing continues. */
126
+ exits(): readonly string[];
127
+ /** World pose at `arc` metres, shifted `lateral` metres to the driver's left. */
128
+ pose(laneId: string, arc: number, lateral?: number): LanePose | null;
129
+ /** Arc on `laneId` nearest a world XZ point, clamped to the span. */
130
+ project(laneId: string, x: number, z: number): number;
131
+ /** Travel heading at `arc` along a lane, in radians. */
132
+ heading(laneId: string, arc: number): number;
133
+ }
134
+ /**
135
+ * Write the world pose at `arc` along a lane into a caller-owned record — the allocation-free form
136
+ * {@link LaneGraph.pose} wraps, for a runtime that poses hundreds of cars per tick.
137
+ * @internal
138
+ */
139
+ export declare function lanePoseInto(lane: LaneSpan, arc: number, lateral: number, out: {
140
+ x: number;
141
+ y: number;
142
+ z: number;
143
+ heading: number;
144
+ }): void;
145
+ /**
146
+ * Read handle over the graph resolved once at construction. The build mutates its working set; this
147
+ * object never changes, so a save system rebuilds it from the scene document instead of serializing it.
148
+ *
149
+ * @capability lane-graph directed lane topology with junctions, signals and parking for vehicle traffic
150
+ */
151
+ export declare function createLaneGraph(config: LaneGraphConfig): LaneGraph;
152
+ /** How the document reader recognizes authored lane content. */
153
+ export interface LaneGraphDocumentOptions {
154
+ /** Path kinds authored as one directed lane each. Default `["traffic_lane"]`. */
155
+ laneKinds?: readonly string[];
156
+ /** Path kinds authored as a two-way road centerline, expanded into a lane per direction. Default `["road"]`. */
157
+ roadKinds?: readonly string[];
158
+ /** Marker kinds that carry an intersection's right-of-way in `meta`. Default `["traffic_intersection"]`. */
159
+ intersectionKinds?: readonly string[];
160
+ /** Marker kinds that place a parking spot. Default `["traffic_parking"]`. */
161
+ parkingKinds?: readonly string[];
162
+ /** Distance within which lane endpoints and markers join a junction. Default 3. */
163
+ joinRadius?: number;
164
+ /** Speed limit, in world units per second, by authored `meta.level`. */
165
+ speedByLevel?: Readonly<Record<string, number>>;
166
+ /** Speed limit for a path with no recognized level. Default 13. */
167
+ defaultSpeed?: number;
168
+ }
169
+ /**
170
+ * Read a lane graph out of an authored scene document. `traffic_lane` paths are one directed lane each;
171
+ * `road` paths (the editor's street bake) become a lane per direction, offset onto their own side of
172
+ * the road; `traffic_intersection` markers carry a signal program and right-of-way in `meta`;
173
+ * `traffic_parking` markers place parking spots. Runtime positions come from the paths, so editing a
174
+ * road moves its traffic instead of leaving a duplicate lane behind.
175
+ *
176
+ * @capability lane-graph-from-scene read an authored lane graph, signals and parking out of the scene document
177
+ */
178
+ export declare function laneGraphFromDocument(doc: Pick<EditorDocument, "paths" | "markers">, options?: LaneGraphDocumentOptions): LaneGraph;