@jgengine/core 0.11.0 → 0.13.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 (256) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/ai/populationDirector.d.ts +103 -0
  3. package/dist/ai/populationDirector.js +190 -0
  4. package/dist/ai/pursuit.d.ts +56 -0
  5. package/dist/ai/pursuit.js +53 -0
  6. package/dist/authoring.d.ts +1 -1
  7. package/dist/authoring.js +1 -1
  8. package/dist/combat/effects.d.ts +11 -4
  9. package/dist/combat/effects.js +24 -17
  10. package/dist/combat/magazine.d.ts +13 -0
  11. package/dist/combat/magazine.js +27 -0
  12. package/dist/combat/vfxPresets.d.ts +176 -0
  13. package/dist/combat/vfxPresets.js +74 -0
  14. package/dist/combat/weaponFire.d.ts +114 -0
  15. package/dist/combat/weaponFire.js +96 -0
  16. package/dist/combat.d.ts +5 -2
  17. package/dist/combat.js +4 -1
  18. package/dist/devtools/controls.d.ts +38 -0
  19. package/dist/devtools/controls.js +181 -0
  20. package/dist/devtools/devtools.d.ts +5 -197
  21. package/dist/devtools/devtools.js +38 -774
  22. package/dist/devtools/discover.d.ts +32 -0
  23. package/dist/devtools/discover.js +240 -0
  24. package/dist/devtools/fallbackSeams.d.ts +50 -0
  25. package/dist/devtools/fallbackSeams.js +111 -0
  26. package/dist/devtools/frame.d.ts +15 -0
  27. package/dist/devtools/frame.js +247 -0
  28. package/dist/devtools/logs.d.ts +15 -0
  29. package/dist/devtools/logs.js +86 -0
  30. package/dist/devtools/overrides.d.ts +16 -0
  31. package/dist/devtools/overrides.js +66 -0
  32. package/dist/devtools/types.d.ts +212 -0
  33. package/dist/devtools/types.js +1 -0
  34. package/dist/devtools/urlFlags.d.ts +22 -0
  35. package/dist/devtools/urlFlags.js +62 -0
  36. package/dist/economy/auctionBook.d.ts +122 -0
  37. package/dist/economy/auctionBook.js +213 -0
  38. package/dist/economy/priceHistory.d.ts +44 -0
  39. package/dist/economy/priceHistory.js +89 -0
  40. package/dist/economy/unlockPoints.d.ts +92 -0
  41. package/dist/economy/unlockPoints.js +103 -0
  42. package/dist/editor/commandApply.d.ts +5 -0
  43. package/dist/editor/commandApply.js +755 -0
  44. package/dist/editor/commands.d.ts +48 -3
  45. package/dist/editor/commands.js +40 -754
  46. package/dist/editor/directives.d.ts +94 -0
  47. package/dist/editor/directives.js +206 -0
  48. package/dist/editor/document.d.ts +37 -2
  49. package/dist/editor/document.js +422 -6
  50. package/dist/editor/entityCatalog.d.ts +42 -0
  51. package/dist/editor/entityCatalog.js +101 -0
  52. package/dist/editor/environment.d.ts +74 -0
  53. package/dist/editor/environment.js +169 -0
  54. package/dist/editor/index.d.ts +26 -1
  55. package/dist/editor/index.js +8 -1
  56. package/dist/editor/minimap.d.ts +31 -0
  57. package/dist/editor/minimap.js +73 -0
  58. package/dist/editor/streamer.d.ts +66 -0
  59. package/dist/editor/streamer.js +68 -0
  60. package/dist/editor/types.d.ts +143 -0
  61. package/dist/editor/world.d.ts +136 -0
  62. package/dist/editor/world.js +303 -0
  63. package/dist/game/breeding.d.ts +161 -0
  64. package/dist/game/breeding.js +181 -0
  65. package/dist/game/cameraConfig.d.ts +451 -0
  66. package/dist/game/cameraConfig.js +1 -0
  67. package/dist/game/clipRoles.d.ts +51 -0
  68. package/dist/game/clipRoles.js +142 -0
  69. package/dist/game/defineGame.d.ts +16 -9
  70. package/dist/game/defineGame.js +14 -6
  71. package/dist/game/defineSystem.d.ts +2 -0
  72. package/dist/game/defineSystem.js +2 -0
  73. package/dist/game/events.d.ts +28 -0
  74. package/dist/game/partAnimation.d.ts +78 -0
  75. package/dist/game/partAnimation.js +147 -0
  76. package/dist/game/playableGame.d.ts +72 -440
  77. package/dist/game/playableGame.js +1 -1
  78. package/dist/game/progression.d.ts +35 -0
  79. package/dist/game/progression.js +22 -1
  80. package/dist/game/quest.js +1 -3
  81. package/dist/game/tribe.d.ts +184 -0
  82. package/dist/game/tribe.js +432 -0
  83. package/dist/gameplay.d.ts +3 -1
  84. package/dist/gameplay.js +3 -1
  85. package/dist/inventory/encumbrance.d.ts +69 -0
  86. package/dist/inventory/encumbrance.js +68 -0
  87. package/dist/inventory/inventoryModel.d.ts +24 -8
  88. package/dist/inventory/inventoryModel.js +47 -8
  89. package/dist/item/durability.js +1 -3
  90. package/dist/meta/changelog.d.ts +1 -1
  91. package/dist/meta/changelog.js +48 -1
  92. package/dist/movement/movementModel.d.ts +15 -1
  93. package/dist/movement/movementModel.js +33 -15
  94. package/dist/movement/playerMovement.js +102 -2
  95. package/dist/multiplayer/combatSnapshot.js +13 -8
  96. package/dist/multiplayer/presenceModel.d.ts +2 -0
  97. package/dist/multiplayer/presenceModel.js +8 -0
  98. package/dist/physics/drivableVehicle.d.ts +2 -0
  99. package/dist/physics/drivableVehicle.js +1 -1
  100. package/dist/physics/flightDynamics.d.ts +82 -0
  101. package/dist/physics/flightDynamics.js +165 -0
  102. package/dist/physics/gravityField.d.ts +25 -0
  103. package/dist/physics/gravityField.js +41 -0
  104. package/dist/physics/kinematicVehicle.d.ts +87 -0
  105. package/dist/physics/kinematicVehicle.js +193 -11
  106. package/dist/physics/physicsWorld.d.ts +26 -0
  107. package/dist/physics/physicsWorld.js +109 -117
  108. package/dist/physics/vehicleObstacles.d.ts +44 -0
  109. package/dist/physics/vehicleObstacles.js +115 -0
  110. package/dist/random/rng.d.ts +4 -1
  111. package/dist/random/rng.js +4 -1
  112. package/dist/runtime/adapter.d.ts +13 -0
  113. package/dist/runtime/adapter.js +13 -0
  114. package/dist/runtime/context/combat.d.ts +51 -0
  115. package/dist/runtime/context/combat.js +131 -0
  116. package/dist/runtime/context/combatFx.js +15 -5
  117. package/dist/runtime/context/deathLoot.d.ts +30 -0
  118. package/dist/runtime/context/deathLoot.js +39 -0
  119. package/dist/runtime/context/player.d.ts +48 -0
  120. package/dist/runtime/context/player.js +125 -0
  121. package/dist/runtime/context/scene.d.ts +60 -0
  122. package/dist/runtime/context/scene.js +329 -0
  123. package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
  124. package/dist/runtime/context/snapshotCodecs.js +26 -0
  125. package/dist/runtime/descriptors/baseline.js +26 -3
  126. package/dist/runtime/descriptors/features.d.ts +5 -0
  127. package/dist/runtime/descriptors/features.js +20 -2
  128. package/dist/runtime/gameContext.d.ts +2 -428
  129. package/dist/runtime/gameContext.js +133 -500
  130. package/dist/runtime/gameContextTypes.d.ts +500 -0
  131. package/dist/runtime/gameContextTypes.js +1 -0
  132. package/dist/runtime/headlessRunner.d.ts +17 -2
  133. package/dist/runtime/headlessRunner.js +15 -0
  134. package/dist/runtime/hostPolicy.d.ts +51 -0
  135. package/dist/runtime/hostPolicy.js +68 -0
  136. package/dist/runtime/hostedGameRunner.d.ts +3 -1
  137. package/dist/runtime/hostedGameRunner.js +1 -0
  138. package/dist/runtime/hostedWorldSession.d.ts +3 -1
  139. package/dist/runtime/hostedWorldSession.js +1 -0
  140. package/dist/runtime/worldSnapshot.d.ts +13 -1
  141. package/dist/runtime/worldSnapshot.js +15 -3
  142. package/dist/scene/assetCatalog.d.ts +7 -0
  143. package/dist/scene/assetSpace.js +1 -3
  144. package/dist/scene/authoredTriggers.d.ts +50 -0
  145. package/dist/scene/authoredTriggers.js +114 -0
  146. package/dist/scene/builtinSceneKinds.js +8 -3
  147. package/dist/scene/colliders.d.ts +79 -0
  148. package/dist/scene/colliders.js +170 -0
  149. package/dist/scene/collisionMesh.d.ts +94 -0
  150. package/dist/scene/collisionMesh.js +699 -0
  151. package/dist/scene/companion.d.ts +147 -0
  152. package/dist/scene/companion.js +193 -0
  153. package/dist/scene/entityStats.d.ts +7 -9
  154. package/dist/scene/entityStats.js +19 -15
  155. package/dist/scene/sceneKinds.d.ts +16 -0
  156. package/dist/scene/sceneKinds.js +4 -0
  157. package/dist/scene/sceneRaycast.js +15 -0
  158. package/dist/stats/spawnLevelStats.d.ts +110 -0
  159. package/dist/stats/spawnLevelStats.js +140 -0
  160. package/dist/stats/statModifiers.d.ts +16 -0
  161. package/dist/stats/statModifiers.js +30 -1
  162. package/dist/stats/statPool.d.ts +70 -0
  163. package/dist/stats/statPool.js +62 -0
  164. package/dist/store/changeSignal.d.ts +3 -0
  165. package/dist/store/changeSignal.js +2 -0
  166. package/dist/store/observableKeyedStore.d.ts +2 -0
  167. package/dist/store/observableKeyedStore.js +1 -0
  168. package/dist/survival/decayMeter.js +1 -3
  169. package/dist/survival/regionHealth.js +1 -3
  170. package/dist/ui/hudLayout.d.ts +5 -0
  171. package/dist/ui/hudLayout.js +17 -1
  172. package/dist/ui/panelModel.d.ts +120 -0
  173. package/dist/ui/panelModel.js +168 -0
  174. package/dist/ui.d.ts +1 -0
  175. package/dist/ui.js +1 -0
  176. package/dist/world/authoredEntities.d.ts +29 -0
  177. package/dist/world/authoredEntities.js +29 -0
  178. package/dist/world/authoredObjects.d.ts +15 -1
  179. package/dist/world/authoredObjects.js +13 -1
  180. package/dist/world/authoredSpawn.d.ts +39 -0
  181. package/dist/world/authoredSpawn.js +37 -0
  182. package/dist/world/buildSockets.d.ts +152 -0
  183. package/dist/world/buildSockets.js +188 -0
  184. package/dist/world/buildingLots.d.ts +84 -0
  185. package/dist/world/buildingLots.js +119 -0
  186. package/dist/world/buildings.d.ts +6 -0
  187. package/dist/world/cityBlocks.d.ts +152 -0
  188. package/dist/world/cityBlocks.js +670 -0
  189. package/dist/world/cityContent.d.ts +77 -0
  190. package/dist/world/cityContent.js +261 -0
  191. package/dist/world/cityGenerator.d.ts +41 -0
  192. package/dist/world/cityGenerator.js +58 -0
  193. package/dist/world/cityGeometry.d.ts +114 -0
  194. package/dist/world/cityGeometry.js +573 -0
  195. package/dist/world/cityKind.d.ts +314 -0
  196. package/dist/world/cityKind.js +1578 -0
  197. package/dist/world/environmentSummary.js +57 -1
  198. package/dist/world/features.d.ts +53 -5
  199. package/dist/world/features.js +15 -3
  200. package/dist/world/grassKind.js +2 -2
  201. package/dist/world/markers.d.ts +50 -4
  202. package/dist/world/markers.js +49 -0
  203. package/dist/world/massing.js +1 -1
  204. package/dist/world/minimapBake.d.ts +78 -0
  205. package/dist/world/minimapBake.js +208 -0
  206. package/dist/world/minimapTrack.d.ts +17 -0
  207. package/dist/world/minimapTrack.js +22 -0
  208. package/dist/world/place.d.ts +166 -0
  209. package/dist/world/place.js +96 -0
  210. package/dist/world/placeAsset.js +3 -1
  211. package/dist/world/poleLineKind.d.ts +40 -0
  212. package/dist/world/poleLineKind.js +97 -0
  213. package/dist/world/polyline.js +9 -2
  214. package/dist/world/resourceNode.d.ts +109 -0
  215. package/dist/world/resourceNode.js +140 -0
  216. package/dist/world/roads.d.ts +19 -2
  217. package/dist/world/roads.js +60 -3
  218. package/dist/world/scatter.js +2 -20
  219. package/dist/world/streetGenerator.d.ts +159 -0
  220. package/dist/world/streetGenerator.js +684 -0
  221. package/dist/world/streets.d.ts +103 -1
  222. package/dist/world/streets.js +273 -0
  223. package/dist/world/terraform.js +58 -88
  224. package/dist/world/terrain.d.ts +65 -1
  225. package/dist/world/terrain.js +119 -2
  226. package/dist/world.d.ts +14 -5
  227. package/dist/world.js +12 -3
  228. package/package.json +1 -1
  229. package/skills/game-design/SKILL.md +28 -0
  230. package/skills/game-design/agents/openai.yaml +4 -0
  231. package/skills/game-design/references/application-playbook.md +170 -0
  232. package/skills/game-design/references/field-guide.md +288 -0
  233. package/skills/jgengine/SKILL.md +8 -4
  234. package/skills/jgengine/api.md +182 -81
  235. package/skills/jgengine/capabilities.md +104 -2
  236. package/skills/jgengine/recipes/minimal-game.md +88 -0
  237. package/skills/jgengine-combat/SKILL.md +19 -1
  238. package/skills/jgengine-combat/api.md +75 -2
  239. package/skills/jgengine-combat/capabilities.md +61 -1
  240. package/skills/jgengine-combat/recipes/portable-damage-effects.md +197 -0
  241. package/skills/jgengine-combat/recipes/portable-runtime-state.md +154 -0
  242. package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
  243. package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +156 -0
  244. package/skills/jgengine-combat/recipes/visible-attacks.md +81 -0
  245. package/skills/jgengine-combat/reference.md +33 -4
  246. package/skills/jgengine-gameplay/SKILL.md +8 -0
  247. package/skills/jgengine-gameplay/api.md +182 -26
  248. package/skills/jgengine-gameplay/capabilities.md +96 -2
  249. package/skills/jgengine-gameplay/recipes/portable-xp-leveling.md +168 -0
  250. package/skills/jgengine-gameplay/reference-systems.md +10 -5
  251. package/skills/jgengine-world/SKILL.md +13 -0
  252. package/skills/jgengine-world/api.md +453 -21
  253. package/skills/jgengine-world/capabilities.md +112 -2
  254. package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
  255. package/skills/jgengine-world/reference.md +43 -4
  256. package/skills/jgengine/reference-cartridge.md +0 -72
