@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,101 @@
1
+ import { parseParams } from "../scene/sceneKinds.js";
2
+ /**
3
+ * Well-known catalog id for authored entity definitions. A `mob`/`boss` marker's `catalogId`
4
+ * references an entry (row) in this catalog, and {@link entityEntryFromCatalog} turns that row into
5
+ * the runtime {@link GameContextEntityEntry} a spawn consumes — so an entity's stats and speed are
6
+ * authored in the editor Data tab, not hardcoded in game TS.
7
+ */
8
+ export const ENTITY_CATALOG_ID = "entities";
9
+ const ENTITY_ROLES = ["player", "enemy", "hostile", "npc", "vehicle"];
10
+ /**
11
+ * The default {@link ParamSchema} for an authored entity definition — role, health, movement speed,
12
+ * and visual scale. Drives the editor Data-tab inspector for the `entities` catalog and is the shape
13
+ * {@link entityEntryFromCatalog} reads. A game exports it as an `editorCatalogs` schema; the scaffold
14
+ * ships a starter one so the tab is never empty.
15
+ * @capability editor-entities Author entity stats/movement per catalog row instead of hardcoding TS.
16
+ */
17
+ export const entityDefinitionSchema = {
18
+ fields: [
19
+ {
20
+ key: "role",
21
+ type: "select",
22
+ label: "Role",
23
+ default: "enemy",
24
+ options: ENTITY_ROLES.map((role) => ({ value: role })),
25
+ },
26
+ { key: "maxHealth", type: "number", label: "Max health", default: 20, min: 1, max: 100000, step: 1 },
27
+ { key: "walkSpeed", type: "range", label: "Walk speed", default: 3, min: 0, max: 20, step: 0.1, unit: "m/s" },
28
+ { key: "scale", type: "range", label: "Scale", default: 1, min: 0.1, max: 10, step: 0.1 },
29
+ ],
30
+ };
31
+ function entityCatalogDefinition(definitions) {
32
+ return definitions?.find((definition) => definition.id === ENTITY_CATALOG_ID);
33
+ }
34
+ /**
35
+ * Resolves the merged `meta` for one entity row: game-exported defaults (from `definitions`, if
36
+ * given) overlaid by the values persisted on the scene document. Returns `undefined` when neither
37
+ * source carries the row, so callers can distinguish "no such entity" from "default entity".
38
+ */
39
+ function mergedEntityMeta(document, entityId, definitions) {
40
+ const defaultEntry = entityCatalogDefinition(definitions)?.entries.find((entry) => entry.id === entityId);
41
+ const authored = document.catalogs
42
+ .find((catalog) => catalog.id === ENTITY_CATALOG_ID)
43
+ ?.entries.find((entry) => entry.id === entityId);
44
+ if (defaultEntry === undefined && authored === undefined)
45
+ return undefined;
46
+ return { ...defaultEntry?.meta, ...authored?.meta };
47
+ }
48
+ /**
49
+ * Turns an authored entity row into the runtime {@link GameContextEntityEntry} a spawn consumes:
50
+ * looks up `entityId` in the document's {@link ENTITY_CATALOG_ID} catalog (falling back to the
51
+ * game-exported `definitions` defaults), parses it against {@link entityDefinitionSchema}, and maps
52
+ * the fields onto stats/movement/role. Returns `null` when no row defines the entity, so the default
53
+ * `content.ts` path can chain other resolvers. The single seam that makes "place a mob, tune its
54
+ * stats/speed in the editor, save — it spawns with those stats" work with zero game code.
55
+ * @capability editor-entities Consume authored entity definitions from the scene document at runtime.
56
+ */
57
+ export function entityEntryFromCatalog(document, entityId, definitions) {
58
+ const meta = mergedEntityMeta(document, entityId, definitions);
59
+ if (meta === undefined)
60
+ return null;
61
+ // Prefer a document-carried schema for the entities catalog (an editor-authored schema) over the
62
+ // hardcoded default, so a game that re-authored the entity fields in the editor drives the parse.
63
+ const schema = document.catalogs.find((catalog) => catalog.id === ENTITY_CATALOG_ID)?.schema ?? entityDefinitionSchema;
64
+ const params = parseParams(schema, meta);
65
+ const role = params.role;
66
+ const maxHealth = params.maxHealth;
67
+ const walkSpeed = params.walkSpeed;
68
+ const scale = params.scale;
69
+ const entry = {
70
+ role,
71
+ stats: { health: { max: maxHealth, min: 0 } },
72
+ receive: { damage: { order: ["health"] } },
73
+ movement: { walkSpeed },
74
+ };
75
+ if (scale !== 1)
76
+ entry.scale = scale;
77
+ return entry;
78
+ }
79
+ /**
80
+ * Merges game-exported catalog definitions with editor-authored ones carried on the scene document.
81
+ * A document catalog that carries its own `schema` and has no matching game definition is synthesized
82
+ * into a full {@link EditorCatalogDefinition} (label falls back to the id) so the Data panel, the
83
+ * `list_catalogs` RPC, and the count all see it. A game definition always wins on id collision — its
84
+ * schema lives in code and is authoritative. Document catalogs without a schema (plain value bags for
85
+ * a game-declared catalog) are ignored here; they are addressed through their game definition.
86
+ * @capability editor-catalogs Merge game-exported and editor-authored catalogs into one addressable set.
87
+ */
88
+ export function resolveCatalogDefinitions(document, gameDefinitions) {
89
+ const byId = new Map(gameDefinitions.map((definition) => [definition.id, definition]));
90
+ for (const catalog of document.catalogs) {
91
+ if (catalog.schema === undefined || byId.has(catalog.id))
92
+ continue;
93
+ byId.set(catalog.id, {
94
+ id: catalog.id,
95
+ label: catalog.label ?? catalog.id,
96
+ schema: catalog.schema,
97
+ entries: catalog.entries,
98
+ });
99
+ }
100
+ return [...byId.values()];
101
+ }
@@ -0,0 +1,74 @@
1
+ import type { SkyEnvironmentConfig, WorldBounds } from "../world/features.js";
2
+ import type { AvoidZone } from "../world/geometry.js";
3
+ import { type ClearanceOptions } from "../world/scatterRegion.js";
4
+ import type { TerraformSnapshot } from "../world/terraform.js";
5
+ import type { EditorDocument, EditorEnvironment } from "./types.js";
6
+ /**
7
+ * World-authoring convergence (#1018, epic #1006 Phase 4): derive the **coordinate/placement**
8
+ * content of an `environment()` world from the scene document, so a game with an authored document
9
+ * needs no coordinate arrays in `world.ts` — the remaining `world.ts` surface is engine tuning
10
+ * (noise, materials, sky preset, physics). Terrain footprint follows the authored objects, ground
11
+ * clearings come from authored spawns/POIs (`clearanceZonesFrom`), and the sculpt snapshot is the
12
+ * document's own terrain overlay. Pure and serializable; consumers pass the result straight to
13
+ * `environment({ terrain: terrain({ bounds }), clearings, sculpt })`.
14
+ */
15
+ /** Options controlling how {@link environmentContentFromDocument} sizes and clears the world. */
16
+ export interface EnvironmentContentOptions extends ClearanceOptions {
17
+ /** Padding (m) added around the authored objects' extent when sizing the terrain. Default 24. */
18
+ padding?: number;
19
+ /** Minimum terrain footprint so a sparse document still yields a walkable world. Default 64×64. */
20
+ minBounds?: WorldBounds;
21
+ }
22
+ /** The coordinate/placement content of a world, derived from its scene document. */
23
+ export interface EnvironmentContent {
24
+ /** Terrain footprint (origin-centered) sized to cover every authored object plus padding. */
25
+ bounds: WorldBounds;
26
+ /** Ground-flatten discs under authored spawns/plots/POIs — feed `environment({ clearings })`. */
27
+ clearings: AvoidZone[];
28
+ /** The document's authored sculpt snapshot, when it has one — feed `environment({ sculpt })`. */
29
+ sculpt?: TerraformSnapshot;
30
+ /**
31
+ * Scene-document sky/fog/lighting (#1110), when authored. Feed into `environment({ sky: sky(sky) })`
32
+ * so the lighting workspace owns the look without hardcoding coordinates in world.ts.
33
+ */
34
+ sky?: SkyEnvironmentConfig;
35
+ }
36
+ /**
37
+ * Maps the scene document's serializable environment bag onto the runtime sky config consumed by
38
+ * `sky()` / `environment({ sky })`. Returns `undefined` when the document has no environment field
39
+ * so callers can fall back to a world.ts default without inventing values.
40
+ * @capability editor-environment project document lighting onto the runtime sky descriptor
41
+ */
42
+ export declare function skyFromDocument(doc: EditorDocument): SkyEnvironmentConfig | undefined;
43
+ /**
44
+ * Projects a validated {@link EditorEnvironment} bag onto {@link SkyEnvironmentConfig}.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function skyConfigFromEnvironment(env: EditorEnvironment): SkyEnvironmentConfig;
49
+ /**
50
+ * The origin-centered terrain footprint that covers every authored object in a document, padded and
51
+ * floored to `minBounds`. An empty document yields exactly `minBounds`. Because `environment()`
52
+ * terrain is centered on the origin, the footprint is sized symmetrically to the farthest object on
53
+ * each axis, so no authored object falls off the ground.
54
+ * @capability world-convergence size terrain to the authored document instead of a hardcoded bounds
55
+ */
56
+ export declare function terrainBoundsFromDocument(doc: EditorDocument, options?: EnvironmentContentOptions): WorldBounds;
57
+ /**
58
+ * Derives a terrain-depression sculpt snapshot from the document's authored `water` volumes: each
59
+ * water box carves a lake bed that ramps from the shoreline down to below the box's bottom face, so
60
+ * an editor-authored pond/lake sits *in* the ground instead of floating as a sheet on top of it (and
61
+ * ground-sampled vegetation sinks below the surface rather than growing through it). Returns
62
+ * `undefined` when the document has no water volumes.
63
+ * @capability world-convergence carve lake beds under authored water volumes
64
+ */
65
+ export declare function lakebedFromWaterVolumes(doc: EditorDocument): TerraformSnapshot | undefined;
66
+ /**
67
+ * Derives the coordinate/placement content of an `environment()` world from its scene document:
68
+ * terrain footprint (via {@link terrainBoundsFromDocument}), ground clearings under authored
69
+ * spawns/POIs (via `clearanceZonesFrom`), and the document's sculpt snapshot. This is the seam that
70
+ * lets a game author its world footprint and flatten regions in the editor instead of hardcoding
71
+ * them — the `world.ts` that consumes it carries only engine tuning.
72
+ * @capability world-convergence derive environment coordinate content from the scene document
73
+ */
74
+ export declare function environmentContentFromDocument(doc: EditorDocument, options?: EnvironmentContentOptions): EnvironmentContent;
@@ -0,0 +1,169 @@
1
+ import { clearanceZonesFrom, DEFAULT_CLEARANCE_KINDS } from "../world/scatterRegion.js";
2
+ import { editorDocumentBounds } from "./document.js";
3
+ /**
4
+ * Maps the scene document's serializable environment bag onto the runtime sky config consumed by
5
+ * `sky()` / `environment({ sky })`. Returns `undefined` when the document has no environment field
6
+ * so callers can fall back to a world.ts default without inventing values.
7
+ * @capability editor-environment project document lighting onto the runtime sky descriptor
8
+ */
9
+ export function skyFromDocument(doc) {
10
+ const env = doc.environment;
11
+ if (env === undefined)
12
+ return undefined;
13
+ return skyConfigFromEnvironment(env);
14
+ }
15
+ /**
16
+ * Projects a validated {@link EditorEnvironment} bag onto {@link SkyEnvironmentConfig}.
17
+ *
18
+ * @internal
19
+ */
20
+ export function skyConfigFromEnvironment(env) {
21
+ return {
22
+ ...(env.preset === undefined ? {} : { preset: env.preset }),
23
+ ...(env.timeOfDay === undefined ? {} : { timeOfDay: env.timeOfDay }),
24
+ ...(env.horizonColor === undefined ? {} : { horizonColor: env.horizonColor }),
25
+ ...(env.zenithColor === undefined ? {} : { zenithColor: env.zenithColor }),
26
+ ...(env.sunIntensity === undefined ? {} : { sunIntensity: env.sunIntensity }),
27
+ ...(env.ambientIntensity === undefined ? {} : { ambientIntensity: env.ambientIntensity }),
28
+ ...(env.fog === undefined ? {} : { fog: { ...env.fog } }),
29
+ };
30
+ }
31
+ /**
32
+ * The origin-centered terrain footprint that covers every authored object in a document, padded and
33
+ * floored to `minBounds`. An empty document yields exactly `minBounds`. Because `environment()`
34
+ * terrain is centered on the origin, the footprint is sized symmetrically to the farthest object on
35
+ * each axis, so no authored object falls off the ground.
36
+ * @capability world-convergence size terrain to the authored document instead of a hardcoded bounds
37
+ */
38
+ export function terrainBoundsFromDocument(doc, options = {}) {
39
+ const padding = options.padding ?? 24;
40
+ const min = options.minBounds ?? { w: 64, d: 64 };
41
+ const bounds = editorDocumentBounds(doc);
42
+ if (bounds === null)
43
+ return { w: min.w, d: min.d };
44
+ const halfX = Math.max(Math.abs(bounds.min.x), Math.abs(bounds.max.x));
45
+ const halfZ = Math.max(Math.abs(bounds.min.z), Math.abs(bounds.max.z));
46
+ return {
47
+ w: Math.max(min.w, halfX * 2 + padding * 2),
48
+ d: Math.max(min.d, halfZ * 2 + padding * 2),
49
+ };
50
+ }
51
+ const LAKEBED_CELL = 1.5;
52
+ /** Meters the bed drops below the water box's bottom face at full depth. */
53
+ const LAKEBED_UNDERCUT = 1.2;
54
+ /** Meters from the shoreline over which the bed ramps to full depth. */
55
+ const LAKEBED_SHORE_RAMP = 4;
56
+ /** Wavelength (m) of the seeded shore-ramp modulation that keeps the waterline organic. */
57
+ const LAKEBED_SHORE_NOISE_SCALE = 5.5;
58
+ /** Deterministic 2D value noise in [0,1] — the shoreline wobble must be stable across runs. */
59
+ function lakebedShoreNoise(x, z) {
60
+ const lattice = (ix, iz) => {
61
+ let h = (Math.imul(ix, 374761393) + Math.imul(iz, 668265263)) ^ 0x5bf03635;
62
+ h = Math.imul(h ^ (h >>> 13), 1274126177);
63
+ return ((h ^ (h >>> 16)) >>> 0) / 4294967296;
64
+ };
65
+ const ix = Math.floor(x);
66
+ const iz = Math.floor(z);
67
+ const fx = x - ix;
68
+ const fz = z - iz;
69
+ const ux = fx * fx * (3 - 2 * fx);
70
+ const uz = fz * fz * (3 - 2 * fz);
71
+ const a = lattice(ix, iz);
72
+ const b = lattice(ix + 1, iz);
73
+ const c = lattice(ix, iz + 1);
74
+ const d = lattice(ix + 1, iz + 1);
75
+ return a + (b - a) * ux + (c - a) * uz + (a - b - c + d) * ux * uz;
76
+ }
77
+ /**
78
+ * Derives a terrain-depression sculpt snapshot from the document's authored `water` volumes: each
79
+ * water box carves a lake bed that ramps from the shoreline down to below the box's bottom face, so
80
+ * an editor-authored pond/lake sits *in* the ground instead of floating as a sheet on top of it (and
81
+ * ground-sampled vegetation sinks below the surface rather than growing through it). Returns
82
+ * `undefined` when the document has no water volumes.
83
+ * @capability world-convergence carve lake beds under authored water volumes
84
+ */
85
+ export function lakebedFromWaterVolumes(doc) {
86
+ const waters = doc.volumes.filter((volume) => volume.kind === "water" && volume.halfExtents !== undefined);
87
+ if (waters.length === 0)
88
+ return undefined;
89
+ let minX = Infinity;
90
+ let minZ = Infinity;
91
+ let maxX = -Infinity;
92
+ let maxZ = -Infinity;
93
+ for (const water of waters) {
94
+ const he = water.halfExtents;
95
+ minX = Math.min(minX, water.center.x - he.x - LAKEBED_SHORE_RAMP);
96
+ minZ = Math.min(minZ, water.center.z - he.z - LAKEBED_SHORE_RAMP);
97
+ maxX = Math.max(maxX, water.center.x + he.x + LAKEBED_SHORE_RAMP);
98
+ maxZ = Math.max(maxZ, water.center.z + he.z + LAKEBED_SHORE_RAMP);
99
+ }
100
+ const cols = Math.max(1, Math.ceil((maxX - minX) / LAKEBED_CELL));
101
+ const rows = Math.max(1, Math.ceil((maxZ - minZ) / LAKEBED_CELL));
102
+ const vertsX = cols + 1;
103
+ const vertsZ = rows + 1;
104
+ const offsets = new Array(vertsX * vertsZ).fill(0);
105
+ for (let iz = 0; iz < vertsZ; iz += 1) {
106
+ for (let ix = 0; ix < vertsX; ix += 1) {
107
+ const x = minX + (ix / cols) * (maxX - minX);
108
+ const z = minZ + (iz / rows) * (maxZ - minZ);
109
+ let offset = 0;
110
+ for (const water of waters) {
111
+ const he = water.halfExtents;
112
+ // Rounded-rectangle signed distance on XZ (negative inside): the carve — and with it the
113
+ // waterline — rounds the box corners instead of stamping a sharp rectangle in the ground.
114
+ const rr = Math.min(he.x, he.z) * 0.3;
115
+ const ax = Math.abs(x - water.center.x) - he.x + rr;
116
+ const az = Math.abs(z - water.center.z) - he.z + rr;
117
+ const raw = Math.hypot(Math.max(ax, 0), Math.max(az, 0)) + Math.min(Math.max(ax, az), 0) - rr;
118
+ if (raw >= 0)
119
+ continue;
120
+ // Seeded wobble wanders the shoreline up to ±0.8 m, ramped in from the box edge so the
121
+ // carve never escapes the authored footprint.
122
+ const wobble = (lakebedShoreNoise(x / LAKEBED_SHORE_NOISE_SCALE, z / LAKEBED_SHORE_NOISE_SCALE) - 0.5) * 1.6;
123
+ const dist = raw + wobble * Math.min(1, -raw / 1.5);
124
+ if (dist >= 0)
125
+ continue;
126
+ const depth = he.y * 2 + LAKEBED_UNDERCUT;
127
+ const t = Math.min(1, -dist / LAKEBED_SHORE_RAMP);
128
+ // Smoothstep the shore ramp so the bed curves like a basin, not a funnel.
129
+ const w = t * t * (3 - 2 * t);
130
+ offset = Math.min(offset, -depth * w);
131
+ }
132
+ offsets[iz * vertsX + ix] = offset;
133
+ }
134
+ }
135
+ return {
136
+ bounds: { minX, minZ, maxX, maxZ },
137
+ cellSize: LAKEBED_CELL,
138
+ cols,
139
+ rows,
140
+ offsets,
141
+ surfaces: new Array(cols * rows).fill(null),
142
+ };
143
+ }
144
+ /**
145
+ * Derives the coordinate/placement content of an `environment()` world from its scene document:
146
+ * terrain footprint (via {@link terrainBoundsFromDocument}), ground clearings under authored
147
+ * spawns/POIs (via `clearanceZonesFrom`), and the document's sculpt snapshot. This is the seam that
148
+ * lets a game author its world footprint and flatten regions in the editor instead of hardcoding
149
+ * them — the `world.ts` that consumes it carries only engine tuning.
150
+ * @capability world-convergence derive environment coordinate content from the scene document
151
+ */
152
+ export function environmentContentFromDocument(doc, options = {}) {
153
+ const clearanceOptions = {
154
+ kinds: options.kinds ?? DEFAULT_CLEARANCE_KINDS,
155
+ ...(options.ids === undefined ? {} : { ids: options.ids }),
156
+ ...(options.defaultClearance === undefined ? {} : { defaultClearance: options.defaultClearance }),
157
+ ...(options.feather === undefined ? {} : { feather: options.feather }),
158
+ };
159
+ // The authored sculpt snapshot wins outright when the document carries one (the author owns the
160
+ // ground); otherwise water volumes derive their own lake-bed depression.
161
+ const sculpt = doc.terrain ?? lakebedFromWaterVolumes(doc);
162
+ const sky = skyFromDocument(doc);
163
+ return {
164
+ bounds: terrainBoundsFromDocument(doc, options),
165
+ clearings: clearanceZonesFrom(doc, clearanceOptions),
166
+ ...(sculpt === undefined ? {} : { sculpt }),
167
+ ...(sky === undefined ? {} : { sky }),
168
+ };
169
+ }
@@ -2,6 +2,10 @@
2
2
  export type { EditorDocument } from "./types.js";
