@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,168 @@
1
+ /**
2
+ * Headless, serializable model for a set of player-toggled windows/panels — the data layer behind a
3
+ * WoW-style UI where B opens the bag, C the character sheet, and ESC closes the topmost window. It
4
+ * carries *which panels are open*, their focus/z stacking order, per-panel position overrides, and
5
+ * exclusive-group membership, with zero rendering opinion. The React chrome in
6
+ * `@jgengine/react/panels` consumes this; a game keeps layout, skin, keybinds, and window content
7
+ * caller-owned. Pure and immutable: every reducer returns a new state and never mutates its input.
8
+ */
9
+ /**
10
+ * Build the initial {@link PanelState} from the panel defs: panels flagged `initial` start open (in
11
+ * declaration order, respecting group exclusivity), and every def's group/closable is captured into
12
+ * the state so later reducers need no defs.
13
+ *
14
+ * @capability panel-state serializable open/z/position state for a set of toggleable game windows
15
+ */
16
+ export function createPanelState(defs) {
17
+ const meta = {};
18
+ for (const def of defs) {
19
+ const entry = {};
20
+ if (def.group !== undefined)
21
+ entry.group = def.group;
22
+ if (def.closable !== undefined)
23
+ entry.closable = def.closable;
24
+ meta[def.id] = entry;
25
+ }
26
+ let state = { open: {}, z: {}, pos: {}, meta, nextZ: 1 };
27
+ for (const def of defs) {
28
+ if (def.initial === true)
29
+ state = openPanel(state, def.id);
30
+ }
31
+ return state;
32
+ }
33
+ /** Whether panel `id` is currently open. */
34
+ export function isOpen(state, id) {
35
+ return state.open[id] === true;
36
+ }
37
+ /** @internal Whether panel `id` may be closed by the player (default true). */
38
+ function isClosable(state, id) {
39
+ return state.meta[id]?.closable !== false;
40
+ }
41
+ /**
42
+ * Open panel `id`, raising it to the top of the focus stack. If it belongs to a `group`, the other
43
+ * open panels in that group are closed (exclusive windows). Opening an already-open panel just
44
+ * refocuses it. Position overrides are preserved.
45
+ *
46
+ * @capability panel-open open a window, raise its z, and enforce exclusive-group membership
47
+ */
48
+ export function openPanel(state, id) {
49
+ const group = state.meta[id]?.group;
50
+ const open = { ...state.open };
51
+ const z = { ...state.z };
52
+ let nextZ = state.nextZ;
53
+ if (group !== undefined) {
54
+ for (const otherId of Object.keys(open)) {
55
+ if (otherId !== id && state.meta[otherId]?.group === group) {
56
+ delete open[otherId];
57
+ delete z[otherId];
58
+ }
59
+ }
60
+ }
61
+ open[id] = true;
62
+ z[id] = nextZ++;
63
+ return { ...state, open, z, nextZ };
64
+ }
65
+ /**
66
+ * Close panel `id`. No-op if it is already closed. Its position override is kept so reopening restores
67
+ * the last placement.
68
+ *
69
+ * @capability panel-close close a window while remembering its last position
70
+ */
71
+ export function closePanel(state, id) {
72
+ if (state.open[id] !== true)
73
+ return state;
74
+ const open = { ...state.open };
75
+ const z = { ...state.z };
76
+ delete open[id];
77
+ delete z[id];
78
+ return { ...state, open, z };
79
+ }
80
+ /**
81
+ * Toggle panel `id` — open it (with group exclusivity) if closed, close it if open. The one-key
82
+ * behavior behind a keybind like `B` for the bag.
83
+ *
84
+ * @capability panel-toggle toggle a window open/closed from a single keybind
85
+ */
86
+ export function togglePanel(state, id) {
87
+ return isOpen(state, id) ? closePanel(state, id) : openPanel(state, id);
88
+ }
89
+ /**
90
+ * Raise open panel `id` to the top of the focus stack (assign it the highest z). No-op if it is
91
+ * closed or already on top, so a pointer-down that refocuses the top window returns the same state.
92
+ *
93
+ * @capability panel-focus raise a window to the top of the focus/z stack
94
+ */
95
+ export function focusPanel(state, id) {
96
+ if (state.open[id] !== true)
97
+ return state;
98
+ if (topPanel(state) === id)
99
+ return state;
100
+ return { ...state, z: { ...state.z, [id]: state.nextZ }, nextZ: state.nextZ + 1 };
101
+ }
102
+ /** @internal The id of the top (highest-z) open panel, or null when none are open. */
103
+ function topPanel(state) {
104
+ let top = null;
105
+ let topZ = -Infinity;
106
+ for (const id of Object.keys(state.open)) {
107
+ const value = state.z[id] ?? 0;
108
+ if (value > topZ) {
109
+ topZ = value;
110
+ top = id;
111
+ }
112
+ }
113
+ return top;
114
+ }
115
+ /**
116
+ * Close the topmost closable open panel — the ESC handler. Skips panels pinned with `closable: false`
117
+ * and returns the same state when nothing closable is open.
118
+ *
119
+ * @capability panel-close-top close the focused (topmost) closable window — the ESC behavior
120
+ */
121
+ export function closeTopPanel(state) {
122
+ const ordered = orderedOpenIds(state);
123
+ for (let i = ordered.length - 1; i >= 0; i--) {
124
+ const id = ordered[i];
125
+ if (isClosable(state, id))
126
+ return closePanel(state, id);
127
+ }
128
+ return state;
129
+ }
130
+ /** @internal Open panel ids sorted ascending by z (render order — last is on top). */
131
+ function orderedOpenIds(state) {
132
+ return Object.keys(state.open).sort((a, b) => (state.z[a] ?? 0) - (state.z[b] ?? 0));
133
+ }
134
+ /**
135
+ * Find the panel whose `hotkey` matches `code`, case-insensitively — the keybind router. `code` may be
136
+ * a `KeyboardEvent.code` (`"KeyB"`) or key (`"b"`); it is compared verbatim to each def's `hotkey`, so
137
+ * author the hotkey in whichever form the caller feeds in. Returns the first match's id, or null.
138
+ *
139
+ * @capability panel-hotkey resolve a keybind (code or key) to the panel id it toggles
140
+ */
141
+ export function panelByHotkey(defs, code) {
142
+ const needle = code.toLowerCase();
143
+ for (const def of defs) {
144
+ if (def.hotkey !== undefined && def.hotkey.toLowerCase() === needle)
145
+ return def.id;
146
+ }
147
+ return null;
148
+ }
149
+ /**
150
+ * Set panel `id`'s position override — the drag commit. Does not require the panel to be open, so a
151
+ * caller can pre-place a window before it is shown.
152
+ *
153
+ * @capability panel-move override a window's on-surface position (the drag commit)
154
+ */
155
+ export function movePanel(state, id, pos) {
156
+ return { ...state, pos: { ...state.pos, [id]: { x: pos.x, y: pos.y } } };
157
+ }
158
+ /**
159
+ * The open panels' defs sorted ascending by z — the render order for a window host (map to elements
160
+ * so the last, highest-z window paints on top). Defs unknown to the state are skipped.
161
+ *
162
+ * @capability panel-ordered-open open panels' defs in z/render order for a window host
163
+ */
164
+ export function orderedOpen(state, defs) {
165
+ return defs
166
+ .filter((def) => state.open[def.id] === true)
167
+ .sort((a, b) => (state.z[a.id] ?? 0) - (state.z[b.id] ?? 0));
168
+ }
package/dist/ui.d.ts CHANGED
@@ -10,6 +10,7 @@ export { listHudPanelTypes, registerHudPanelType, resizePanelSize, resolveHudPan
10
10
  export { HUD_ANCHOR_FRACTIONS, type HudAnchor, type HudLayoutStore, type HudPlacement, type HudSize, } from "./ui/hudLayout.js";
11
11
  export { hudScaleForViewport, overflowingPanels, resolveHudFit, type HudPlatform, type HudViewportConfig, } from "./ui/hudScale.js";
12
12
  export { orientationGateActive, orientationHintActive, resolveOrientationRequirement, type LayoutOrientation, } from "./ui/orientation.js";
13
+ export { closePanel, closeTopPanel, createPanelState, focusPanel, isOpen, movePanel, openPanel, orderedOpen, panelByHotkey, togglePanel, type PanelDef, type PanelPosition, type PanelState, } from "./ui/panelModel.js";
13
14
  export { moveSelectionFocus, selectionWindow, summarizeSelection, type EntitySummaryDef, type EntityVital, type SelectionFocusDirection, type SelectionGroup, type SelectionView, type SelectionWindow, type SummarizeSelectionOptions, } from "./ui/selectionModel.js";
14
15
  export { swingTimerState, type SwingTargetInput } from "./ui/swingTimer.js";
15
16
  export { actionTooltip, placePopover, type PopoverOptions, type PopoverPlacement, type PopoverSide, type TooltipContent, type UiRect, type UiSize, } from "./ui/tooltipModel.js";
package/dist/ui.js CHANGED
@@ -10,6 +10,7 @@ export { listHudPanelTypes, registerHudPanelType, resizePanelSize, resolveHudPan
10
10
  export { HUD_ANCHOR_FRACTIONS, } from "./ui/hudLayout.js";
11
11
  export { hudScaleForViewport, overflowingPanels, resolveHudFit, } from "./ui/hudScale.js";
12
12
  export { orientationGateActive, orientationHintActive, resolveOrientationRequirement, } from "./ui/orientation.js";
13
+ export { closePanel, closeTopPanel, createPanelState, focusPanel, isOpen, movePanel, openPanel, orderedOpen, panelByHotkey, togglePanel, } from "./ui/panelModel.js";
13
14
  export { moveSelectionFocus, selectionWindow, summarizeSelection, } from "./ui/selectionModel.js";
14
15
  export { swingTimerState } from "./ui/swingTimer.js";
15
16
  export { actionTooltip, placePopover, } from "./ui/tooltipModel.js";
@@ -0,0 +1,29 @@
1
+ import { ENTITY_MARKER_KINDS, type AuthoredObjectMarkerLike } from "./authoredObjects.js";
2
+ /** Marker kinds {@link authoredEntitySpawns} treats as spawnable entities by default. Re-exported from `authoredObjects` as the single source of truth (the same kinds `resolveAuthoredObjects` skips). */
3
+ export { ENTITY_MARKER_KINDS };
4
+ /** The minimal marker shape entity spawns read; any `EditorMarker` satisfies it. */
5
+ export type AuthoredEntityMarkerLike = AuthoredObjectMarkerLike;
6
+ /** The minimal document shape {@link authoredEntitySpawns} walks; any `EditorDocument` satisfies it. */
7
+ export interface AuthoredEntityDocumentLike {
8
+ markers: readonly AuthoredEntityMarkerLike[];
9
+ }
10
+ /** One resolved entity to spawn from the authored scene — its marker id, catalog id, and pose. */
11
+ export interface AuthoredEntitySpawn {
12
+ /** The marker's id — a stable spawn id, so re-spawning the same marker is idempotent. */
13
+ markerId: string;
14
+ /** The entity catalog id to spawn (join key into `content.entityById`). */
15
+ catalogId: string;
16
+ /** World-space spawn position as a spawn-ready `[x, y, z]` tuple. */
17
+ position: [number, number, number];
18
+ /** Facing (yaw radians), 0 when the marker has none. */
19
+ rotationY: number;
20
+ }
21
+ /**
22
+ * Every authored entity marker (`mob`/`boss` by default) that carries a `catalogId`, as a spawn plan
23
+ * the runtime feeds to `ctx.scene.entity.spawn(catalogId, { id, position })`. Reads the same document
24
+ * the editor authors, so placing a mob and setting its catalog id in the editor is all it takes to
25
+ * spawn it — no coordinates or spawn tables copied into game code. Markers without a catalog id are
26
+ * skipped (they are placement-only, e.g. a boss arena marker a game handles specially).
27
+ * @capability authored-entities spawn authored mob/boss markers from the editor document at runtime
28
+ */
29
+ export declare function authoredEntitySpawns(document: AuthoredEntityDocumentLike, kinds?: readonly string[]): AuthoredEntitySpawn[];
@@ -0,0 +1,29 @@
1
+ import { ENTITY_MARKER_KINDS, markerCatalogId } from "./authoredObjects.js";
2
+ /** Marker kinds {@link authoredEntitySpawns} treats as spawnable entities by default. Re-exported from `authoredObjects` as the single source of truth (the same kinds `resolveAuthoredObjects` skips). */
3
+ export { ENTITY_MARKER_KINDS };
4
+ /**
5
+ * Every authored entity marker (`mob`/`boss` by default) that carries a `catalogId`, as a spawn plan
6
+ * the runtime feeds to `ctx.scene.entity.spawn(catalogId, { id, position })`. Reads the same document
7
+ * the editor authors, so placing a mob and setting its catalog id in the editor is all it takes to
8
+ * spawn it — no coordinates or spawn tables copied into game code. Markers without a catalog id are
9
+ * skipped (they are placement-only, e.g. a boss arena marker a game handles specially).
10
+ * @capability authored-entities spawn authored mob/boss markers from the editor document at runtime
11
+ */
12
+ export function authoredEntitySpawns(document, kinds = ENTITY_MARKER_KINDS) {
13
+ const wanted = new Set(kinds);
14
+ const spawns = [];
15
+ for (const marker of document.markers) {
16
+ if (!wanted.has(marker.kind))
17
+ continue;
18
+ const catalogId = markerCatalogId(marker);
19
+ if (catalogId === null)
20
+ continue;
21
+ spawns.push({
22
+ markerId: marker.id,
23
+ catalogId,
24
+ position: [marker.position.x, marker.position.y, marker.position.z],
25
+ rotationY: marker.rotationY ?? 0,
26
+ });
27
+ }
28
+ return spawns;
29
+ }
@@ -42,12 +42,26 @@ export interface PlaceAuthoredObjectsOptions {
42
42
  * @capability authored-objects place catalog mesh props from an editor document
43
43
  */
44
44
  export declare function markerCatalogId(marker: AuthoredObjectMarkerLike): string | null;
45
+ /**
46
+ * Marker kinds that are spawnable entities (materialized by `authoredEntitySpawns`), not static
47
+ * catalog props. {@link resolveAuthoredObjects} skips them by default so a placed `mob`/`boss` is
48
+ * spawned once as an entity and never also placed as a static mesh.
49
+ * @capability authored-objects marker kinds resolved as entity spawns, not static props
50
+ */
51
+ export declare const ENTITY_MARKER_KINDS: readonly string[];
52
+ /** Options for {@link resolveAuthoredObjects}. */
53
+ export interface ResolveAuthoredObjectsOptions {
54
+ /** Marker kinds to skip (they are handled elsewhere). Default {@link ENTITY_MARKER_KINDS}. */
55
+ excludeKinds?: readonly string[];
56
+ }
45
57
  /**
46
58
  * Every marker carrying a catalog id, as placeable props — pure, no terrain sample. Parallel to
47
59
  * {@link resolveScatter}: games and headless tests read the same list `<AuthoredObjects>` places.
60
+ * Entity-spawn kinds (`mob`/`boss`) are skipped by default — they carry a `catalogId` for their
61
+ * entity definition, but are spawned via `authoredEntitySpawns`, not placed as static meshes.
48
62
  * @capability authored-objects place catalog mesh props from an editor document
49
63
  */
50
- export declare function resolveAuthoredObjects(document: AuthoredObjectsDocumentLike): AuthoredObject[];
64
+ export declare function resolveAuthoredObjects(document: AuthoredObjectsDocumentLike, options?: ResolveAuthoredObjectsOptions): AuthoredObject[];
51
65
  /**
52
66
  * Places resolved authored objects into an object store, grounding each on `sampleHeight(x,z)` plus
53
67
  * per-object and options vertical offsets. Returns the instance ids that were placed (or kept).
@@ -22,14 +22,26 @@ function markerVerticalOffset(marker) {
22
22
  metaNumber(marker.meta, "yOffset");
23
23
  return fromMeta ?? 0;
24
24
  }
25
+ /**
26
+ * Marker kinds that are spawnable entities (materialized by `authoredEntitySpawns`), not static
27
+ * catalog props. {@link resolveAuthoredObjects} skips them by default so a placed `mob`/`boss` is
28
+ * spawned once as an entity and never also placed as a static mesh.
29
+ * @capability authored-objects marker kinds resolved as entity spawns, not static props
30
+ */
31
+ export const ENTITY_MARKER_KINDS = ["mob", "boss"];
25
32
  /**
26
33
  * Every marker carrying a catalog id, as placeable props — pure, no terrain sample. Parallel to
27
34
  * {@link resolveScatter}: games and headless tests read the same list `<AuthoredObjects>` places.
35
+ * Entity-spawn kinds (`mob`/`boss`) are skipped by default — they carry a `catalogId` for their
36
+ * entity definition, but are spawned via `authoredEntitySpawns`, not placed as static meshes.
28
37
  * @capability authored-objects place catalog mesh props from an editor document
29
38
  */
30
- export function resolveAuthoredObjects(document) {
39
+ export function resolveAuthoredObjects(document, options = {}) {
40
+ const excluded = new Set(options.excludeKinds ?? ENTITY_MARKER_KINDS);
31
41
  const out = [];
32
42
  for (const marker of document.markers) {
43
+ if (excluded.has(marker.kind))
44
+ continue;
33
45
  const catalogId = markerCatalogId(marker);
34
46
  if (catalogId === null)
35
47
  continue;
@@ -0,0 +1,39 @@
1
+ import type { SceneMarkerLike } from "./sceneShapes.js";
2
+ /** Minimal marker shape spawn queries read; any `EditorMarker` satisfies it. */
3
+ export interface AuthoredSpawnMarkerLike extends SceneMarkerLike {
4
+ rotationY?: number;
5
+ }
6
+ /** Minimal document shape spawn queries walk; any `EditorDocument` satisfies it. */
7
+ export interface AuthoredSpawnDocumentLike {
8
+ markers: readonly AuthoredSpawnMarkerLike[];
9
+ }
10
+ /** Marker kind {@link authoredSpawnPosition} resolves by default. */
11
+ export declare const PLAYER_SPAWN_KIND = "player_spawn";
12
+ /**
13
+ * Every marker of `kind`, in document order — the generic query behind gameplay that references
14
+ * authored markers by kind instead of copying coordinates into code.
15
+ * @capability authored-spawn read spawn points and markers from the editor document
16
+ */
17
+ export declare function markersOfKind<TMarker extends AuthoredSpawnMarkerLike>(document: {
18
+ markers: readonly TMarker[];
19
+ }, kind: string): TMarker[];
20
+ /** Options for {@link authoredSpawnPosition}: pick a specific marker id or a non-default kind. */
21
+ export interface AuthoredSpawnOptions {
22
+ /** Marker kind to resolve. Default {@link PLAYER_SPAWN_KIND}. */
23
+ kind?: string;
24
+ /** Exact marker id; when set, kind only filters among markers with this id's kind. */
25
+ id?: string;
26
+ }
27
+ /**
28
+ * Position of the authored spawn marker as a spawn-ready `[x, y, z]` tuple, or null when the
29
+ * document has none. Reads the first `player_spawn` marker by default, so dragging the marker in
30
+ * the editor moves where players spawn — no coordinates copied into game code.
31
+ * @capability authored-spawn read spawn points and markers from the editor document
32
+ */
33
+ export declare function authoredSpawnPosition(document: AuthoredSpawnDocumentLike, options?: AuthoredSpawnOptions): [number, number, number] | null;
34
+ /**
35
+ * Facing (yaw radians) of the authored spawn marker, or 0 when the document has none — pair with
36
+ * {@link authoredSpawnPosition} to spawn players where and how the editor placed them.
37
+ * @capability authored-spawn read spawn points and markers from the editor document
38
+ */
39
+ export declare function authoredSpawnRotation(document: AuthoredSpawnDocumentLike, options?: AuthoredSpawnOptions): number;
@@ -0,0 +1,37 @@
1
+ /** Marker kind {@link authoredSpawnPosition} resolves by default. */
2
+ export const PLAYER_SPAWN_KIND = "player_spawn";
3
+ /**
4
+ * Every marker of `kind`, in document order — the generic query behind gameplay that references
5
+ * authored markers by kind instead of copying coordinates into code.
6
+ * @capability authored-spawn read spawn points and markers from the editor document
7
+ */
8
+ export function markersOfKind(document, kind) {
9
+ return document.markers.filter((marker) => marker.kind === kind);
10
+ }
11
+ /**
12
+ * Position of the authored spawn marker as a spawn-ready `[x, y, z]` tuple, or null when the
13
+ * document has none. Reads the first `player_spawn` marker by default, so dragging the marker in
14
+ * the editor moves where players spawn — no coordinates copied into game code.
15
+ * @capability authored-spawn read spawn points and markers from the editor document
16
+ */
17
+ export function authoredSpawnPosition(document, options = {}) {
18
+ const kind = options.kind ?? PLAYER_SPAWN_KIND;
19
+ const marker = options.id === undefined
20
+ ? document.markers.find((entry) => entry.kind === kind)
21
+ : document.markers.find((entry) => entry.id === options.id && entry.kind === kind);
22
+ if (marker === undefined)
23
+ return null;
24
+ return [marker.position.x, marker.position.y, marker.position.z];
25
+ }
26
+ /**
27
+ * Facing (yaw radians) of the authored spawn marker, or 0 when the document has none — pair with
28
+ * {@link authoredSpawnPosition} to spawn players where and how the editor placed them.
29
+ * @capability authored-spawn read spawn points and markers from the editor document
30
+ */
31
+ export function authoredSpawnRotation(document, options = {}) {
32
+ const kind = options.kind ?? PLAYER_SPAWN_KIND;
33
+ const marker = options.id === undefined
34
+ ? document.markers.find((entry) => entry.kind === kind)
35
+ : document.markers.find((entry) => entry.id === options.id && entry.kind === kind);
36
+ return marker?.rotationY ?? 0;
37
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Data-driven modular build-piece snap system: a catalog of piece types, each declaring named
3
+ * sockets (local position + outward facing + a generic "kind"), plus a connection-rule table saying
4
+ * which socket kinds may mate. Given a placed piece and a candidate type it resolves every valid
5
+ * snap — the world transform that lands the candidate's socket exactly on the placed piece's socket,
6
+ * facing back into it. Genre-agnostic: foundation/wall/ceiling/pillar/edge snapping falls out of the
7
+ * catalog data, with no game-specific piece names baked in. Pure and serializable; composes with
8
+ * `world/footprintGrid` occupancy (via an injected `isFree` predicate — never imported) and with
9
+ * `scene/modelSockets` named attach points (via {@link pieceSocketsFromModel}).
10
+ */
11
+ /** A local- or world-space position/offset, matching the `[x, y, z]` triple used across core. */
12
+ export type SocketVec3 = readonly [number, number, number];
13
+ /** One named socket on a piece: where it sits and which horizontal direction it faces (yaw about Y). */
14
+ export interface SocketDef {
15
+ /** Stable socket name, unique within its piece. */
16
+ name: string;
17
+ /** Generic connection kind — e.g. "top", "bottom", "edge", "side". Matched by the rule table. */
18
+ kind: string;
19
+ /** Local offset from the piece origin. */
20
+ position: SocketVec3;
21
+ /** Outward-facing yaw in radians (rotation about +Y), local to the piece. Default `0`. */
22
+ yaw?: number;
23
+ }
24
+ /** A rectangular footprint (world units) a piece occupies on the build grid — the occupancy bridge. */
25
+ export interface BuildFootprint {
26
+ w: number;
27
+ d: number;
28
+ }
29
+ /** A registered piece type: its named sockets and, optionally, the footprint it claims when placed. */
30
+ export interface BuildPieceDef {
31
+ /** Stable piece-type id, unique within the catalog. */
32
+ type: string;
33
+ sockets: readonly SocketDef[];
34
+ /** Grid footprint for occupancy checks; omit for pieces that don't reserve cells. */
35
+ footprint?: BuildFootprint;
36
+ }
37
+ /** Declares that two socket kinds may snap together; `mutual` (default `true`) also allows `b`→`a`. */
38
+ export interface ConnectionRule {
39
+ a: string;
40
+ b: string;
41
+ /** When `false` the rule is directional: an `a`-kind candidate socket may mate onto a `b`-kind placed socket, but not the reverse. */
42
+ mutual?: boolean;
43
+ }
44
+ /** World transform of a placed piece: position plus a yaw about +Y. */
45
+ export interface PieceTransform {
46
+ position: SocketVec3;
47
+ /** Rotation about +Y in radians. Default `0`. */
48
+ yaw?: number;
49
+ }
50
+ /** An already-placed piece the resolver snaps candidates against. */
51
+ export interface PlacedPiece {
52
+ type: string;
53
+ transform: PieceTransform;
54
+ }
55
+ /** A piece's socket resolved into world space. */
56
+ export interface PlacedSocket {
57
+ name: string;
58
+ kind: string;
59
+ /** World-space socket position. */
60
+ position: SocketVec3;
61
+ /** World-space outward facing yaw (radians). */
62
+ yaw: number;
63
+ }
64
+ /** One integer grid cell — structurally the `GridCell` of `world/footprintGrid` (no import needed). */
65
+ export interface BuildCell {
66
+ col: number;
67
+ row: number;
68
+ }
69
+ /** A resolved snap: which sockets mate and the world transform that lands the candidate onto the target. */
70
+ export interface SnapCandidate {
71
+ candidateType: string;
72
+ /** Name of the socket on the already-placed piece. */
73
+ targetSocket: string;
74
+ /** Name of the candidate's socket that mates onto it. */
75
+ candidateSocket: string;
76
+ /** Where to place the candidate so its socket aligns onto the target socket. */
77
+ transform: {
78
+ position: SocketVec3;
79
+ yaw: number;
80
+ };
81
+ /** Shared world position of the mated sockets. */
82
+ point: SocketVec3;
83
+ /** Distance from the resolve `cursor` to `point`, or `0` when no cursor was given. */
84
+ distance: number;
85
+ }
86
+ /** Options for {@link BuildSocketCatalog.resolveSnaps}. */
87
+ export interface ResolveSnapsOptions {
88
+ /** Cursor point; when given, candidates are ordered nearest-first by mate-point distance. */
89
+ cursor?: SocketVec3;
90
+ /**
91
+ * Occupancy predicate — return `false` to reject a snap whose target cells are taken. Only applied
92
+ * to candidate pieces that declare a `footprint`. Compose with `world/footprintGrid` via
93
+ * `(cell) => grid.isFree([cell])`; the catalog never imports the grid.
94
+ */
95
+ isFree?: (cell: BuildCell) => boolean;
96
+ }
97
+ /** Serializable catalog config; also the shape returned by {@link BuildSocketCatalog.toJSON}. */
98
+ export interface BuildSocketCatalogConfig {
99
+ pieces: readonly BuildPieceDef[];
100
+ rules: readonly ConnectionRule[];
101
+ /** World units per grid cell for footprint occupancy. Default `1`. */
102
+ cellSize?: number;
103
+ }
104
+ /** Handle returned by {@link createBuildSocketCatalog}. */
105
+ export interface BuildSocketCatalog {
106
+ readonly cellSize: number;
107
+ has(type: string): boolean;
108
+ piece(type: string): BuildPieceDef | null;
109
+ /** Registered piece-type ids, in declaration order. */
110
+ types(): readonly string[];
111
+ /** True when a `candidateKind` socket may mate onto a `placedKind` socket. */
112
+ canConnect(candidateKind: string, placedKind: string): boolean;
113
+ /** Every socket of `placed` resolved into world space. */
114
+ worldSockets(placed: PlacedPiece): PlacedSocket[];
115
+ /** Every valid snap of `candidateType` onto `placed`, ordered deterministically. */
116
+ resolveSnaps(placed: PlacedPiece, candidateType: string, options?: ResolveSnapsOptions): SnapCandidate[];
117
+ /** Plain, serializable snapshot — feed straight back into {@link createBuildSocketCatalog}. */
118
+ toJSON(): BuildSocketCatalogConfig;
119
+ }
120
+ /**
121
+ * The grid cells a piece `footprint` centered on `position` (world units) covers, honoring `yaw`
122
+ * (snapped to the nearest quarter turn). Mirrors `world/footprintGrid`'s `cellsFor` so a game can
123
+ * feed the result straight into `grid.isFree`/`grid.reserve` after committing a resolved snap —
124
+ * without this module importing the grid.
125
+ *
126
+ * @capability build-footprint-cells grid cells a snapped build piece footprint covers (footprintGrid occupancy bridge)
127
+ */
128
+ export declare function footprintCells(position: SocketVec3, footprint: BuildFootprint, yaw?: number, cellSize?: number): BuildCell[];
129
+ /** A `{ name, offset }` attach point — structurally the `ModelSocket` of `scene/modelSockets`. */
130
+ export interface ModelSocketLike {
131
+ name: string;
132
+ offset: SocketVec3;
133
+ }
134
+ /**
135
+ * Turn a model's named attach points (from `scene/modelSockets`' `readNamedSockets`) into
136
+ * {@link SocketDef}s, classifying each into a connection `kind` via `kindOf` and, optionally, an
137
+ * outward `yaw` via `yawOf`. Lets an artist author snap sockets as empties in a GLB instead of by
138
+ * hand in the catalog. Pure and structural — no three.js or scene import.
139
+ *
140
+ * @capability piece-sockets-from-model derive build-piece snap sockets from a model's named attach points
141
+ */
142
+ export declare function pieceSocketsFromModel(sockets: readonly ModelSocketLike[], kindOf: (name: string) => string, yawOf?: (name: string) => number): SocketDef[];
143
+ /**
144
+ * Build a modular snap catalog from piece definitions and a connection-rule table. Resolves the set
145
+ * of valid snap transforms when hovering a candidate piece near a placed one: for every compatible
146
+ * socket pair it computes the world transform that seats the candidate's socket onto the placed
147
+ * socket, oriented to face back into it. Deterministic ordering (nearest a cursor when given, else
148
+ * declaration order) and an optional occupancy predicate keep it drop-in for a build-mode controller.
149
+ *
150
+ * @capability build-socket-catalog data-driven named-socket snap catalog for modular base building
151
+ */
152
+ export declare function createBuildSocketCatalog(config: BuildSocketCatalogConfig): BuildSocketCatalog;