@@ -0,0 +1,94 @@
1
+ import type { Aabb, Vec2 } from "../world/geometry.js";
2
+ import type { EditorDirective, EditorDirectiveKind, EditorDocument, EditorPopulationDirective, EditorScatterDirective } from "./types.js";
3
+ /**
4
+ * The deterministic materializer for scene {@link EditorDirective}s — the bake side of the
5
+ * bake/patch seam in #985. A directive is one authored line ("scatter rocks across the north
6
+ * ridge"); this expands it into many placements with **stable per-instance ids** (`<directiveId>#
7
+ * <index>`) so a sparse overlay can hand-tweak or delete individual instances by id without every
8
+ * re-materialize orphaning those edits. Fully deterministic: same directive + seed + region ⇒ the
9
+ * same field, every time (safe for verify/CI). Built on the existing `scatter` primitive.
10
+ */
11
+ /** One materialized placement from a directive — grounded XZ, deterministic scale/yaw, stable id. */
12
+ export interface MaterializedInstance {
13
+ /** Stable id `<directiveId>#<index>` — the key an overlay patches or deletes. */
14
+ id: string;
15
+ /** The directive that minted this instance. */
16
+ directiveId: string;
17
+ kind: EditorDirectiveKind;
18
+ /** What to place: a `scatter` directive's catalog asset, or a `population` directive's species id. */
19
+ asset: string;
20
+ x: number;
21
+ z: number;
22
+ /** Vertical offset in meters (0 unless an overlay sets it); grounding on terrain stays the caller's job. */
23
+ y: number;
24
+ rotationY: number;
25
+ scale: number;
26
+ }
27
+ /** A resolved directive footprint: the XZ bounds to scatter within, plus an optional clip polygon. */
28
+ export interface DirectiveFootprint {
29
+ area: Aabb;
30
+ polygon?: Vec2[];
31
+ }
32
+ /**
33
+ * Resolves the footprint a directive fills: an explicit `area`, else the footprint of the named
34
+ * `region` — a scatter/closed path (as a clip polygon) or a volume (its XZ AABB). Null when neither
35
+ * is present or the region id does not resolve.
36
+ * @capability world-directives resolve a directive's region footprint from the scene document
37
+ */
38
+ export declare function resolveDirectiveFootprint(doc: EditorDocument, directive: EditorDirective): DirectiveFootprint | null;
39
+ /**
40
+ * Materializes a scatter directive into deterministic catalog-prop placements across its footprint,
41
+ * clipped to the region polygon when one is present. Per-instance scale and yaw derive from the
42
+ * directive id + seed + point index, so ids are stable across re-materializes.
43
+ * @capability world-directives materialize a scatter directive into placements
44
+ */
45
+ export declare function materializeScatterDirective(directive: EditorScatterDirective, footprint: DirectiveFootprint): MaterializedInstance[];
46
+ /**
47
+ * Materializes a population directive into deterministic mob spawn placements: at most `cap` of each
48
+ * weighted species, distributed across the footprint. Each placement carries a stable id and its
49
+ * species id in `asset`, ready to feed `ctx.scene.entity.spawn` / `ai/populationDirector`.
50
+ * @capability world-directives materialize a population directive into spawns
51
+ */
52
+ export declare function materializePopulationDirective(directive: EditorPopulationDirective, footprint: DirectiveFootprint): MaterializedInstance[];
53
+ /**
54
+ * Materializes one directive against a document, resolving its footprint first. Returns an empty
55
+ * list when the region does not resolve.
56
+ * @capability world-directives materialize one scene directive into instances
57
+ */
58
+ export declare function materializeDirective(doc: EditorDocument, directive: EditorDirective): MaterializedInstance[];
59
+ /** A per-instance hand-tweak over a materialized directive, keyed by the minted instance id. */
60
+ export interface DirectiveInstancePatch {
61
+ /** Override the placement's XZ position. */
62
+ position?: {
63
+ x: number;
64
+ z: number;
65
+ };
66
+ /** Override the vertical offset. */
67
+ y?: number;
68
+ scale?: number;
69
+ rotationY?: number;
70
+ /** Remove this instance from the materialized set. */
71
+ deleted?: boolean;
72
+ }
73
+ /**
74
+ * A sparse overlay over one directive's materialized instances — the "nudge *that one* rock" answer.
75
+ * Hand-edits are keyed by the stable instance id the materializer mints, so they survive re-bakes.
76
+ */
77
+ export interface EditorDirectiveOverlay {
78
+ /** The directive id whose instances this overlay patches. */
79
+ over: string;
80
+ patches: Record<string, DirectiveInstancePatch>;
81
+ }
82
+ /**
83
+ * Applies a directive overlay to its materialized instances: patched fields override, `deleted`
84
+ * drops the instance. Instances with no patch pass through untouched.
85
+ * @capability world-directives apply a sparse overlay over materialized directive instances
86
+ */
87
+ export declare function applyDirectiveOverlay(instances: readonly MaterializedInstance[], overlay: EditorDirectiveOverlay): MaterializedInstance[];
88
+ /**
89
+ * Materializes every directive on a document into one flat instance list, applying each directive's
90
+ * overlay (matched by `over` === directive id) as it goes. The bulk-content counterpart to
91
+ * `resolveAuthoredObjects` / `resolveScatter`: games and headless tests read the same list.
92
+ * @capability world-directives materialize all scene directives with their overlays
93
+ */
94
+ export declare function materializeDirectives(doc: EditorDocument, overlays?: readonly EditorDirectiveOverlay[]): MaterializedInstance[];
@@ -0,0 +1,206 @@
1
+ import { scatter } from "../world/scatter.js";
2
+ import { pointInPolygon, polygonBounds } from "../world/scatterRegion.js";
3
+ function hash(text) {
4
+ let h = 2166136261 >>> 0;
5
+ for (let i = 0; i < text.length; i += 1) {
6
+ h ^= text.charCodeAt(i);
7
+ h = Math.imul(h, 16777619);
8
+ }
9
+ return (h >>> 0) / 4294967296;
10
+ }
11
+ /**
12
+ * Resolves the footprint a directive fills: an explicit `area`, else the footprint of the named
13
+ * `region` — a scatter/closed path (as a clip polygon) or a volume (its XZ AABB). Null when neither
14
+ * is present or the region id does not resolve.
15
+ * @capability world-directives resolve a directive's region footprint from the scene document
16
+ */
17
+ export function resolveDirectiveFootprint(doc, directive) {
18
+ if (directive.area !== undefined) {
19
+ const { min, max } = directive.area;
20
+ return { area: { minX: min[0], minZ: min[1], maxX: max[0], maxZ: max[1] } };
21
+ }
22
+ if (directive.region === undefined)
23
+ return null;
24
+ const path = doc.paths.find((p) => p.id === directive.region);
25
+ if (path !== undefined && path.points.length >= 3) {
26
+ const polygon = path.points.map((point) => [point.x, point.z]);
27
+ const area = polygonBounds(polygon);
28
+ if (area === null)
29
+ return null;
30
+ return { area, polygon };
31
+ }
32
+ const volume = doc.volumes.find((v) => v.id === directive.region);
33
+ if (volume !== undefined) {
34
+ const half = volume.halfExtents !== undefined
35
+ ? { x: volume.halfExtents.x, z: volume.halfExtents.z }
36
+ : volume.radius !== undefined
37
+ ? { x: volume.radius, z: volume.radius }
38
+ : null;
39
+ if (half === null)
40
+ return null;
41
+ return {
42
+ area: {
43
+ minX: volume.center.x - half.x,
44
+ minZ: volume.center.z - half.z,
45
+ maxX: volume.center.x + half.x,
46
+ maxZ: volume.center.z + half.z,
47
+ },
48
+ };
49
+ }
50
+ return null;
51
+ }
52
+ function scatterSeed(directive) {
53
+ return `${directive.id}:${directive.seed ?? directive.id}`;
54
+ }
55
+ /**
56
+ * Materializes a scatter directive into deterministic catalog-prop placements across its footprint,
57
+ * clipped to the region polygon when one is present. Per-instance scale and yaw derive from the
58
+ * directive id + seed + point index, so ids are stable across re-materializes.
59
+ * @capability world-directives materialize a scatter directive into placements
60
+ */
61
+ export function materializeScatterDirective(directive, footprint) {
62
+ if (directive.density <= 0)
63
+ return [];
64
+ const seed = scatterSeed(directive);
65
+ const points = scatter({
66
+ area: footprint.area,
67
+ density: directive.density,
68
+ minDistance: directive.minSpacing ?? 0,
69
+ jitter: directive.jitter ?? 1,
70
+ seed,
71
+ });
72
+ const minScale = directive.minScale ?? 1;
73
+ const maxScale = directive.maxScale ?? 1;
74
+ const minYaw = directive.minYaw ?? 0;
75
+ const maxYaw = directive.maxYaw ?? Math.PI * 2;
76
+ const out = [];
77
+ for (const point of points) {
78
+ if (footprint.polygon !== undefined && !pointInPolygon([point.x, point.z], footprint.polygon))
79
+ continue;
80
+ const scaleRoll = hash(`${seed}:scale:${point.index}`);
81
+ const yawRoll = hash(`${seed}:yaw:${point.index}`);
82
+ out.push({
83
+ id: `${directive.id}#${point.index}`,
84
+ directiveId: directive.id,
85
+ kind: "scatter",
86
+ asset: directive.asset,
87
+ x: point.x,
88
+ z: point.z,
89
+ y: 0,
90
+ rotationY: minYaw + (maxYaw - minYaw) * yawRoll,
91
+ scale: minScale + (maxScale - minScale) * scaleRoll,
92
+ });
93
+ }
94
+ return out;
95
+ }
96
+ function pickSpecies(available, roll) {
97
+ const total = available.reduce((sum, s) => sum + Math.max(0, s.weight ?? 1), 0);
98
+ if (total <= 0)
99
+ return available[0];
100
+ let acc = roll * total;
101
+ for (const species of available) {
102
+ acc -= Math.max(0, species.weight ?? 1);
103
+ if (acc <= 0)
104
+ return species;
105
+ }
106
+ return available[available.length - 1];
107
+ }
108
+ /**
109
+ * Materializes a population directive into deterministic mob spawn placements: at most `cap` of each
110
+ * weighted species, distributed across the footprint. Each placement carries a stable id and its
111
+ * species id in `asset`, ready to feed `ctx.scene.entity.spawn` / `ai/populationDirector`.
112
+ * @capability world-directives materialize a population directive into spawns
113
+ */
114
+ export function materializePopulationDirective(directive, footprint) {
115
+ const remaining = new Map();
116
+ for (const species of directive.species) {
117
+ remaining.set(species.id, Math.max(0, (remaining.get(species.id) ?? 0) + species.cap));
118
+ }
119
+ const total = [...remaining.values()].reduce((sum, cap) => sum + cap, 0);
120
+ if (total <= 0)
121
+ return [];
122
+ const seed = scatterSeed(directive);
123
+ const points = scatter({ area: footprint.area, count: total, minDistance: 0, seed });
124
+ const out = [];
125
+ for (const point of points) {
126
+ if (footprint.polygon !== undefined && !pointInPolygon([point.x, point.z], footprint.polygon))
127
+ continue;
128
+ const available = directive.species.filter((s) => (remaining.get(s.id) ?? 0) > 0);
129
+ if (available.length === 0)
130
+ break;
131
+ const picked = pickSpecies(available, hash(`${seed}:species:${point.index}`));
132
+ remaining.set(picked.id, (remaining.get(picked.id) ?? 0) - 1);
133
+ out.push({
134
+ id: `${directive.id}#${point.index}`,
135
+ directiveId: directive.id,
136
+ kind: "population",
137
+ asset: picked.id,
138
+ x: point.x,
139
+ z: point.z,
140
+ y: 0,
141
+ rotationY: hash(`${seed}:yaw:${point.index}`) * Math.PI * 2,
142
+ scale: 1,
143
+ });
144
+ }
145
+ return out;
146
+ }
147
+ /**
148
+ * Materializes one directive against a document, resolving its footprint first. Returns an empty
149
+ * list when the region does not resolve.
150
+ * @capability world-directives materialize one scene directive into instances
151
+ */
152
+ export function materializeDirective(doc, directive) {
153
+ const footprint = resolveDirectiveFootprint(doc, directive);
154
+ if (footprint === null)
155
+ return [];
156
+ return directive.kind === "scatter"
157
+ ? materializeScatterDirective(directive, footprint)
158
+ : materializePopulationDirective(directive, footprint);
159
+ }
160
+ /**
161
+ * Applies a directive overlay to its materialized instances: patched fields override, `deleted`
162
+ * drops the instance. Instances with no patch pass through untouched.
163
+ * @capability world-directives apply a sparse overlay over materialized directive instances
164
+ */
165
+ export function applyDirectiveOverlay(instances, overlay) {
166
+ const out = [];
167
+ for (const instance of instances) {
168
+ const patch = overlay.patches[instance.id];
169
+ if (patch === undefined) {
170
+ out.push(instance);
171
+ continue;
172
+ }
173
+ if (patch.deleted === true)
174
+ continue;
175
+ out.push({
176
+ ...instance,
177
+ x: patch.position?.x ?? instance.x,
178
+ z: patch.position?.z ?? instance.z,
179
+ y: patch.y ?? instance.y,
180
+ scale: patch.scale ?? instance.scale,
181
+ rotationY: patch.rotationY ?? instance.rotationY,
182
+ });
183
+ }
184
+ return out;
185
+ }
186
+ /**
187
+ * Materializes every directive on a document into one flat instance list, applying each directive's
188
+ * overlay (matched by `over` === directive id) as it goes. The bulk-content counterpart to
189
+ * `resolveAuthoredObjects` / `resolveScatter`: games and headless tests read the same list.
190
+ * @capability world-directives materialize all scene directives with their overlays
191
+ */
192
+ export function materializeDirectives(doc, overlays) {
193
+ const directives = doc.directives ?? [];
194
+ if (directives.length === 0)
195
+ return [];
196
+ const overlayByDirective = new Map((overlays ?? []).map((overlay) => [overlay.over, overlay]));
197
+ const out = [];
198
+ for (const directive of directives) {
199
+ let instances = materializeDirective(doc, directive);
200
+ const overlay = overlayByDirective.get(directive.id);
201
+ if (overlay !== undefined)
202
+ instances = applyDirectiveOverlay(instances, overlay);
203
+ out.push(...instances);
204
+ }
205
+ return out;
206
+ }
@@ -1,5 +1,5 @@
1
1
  import { type EditorGridLayer } from "./grid.js";