3
3
  /** A sculpted heightfield overlay stored on a document ΓÇö the terraform snapshot of offset deltas. */
4
4
  export type { EditorTerrain } from "./types.js";
5
+ /** A baked top-down minimap (PNG data URI + world bounds) persisted on the scene document. */
6
+ export type { EditorMinimapBake } from "./types.js";
7
+ /** Scene-document sky/fog/lighting bag edited by the lighting workspace. */
8
+ export type { EditorEnvironment, EditorFogConfig, EditorSkyPreset } from "./types.js";
5
9
  /** Per-kind show/hide flags for the editor's layer panel. */
6
10
  export type { EditorKindVisibility } from "./types.js";
7
11
  /** The four placeable-object collections a prefab fragment or clipboard fragment carries. */
@@ -29,6 +33,10 @@ export type { EditorCatalogData } from "./types.js";
29
33
  export type { EditorCatalogDefinition } from "./types.js";
30
34
  /** Accepted shape for a game's `editorCatalogs` export: definitions, or a factory. */
31
35
  export type { EditorCatalogsInput } from "./types.js";
36
+ /** Well-known catalog id for authored entity definitions consumed by the default content path. */
37
+ export { ENTITY_CATALOG_ID, entityDefinitionSchema, entityEntryFromCatalog, resolveCatalogDefinitions } from "./entityCatalog.js";
38
+ /** Minimal document shape {@link entityEntryFromCatalog} reads — any `EditorDocument` satisfies it. */
39
+ export type { EntityCatalogDocumentLike } from "./entityCatalog.js";
32
40
  /** Accepted shape for a game's `editorLayers` export: a document, partial data, or a factory. */
33
41
  export type { EditorLayersInput } from "./types.js";
34
42
  /** A placeable point object in the scene ΓÇö spawn, mob, chest, POI, etc. */
@@ -46,11 +54,28 @@ export type { EditorVolumeShape } from "./types.js";
46
54
  /** HUD layout section on the scene document — panel id → anchor/offset/size/visibility. */
47
55
  export type { EditorUiDocument, EditorUiPanelLayout, HudPanelTypeDef, HudResizeAxes } from "./types.js";
48
56
  export { WELL_KNOWN_MARKER_KINDS, WELL_KNOWN_PATH_KINDS, WELL_KNOWN_VOLUME_KINDS, } from "./types.js";
49
- export { applyEditorDocumentOverlay, cloneEditorDocument, collectDescendants, createEmptyEditorDocument, createPrefabFragment, decodeEditorDocument, editorChildren, editorDocumentBounds, editorDocumentExtras, editorDocumentSize, editorParentOf, editorRoots, exportEditorDocumentJson, extractEditorFragment, findEditorCatalog, findEditorCatalogEntry, findEditorCollection, findEditorMarker, findEditorNote, findEditorPath, findEditorPrefab, findEditorVolume, importEditorDocumentJson, isEditorObjectLocked, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, seedEditorCatalogs, wouldCreateCycle, } from "./document.js";
57
+ export { applyEditorDocumentOverlay, cloneEditorDocument, collectDescendants, createEmptyEditorDocument, createPrefabFragment, decodeEditorDocument, editorChildren, editorDocumentBounds, editorDocumentExtras, editorDocumentSize, editorMarkerPosition, editorMarkerXZ, editorParentOf, editorRoots, exportEditorDocumentJson, extractEditorFragment, findEditorCatalog, findEditorCatalogEntry, findEditorCollection, findEditorMarker, findEditorNote, findEditorPath, findEditorPrefab, findEditorVolume, importEditorDocumentJson, isEditorObjectCollectionLocked, isEditorObjectHidden, isEditorObjectLocked, isEditorObjectSelfLocked, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, requireEditorMarker, seedEditorCatalogs, wouldCreateCycle, } from "./document.js";
50
58
  /** One field-level failure surfaced while decoding an untrusted editor document. */