2
- import type { EditorCatalogData, EditorCatalogDefinition, EditorCatalogEntry, EditorCollection, EditorDocument, EditorFragmentContent, EditorLayersInput, EditorMarker, EditorNote, EditorPath, EditorPrefab, EditorTerrain, EditorVolume } from "./types.js";
2
+ import type { EditorCatalogData, EditorCatalogDefinition, EditorCatalogEntry, EditorCollection, EditorDirective, EditorDocument, EditorEnvironment, EditorFragmentContent, EditorLayersInput, EditorMarker, EditorMinimapBake, EditorNote, EditorPath, EditorPrefab, EditorTerrain, EditorVolume } from "./types.js";
3
3
  /** Spread-ready non-placeable fields preserved across document rebuilds. @internal */
4
4
  export declare function editorDocumentExtras(doc: EditorDocument): {
5
5
  prefabs: EditorPrefab[];
@@ -8,6 +8,9 @@ export declare function editorDocumentExtras(doc: EditorDocument): {
8
8
  grids?: EditorGridLayer[];
9
9
  terrain?: EditorTerrain;
10
10
  ui?: EditorDocument["ui"];
11
+ directives?: EditorDirective[];
12
+ minimap?: EditorMinimapBake;
13
+ environment?: EditorEnvironment;
11
14
  };
12
15
  /** Builds a fresh, empty editor document to start authoring a scene from scratch.
13
16
  * @internal
@@ -71,10 +74,29 @@ export declare function findEditorCatalogEntry(doc: EditorDocument, catalogId: s
71
74
  * @capability editor-catalogs Seed scene-document catalogs from game-exported schemas.
72
75
  */
73
76
  export declare function seedEditorCatalogs(doc: EditorDocument, definitions: readonly EditorCatalogDefinition[]): EditorDocument;
74
- /** True when an object id is a member of any locked collection — blocks move/delete on it.
77
+ /**
78
+ * True when an object carries a per-object `locked` flag (hierarchy lock toggle).
79
+ * Collection membership is checked separately by {@link isEditorObjectLocked}.
80
+ * @internal
81
+ */
82
+ export declare function isEditorObjectSelfLocked(doc: EditorDocument, id: string): boolean;
83
+ /**
84
+ * True when an object is a member of any locked collection (production group lock).
85
+ * @internal
86
+ */
87
+ export declare function isEditorObjectCollectionLocked(doc: EditorDocument, id: string): boolean;
88
+ /**
89
+ * True when an object id is locked for edit — either its own `locked` flag or membership in a
90
+ * locked collection. Blocks move/delete via session mutations.
75
91
  * @internal
76
92
  */
77
93
  export declare function isEditorObjectLocked(doc: EditorDocument, id: string): boolean;
94
+ /**
95
+ * True when an object carries `hidden: true` — omitted from editor viewport overlays and picking
96
+ * while remaining in the hierarchy and document.
97
+ * @internal
98
+ */
99
+ export declare function isEditorObjectHidden(doc: EditorDocument, id: string): boolean;
78
100
  /**
79
101
  * Extracts the selected ids into a prefab fragment centered on their own bounds centroid, so the
80
102
  * same prefab reinserts consistently regardless of where in the scene (or which game) it lands.
@@ -85,6 +107,19 @@ export declare function createPrefabFragment(doc: EditorDocument, ids: readonly
85
107
  * @internal
86
108
  */
87
109
  export declare function findEditorMarker(doc: EditorDocument, id: string): EditorMarker | undefined;
110
+ /** Looks up a marker by id and throws when it is absent — for scene contracts where a
111
+ * missing marker is an authoring error to surface loudly, not a runtime branch to handle.
112
+ * @internal
113
+ */
114
+ export declare function requireEditorMarker(doc: EditorDocument, id: string): EditorMarker;
115
+ /** A marker's world position as an `[x, y, z]` tuple — the shared shape gameplay tables read.
116
+ * @internal
117
+ */
118
+ export declare function editorMarkerPosition(marker: EditorMarker): readonly [x: number, y: number, z: number];
119
+ /** A marker's ground position as an `[x, z]` tuple, dropping the vertical axis.
120
+ * @internal
121
+ */
122
+ export declare function editorMarkerXZ(marker: EditorMarker): readonly [x: number, z: number];
88
123
  /** Looks up a volume by id in an editor document.
89
124
  * @internal
90
125
  */