51
59
  export type { EditorDocumentDiagnostic } from "./document.js";
60
+ export { WORLD_BASE_SHARD_ID, WORLD_MANIFEST_KIND, decodeWorldManifest, exportWorldManifestJson, loadWorldDocument, selectWorldShards, shardMatchesQuery, singleShardWorldManifest, splitEditorDocumentIntoShards, } from "./world.js";
61
+ /** A sharded world manifest (`world.json`): grid + shard list + streaming config. */
62
+ export type { DecodeWorldManifestResult, SplitWorldOptions, SplitWorldResult, WorldGrid, WorldManifest, WorldManifestShard, WorldQuery, WorldShardBounds, WorldShardResidency, WorldShardResolver, WorldStreamingConfig, } from "./world.js";
52
63
  /** Result of {@link decodeEditorDocument}: a typed document, or every diagnostic collected while decoding it. */
53
64
  export type { DecodeEditorDocumentResult } from "./document.js";
65
+ export { environmentContentFromDocument, skyConfigFromEnvironment, skyFromDocument, terrainBoundsFromDocument, } from "./environment.js";
66
+ /** Coordinate/placement content of an `environment()` world derived from the scene document. */
67
+ export type { EnvironmentContent, EnvironmentContentOptions } from "./environment.js";
68
+ export { bakeMinimapFromDocument, documentBakeZones } from "./minimap.js";
69
+ /** Editor-side minimap bake from the scene document (footprint + biome-tint zones). */
70
+ export type { DocumentBakeOptions } from "./minimap.js";
71
+ export { createWorldStreamer, shardDistance } from "./streamer.js";
72
+ /** A stateful proximity streamer that schedules shard load/unload as the camera moves. */
73
+ export type { StreamUpdate, StreamerShard, WorldStreamer, WorldStreamerConfig } from "./streamer.js";
74
+ /** A procedural directive on the scene document — scatter props or a mob population. */
75
+ export type { EditorDirective, EditorDirectiveArea, EditorDirectiveKind, EditorPopulationDirective, EditorPopulationSpecies, EditorScatterDirective, } from "./types.js";
76
+ export { applyDirectiveOverlay, materializeDirective, materializeDirectives, materializePopulationDirective, materializeScatterDirective, resolveDirectiveFootprint, } from "./directives.js";
77
+ /** The deterministic bake of scene directives into per-instance placements + sparse overlays. */
78
+ export type { DirectiveFootprint, DirectiveInstancePatch, EditorDirectiveOverlay, MaterializedInstance, } from "./directives.js";
54
79
  /** Creates an editor session with undo/redo history seeded from an initial document. */
55
80
  export { createEditorSession } from "./commands.js";
56
81
  /** Compact snapshot of a session state ΓÇö counts, selection, and the selected object. */
@@ -1,5 +1,12 @@
1
+ /** Well-known catalog id for authored entity definitions consumed by the default content path. */
2
+ export { ENTITY_CATALOG_ID, entityDefinitionSchema, entityEntryFromCatalog, resolveCatalogDefinitions } from "./entityCatalog.js";
1
3
  export { WELL_KNOWN_MARKER_KINDS, WELL_KNOWN_PATH_KINDS, WELL_KNOWN_VOLUME_KINDS, } from "./types.js";
2
- export { applyEditorDocumentOverlay, cloneEditorDocument, collectDescendants, createEmptyEditorDocument, createPrefabFragment, decodeEditorDocument, editorChildren, editorDocumentBounds, editorDocumentExtras, editorDocumentSize, editorParentOf, editorRoots, exportEditorDocumentJson, extractEditorFragment, findEditorCatalog, findEditorCatalogEntry, findEditorCollection, findEditorMarker, findEditorNote, findEditorPath, findEditorPrefab, findEditorVolume, importEditorDocumentJson, isEditorObjectLocked, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, seedEditorCatalogs, wouldCreateCycle, } from "./document.js";
4
+ export { applyEditorDocumentOverlay, cloneEditorDocument, collectDescendants, createEmptyEditorDocument, createPrefabFragment, decodeEditorDocument, editorChildren, editorDocumentBounds, editorDocumentExtras, editorDocumentSize, editorMarkerPosition, editorMarkerXZ, editorParentOf, editorRoots, exportEditorDocumentJson, extractEditorFragment, findEditorCatalog, findEditorCatalogEntry, findEditorCollection, findEditorMarker, findEditorNote, findEditorPath, findEditorPrefab, findEditorVolume, importEditorDocumentJson, isEditorObjectCollectionLocked, isEditorObjectHidden, isEditorObjectLocked, isEditorObjectSelfLocked, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, requireEditorMarker, seedEditorCatalogs, wouldCreateCycle, } from "./document.js";
5
+ export { WORLD_BASE_SHARD_ID, WORLD_MANIFEST_KIND, decodeWorldManifest, exportWorldManifestJson, loadWorldDocument, selectWorldShards, shardMatchesQuery, singleShardWorldManifest, splitEditorDocumentIntoShards, } from "./world.js";
6
+ export { environmentContentFromDocument, skyConfigFromEnvironment, skyFromDocument, terrainBoundsFromDocument, } from "./environment.js";
7
+ export { bakeMinimapFromDocument, documentBakeZones } from "./minimap.js";
8
+ export { createWorldStreamer, shardDistance } from "./streamer.js";
9
+ export { applyDirectiveOverlay, materializeDirective, materializeDirectives, materializePopulationDirective, materializeScatterDirective, resolveDirectiveFootprint, } from "./directives.js";
3
10
  /** Creates an editor session with undo/redo history seeded from an initial document. */
4
11
  export { createEditorSession } from "./commands.js";
5
12
  /** Compact snapshot of a session state ΓÇö counts, selection, and the selected object. */
@@ -0,0 +1,31 @@
1
+ import { type MinimapBakeBounds, type MinimapBakeOptions, type MinimapBakeZone } from "../world/minimapBake.js";
2
+ import type { EditorDocument } from "./types.js";
3
+ /**
4
+ * The biome-tint zones a document contributes to a bake: every volume carrying a `color` becomes a
5
+ * polygon tint over its footprint. (The optional stretch from #1036 — a bare bake still reads as
6
+ * terrain without any.)
7
+ * @capability minimap-bake derive biome-tint zones for a bake from the scene document
8
+ */
9
+ export declare function documentBakeZones(doc: EditorDocument): MinimapBakeZone[];
10
+ /** Options for {@link bakeMinimapFromDocument}. */
11
+ export interface DocumentBakeOptions extends MinimapBakeOptions {
12
+ /** Explicit bounds; default = the document's object bounds padded by `padding`. */
13
+ bounds?: MinimapBakeBounds;
14
+ /** Padding (m) around the derived bounds when `bounds` is omitted. Default 16. */
15
+ padding?: number;
16
+ /** Ground normal sampler for slope shading (optional). */
17
+ sampleNormal?: (x: number, z: number) => readonly [number, number, number];
18
+ /** Water level — ground at or below renders as water. */
19
+ waterLevel?: number;
20
+ }
21
+ /**
22
+ * Bakes a minimap image from a scene document plus a terrain height sampler: sizes the footprint to
23
+ * the authored objects (or an explicit `bounds`), tints biome-colored volumes, and rasterizes the
24
+ * terrain top-down. Returns `{ background, mapBounds }` for the `Minimap` props. The editor runs this
25
+ * as its bake action and stores the result on the document.
26
+ * @capability minimap-bake bake a minimap PNG + bounds from the authored scene document
27
+ */
28
+ export declare function bakeMinimapFromDocument(doc: EditorDocument, sampleHeight: (x: number, z: number) => number, options?: DocumentBakeOptions): {
29
+ background: string;
30
+ mapBounds: MinimapBakeBounds;
31
+ };
@@ -0,0 +1,73 @@
1
+ import { bakeMinimapImage, minimapBakeToPngDataUri, } from "../world/minimapBake.js";
2
+ import { editorDocumentBounds } from "./document.js";
3
+ /**
4
+ * The editor-side minimap bake (#1036): bake a top-down terrain image straight from the authored
5
+ * scene document. The document owns the zones (biome-tinted volumes) and the footprint; the caller
6
+ * supplies the composed `sampleHeight` (base terrain + the document's sculpt), since the sculpt on
7
+ * the document is a delta over the game's base terrain. Returns the `{ background, mapBounds }` the
8
+ * `Minimap`/`WorldMap` props take, so the editor stores that on the document and runtime consumes it
9
+ * with no new render path. Deterministic for a fixed scene — safe for verify/CI.
10
+ */
11
+ /** A volume that carries a `color` tints the bake over its XZ footprint. */
12
+ function volumeFootprint(volume) {
13
+ const half = volume.halfExtents !== undefined
14
+ ? { x: volume.halfExtents.x, z: volume.halfExtents.z }
15
+ : volume.radius !== undefined
16
+ ? { x: volume.radius, z: volume.radius }
17
+ : null;
18
+ if (half === null)
19
+ return null;
20
+ const { x, z } = volume.center;
21
+ return [
22
+ [x - half.x, z - half.z],
23
+ [x + half.x, z - half.z],
24
+ [x + half.x, z + half.z],
25
+ [x - half.x, z + half.z],
26
+ ];
27
+ }
28
+ /**
29
+ * The biome-tint zones a document contributes to a bake: every volume carrying a `color` becomes a
30
+ * polygon tint over its footprint. (The optional stretch from #1036 — a bare bake still reads as
31
+ * terrain without any.)
32
+ * @capability minimap-bake derive biome-tint zones for a bake from the scene document
33
+ */
34
+ export function documentBakeZones(doc) {
35
+ const zones = [];
36
+ for (const volume of doc.volumes) {
37
+ if (volume.color === undefined)
38
+ continue;
39
+ const polygon = volumeFootprint(volume);
40
+ if (polygon === null)
41
+ continue;
42
+ zones.push({ polygon, color: volume.color, alpha: 0.4 });
43
+ }
44
+ return zones;
45
+ }
46
+ /** Falls back to a small square when the document has no spatial objects. */
47
+ function boundsFromDocument(doc, padding) {
48
+ const b = editorDocumentBounds(doc);
49
+ if (b === null)
50
+ return { minX: -32, minZ: -32, maxX: 32, maxZ: 32 };
51
+ return { minX: b.min.x - padding, minZ: b.min.z - padding, maxX: b.max.x + padding, maxZ: b.max.z + padding };
52
+ }
53
+ /**
54
+ * Bakes a minimap image from a scene document plus a terrain height sampler: sizes the footprint to
55
+ * the authored objects (or an explicit `bounds`), tints biome-colored volumes, and rasterizes the
56
+ * terrain top-down. Returns `{ background, mapBounds }` for the `Minimap` props. The editor runs this
57
+ * as its bake action and stores the result on the document.
58
+ * @capability minimap-bake bake a minimap PNG + bounds from the authored scene document
59
+ */
60
+ export function bakeMinimapFromDocument(doc, sampleHeight, options = {}) {
61
+ const bounds = options.bounds ?? boundsFromDocument(doc, options.padding ?? 16);
62
+ const bake = bakeMinimapImage({
63
+ bounds,
64
+ sampleHeight,
65
+ ...(options.sampleNormal === undefined ? {} : { sampleNormal: options.sampleNormal }),
66
+ ...(options.waterLevel === undefined ? {} : { waterLevel: options.waterLevel }),
67
+ zones: documentBakeZones(doc),
68
+ }, {
69
+ ...(options.resolution === undefined ? {} : { resolution: options.resolution }),
70
+ ...(options.palette === undefined ? {} : { palette: options.palette }),
71
+ });
72
+ return { background: minimapBakeToPngDataUri(bake), mapBounds: bake.bounds };
73
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The proximity streamer for a sharded world (#985 stage 3): a stateful scheduler that decides,
3
+ * as the camera moves, which shards enter and leave residency. It is pure scheduling — actually
4
+ * reading/merging the shard documents stays the caller's job (compose with
5
+ * `@jgengine/core/editor/world`'s `loadWorldDocument` on each returned delta) — so the streamer has
6
+ * no I/O and stays deterministic and testable. Hysteresis (`loadRadius` < `keepRadius`) stops a
7
+ * shard on a boundary from thrashing load/unload every frame.
8
+ */
9
+ /** The minimal shard shape the streamer schedules on — any world-manifest shard satisfies it. */
10
+ export interface StreamerShard {
11
+ /** Stable shard id (the load/unload unit). */
12
+ id: string;
13
+ /** XZ footprint `[minX, minZ]`..`[maxX, maxZ]`; absent = always resident. */
14
+ bounds?: {
15
+ min: [number, number];
16
+ max: [number, number];
17
+ };
18
+ /** `"always"` keeps the shard resident regardless of distance; default `"streamed"` when bounded. */
19
+ residency?: "always" | "streamed";
20
+ }
21
+ /** Configuration for {@link createWorldStreamer}. */
22
+ export interface WorldStreamerConfig {
23
+ shards: readonly StreamerShard[];
24
+ /** Load a shard when the camera comes within this distance (m) of its footprint. */
25
+ loadRadius: number;
26
+ /** Keep a loaded shard resident until the camera is farther than this (m) — hysteresis band. */
27
+ keepRadius: number;
28
+ }
29
+ /** The residency change produced by one {@link WorldStreamer.update}. */
30
+ export interface StreamUpdate {
31
+ /** Shard ids that became resident on this update. */
32
+ load: string[];
33
+ /** Shard ids that left residency on this update. */
34
+ unload: string[];
35
+ /** The full resident shard-id set after this update (sorted, stable order). */
36
+ resident: string[];
37
+ }
38
+ /** A stateful proximity streamer over a fixed shard set. */
39
+ export interface WorldStreamer {
40
+ /** Advances the streamer to a new camera position, returning the residency delta. */
41
+ update(center: {
42
+ x: number;
43
+ z: number;
44
+ }): StreamUpdate;
45
+ /** The current resident shard-id set (sorted). */
46
+ resident(): string[];
47
+ /** Clears all residency (next update reloads from scratch). */
48
+ reset(): void;
49
+ }
50
+ /**
51
+ * Distance from a point to a shard's XZ footprint (0 when inside), or 0 for an always-resident shard.
52
+ * @capability world-streaming distance from a camera to a shard footprint
53
+ */
54
+ export declare function shardDistance(shard: StreamerShard, center: {
55
+ x: number;
56
+ z: number;
57
+ }): number;
58
+ /**
59
+ * Creates a proximity streamer over a world's shards. Always-resident shards are resident from the
60
+ * first update onward; each bounded shard loads when the camera comes within `loadRadius` of its
61
+ * footprint and unloads only once the camera passes `keepRadius` (≥ `loadRadius`, clamped up),
62
+ * giving a hysteresis band that prevents boundary thrash. Purely spatial and deterministic; the
63
+ * caller wires each delta to `loadWorldDocument`.
64
+ * @capability world-streaming schedule shard load/unload by camera proximity
65
+ */
66
+ export declare function createWorldStreamer(config: WorldStreamerConfig): WorldStreamer;