@jgengine/core 0.6.0 → 0.8.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 (330) hide show
  1. package/CHANGELOG.md +214 -2
  2. package/README.md +2 -0
  3. package/dist/ai/crowd.d.ts +36 -0
  4. package/dist/ai/crowd.js +213 -0
  5. package/dist/ai/jobBoard.d.ts +49 -0
  6. package/dist/ai/jobBoard.js +155 -0
  7. package/dist/ai/spawnDirector.d.ts +61 -0
  8. package/dist/ai/spawnDirector.js +162 -0
  9. package/dist/ai/threat.d.ts +25 -0
  10. package/dist/ai/threat.js +67 -0
  11. package/dist/anim/easing.d.ts +14 -0
  12. package/dist/anim/easing.js +55 -0
  13. package/dist/audio/audioFalloff.d.ts +38 -0
  14. package/dist/audio/audioFalloff.js +32 -0
  15. package/dist/behaviour/behaviour.d.ts +136 -0
  16. package/dist/behaviour/behaviour.js +388 -0
  17. package/dist/board/laneBoard.d.ts +63 -0
  18. package/dist/board/laneBoard.js +114 -0
  19. package/dist/board/timelineBoard.d.ts +36 -0
  20. package/dist/board/timelineBoard.js +70 -0
  21. package/dist/cards/cardPile.d.ts +64 -0
  22. package/dist/cards/cardPile.js +175 -0
  23. package/dist/cards/modifierPipeline.d.ts +33 -0
  24. package/dist/cards/modifierPipeline.js +48 -0
  25. package/dist/combat/abilityKit.d.ts +47 -0
  26. package/dist/combat/abilityKit.js +139 -0
  27. package/dist/combat/animationState.d.ts +47 -0
  28. package/dist/combat/animationState.js +101 -0
  29. package/dist/combat/attackTags.d.ts +14 -0
  30. package/dist/combat/attackTags.js +26 -0
  31. package/dist/combat/breakMeters.d.ts +32 -0
  32. package/dist/combat/breakMeters.js +51 -0
  33. package/dist/combat/comboString.d.ts +35 -0
  34. package/dist/combat/comboString.js +59 -0
  35. package/dist/combat/death.d.ts +8 -0
  36. package/dist/combat/death.js +5 -2
  37. package/dist/combat/defensiveWindow.d.ts +34 -0
  38. package/dist/combat/defensiveWindow.js +66 -0
  39. package/dist/combat/downed.d.ts +35 -0
  40. package/dist/combat/downed.js +104 -0
  41. package/dist/combat/effects.d.ts +12 -1
  42. package/dist/combat/effects.js +31 -19
  43. package/dist/combat/hitReaction.d.ts +23 -0
  44. package/dist/combat/hitReaction.js +20 -0
  45. package/dist/combat/projectiles.d.ts +42 -5
  46. package/dist/combat/projectiles.js +98 -12
  47. package/dist/combat/resistance.d.ts +13 -0
  48. package/dist/combat/resistance.js +46 -0
  49. package/dist/combat/telegraph.d.ts +31 -0
  50. package/dist/combat/telegraph.js +51 -0
  51. package/dist/commands/commandRegistry.d.ts +1 -1
  52. package/dist/commands/commandRegistry.js +2 -1
  53. package/dist/crafting/crop.d.ts +59 -0
  54. package/dist/crafting/crop.js +181 -0
  55. package/dist/crafting/production.d.ts +64 -0
  56. package/dist/crafting/production.js +130 -0
  57. package/dist/crafting/recipe.d.ts +62 -0
  58. package/dist/crafting/recipe.js +101 -0
  59. package/dist/data/dataSource.d.ts +26 -0
  60. package/dist/data/dataSource.js +93 -0
  61. package/dist/data/devProxy.d.ts +11 -0
  62. package/dist/data/devProxy.js +38 -0
  63. package/dist/data/fetchJson.d.ts +20 -0
  64. package/dist/data/fetchJson.js +43 -0
  65. package/dist/data/jsonDataSource.d.ts +4 -0
  66. package/dist/data/jsonDataSource.js +6 -0
  67. package/dist/devtools/devtools.d.ts +157 -0
  68. package/dist/devtools/devtools.js +484 -0
  69. package/dist/devtools/transformTunables.d.ts +14 -0
  70. package/dist/devtools/transformTunables.js +38 -0
  71. package/dist/economy/sharedWallet.d.ts +28 -0
  72. package/dist/economy/sharedWallet.js +62 -0
  73. package/dist/economy/techTree.d.ts +40 -0
  74. package/dist/economy/techTree.js +66 -0
  75. package/dist/game/chat.d.ts +66 -0
  76. package/dist/game/chat.js +165 -0
  77. package/dist/game/cosmetics.d.ts +25 -0
  78. package/dist/game/cosmetics.js +51 -0
  79. package/dist/game/defineGame.d.ts +10 -2
  80. package/dist/game/defineGame.js +2 -1
  81. package/dist/game/events.d.ts +87 -0
  82. package/dist/game/levelSequence.d.ts +44 -0
  83. package/dist/game/levelSequence.js +78 -0
  84. package/dist/game/lootFilter.d.ts +33 -0
  85. package/dist/game/lootFilter.js +48 -0
  86. package/dist/game/ping.d.ts +67 -0
  87. package/dist/game/ping.js +86 -0
  88. package/dist/game/playableGame.d.ts +516 -8
  89. package/dist/game/playableGame.js +6 -1
  90. package/dist/game/progression.d.ts +1 -0
  91. package/dist/game/progression.js +23 -2
  92. package/dist/game/race.d.ts +100 -0
  93. package/dist/game/race.js +236 -0
  94. package/dist/game/runDraft.d.ts +34 -0
  95. package/dist/game/runDraft.js +97 -0
  96. package/dist/game/social.d.ts +69 -0
  97. package/dist/game/social.js +138 -0
  98. package/dist/game/spawnPoints.d.ts +19 -0
  99. package/dist/game/spawnPoints.js +23 -0
  100. package/dist/game/worldItem.d.ts +95 -0
  101. package/dist/game/worldItem.js +138 -0
  102. package/dist/input/actionBindings.d.ts +10 -0
  103. package/dist/input/actionBindings.js +14 -0
  104. package/dist/input/axisInput.d.ts +43 -0
  105. package/dist/input/axisInput.js +70 -0
  106. package/dist/input/gestureSurface.d.ts +31 -0
  107. package/dist/input/gestureSurface.js +120 -0
  108. package/dist/input/pointer.d.ts +69 -0
  109. package/dist/input/pointer.js +65 -0
  110. package/dist/input/touchScheme.d.ts +83 -0
  111. package/dist/input/touchScheme.js +115 -0
  112. package/dist/interaction/contextMenu.d.ts +30 -0
  113. package/dist/interaction/contextMenu.js +22 -0
  114. package/dist/interaction/qte.d.ts +20 -0
  115. package/dist/interaction/qte.js +24 -0
  116. package/dist/interaction/skillCheck.d.ts +20 -0
  117. package/dist/interaction/skillCheck.js +26 -0
  118. package/dist/inventory/shapedGrid.d.ts +56 -0
  119. package/dist/inventory/shapedGrid.js +176 -0
  120. package/dist/inventory/storageTier.d.ts +52 -0
  121. package/dist/inventory/storageTier.js +77 -0
  122. package/dist/item/affix.d.ts +54 -0
  123. package/dist/item/affix.js +115 -0
  124. package/dist/item/durability.d.ts +51 -0
  125. package/dist/item/durability.js +78 -0
  126. package/dist/item/modularItem.d.ts +46 -0
  127. package/dist/item/modularItem.js +75 -0
  128. package/dist/meta/changelog.d.ts +1 -1
  129. package/dist/meta/changelog.js +71 -1
  130. package/dist/movement/dash.d.ts +37 -0
  131. package/dist/movement/dash.js +65 -0
  132. package/dist/movement/movementModel.d.ts +36 -3
  133. package/dist/movement/movementModel.js +81 -5
  134. package/dist/movement/voxelController.d.ts +61 -0
  135. package/dist/movement/voxelController.js +229 -0
  136. package/dist/multiplayer/chatContract.d.ts +42 -0
  137. package/dist/multiplayer/chatContract.js +57 -0
  138. package/dist/multiplayer/combatSnapshot.d.ts +45 -0
  139. package/dist/multiplayer/combatSnapshot.js +89 -0
  140. package/dist/multiplayer/identity.d.ts +13 -0
  141. package/dist/multiplayer/identity.js +20 -0
  142. package/dist/multiplayer/lagCompensation.d.ts +43 -0
  143. package/dist/multiplayer/lagCompensation.js +154 -0
  144. package/dist/multiplayer/matchmaking.d.ts +33 -0
  145. package/dist/multiplayer/matchmaking.js +60 -0
  146. package/dist/multiplayer/poseSyncGate.d.ts +1 -0
  147. package/dist/multiplayer/poseSyncGate.js +13 -1
  148. package/dist/multiplayer/presenceModel.d.ts +3 -0
  149. package/dist/multiplayer/presenceModel.js +15 -1
  150. package/dist/multiplayer/simultaneousCommit.d.ts +34 -0
  151. package/dist/multiplayer/simultaneousCommit.js +53 -0
  152. package/dist/multiplayer/voiceContract.d.ts +44 -0
  153. package/dist/multiplayer/voiceContract.js +108 -0
  154. package/dist/nav/navConstrain.d.ts +14 -0
  155. package/dist/nav/navConstrain.js +26 -0
  156. package/dist/nav/navFromEnvironment.d.ts +11 -0
  157. package/dist/nav/navFromEnvironment.js +16 -0
  158. package/dist/nav/navGrid.d.ts +56 -0
  159. package/dist/nav/navGrid.js +265 -0
  160. package/dist/nav/pathFollow.d.ts +31 -0
  161. package/dist/nav/pathFollow.js +69 -0
  162. package/dist/physics/ballisticSweep.d.ts +18 -0
  163. package/dist/physics/ballisticSweep.js +49 -0
  164. package/dist/physics/buoyancy.d.ts +49 -0
  165. package/dist/physics/buoyancy.js +112 -0
  166. package/dist/physics/carryable.d.ts +46 -0
  167. package/dist/physics/carryable.js +110 -0
  168. package/dist/physics/damageZones.d.ts +56 -0
  169. package/dist/physics/damageZones.js +107 -0
  170. package/dist/physics/forceVolume.d.ts +48 -0
  171. package/dist/physics/forceVolume.js +119 -0
  172. package/dist/physics/physicsWorld.d.ts +124 -2
  173. package/dist/physics/physicsWorld.js +493 -41
  174. package/dist/physics/ragdoll.d.ts +49 -0
  175. package/dist/physics/ragdoll.js +103 -0
  176. package/dist/physics/spatialGrid.d.ts +41 -0
  177. package/dist/physics/spatialGrid.js +143 -0
  178. package/dist/physics/structure.d.ts +83 -0
  179. package/dist/physics/structure.js +0 -0
  180. package/dist/physics/traversal.d.ts +75 -0
  181. package/dist/physics/traversal.js +138 -0
  182. package/dist/physics/vehicleBody.d.ts +99 -0
  183. package/dist/physics/vehicleBody.js +233 -0
  184. package/dist/puzzle/cellGrid.d.ts +26 -0
  185. package/dist/puzzle/cellGrid.js +110 -0
  186. package/dist/puzzle/fallingPiece.d.ts +24 -0
  187. package/dist/puzzle/fallingPiece.js +51 -0
  188. package/dist/random/nameGen.d.ts +18 -0
  189. package/dist/random/nameGen.js +35 -0
  190. package/dist/random/rng.d.ts +2 -0
  191. package/dist/random/rng.js +23 -0
  192. package/dist/runtime/adapter.d.ts +35 -0
  193. package/dist/runtime/adapter.js +37 -1
  194. package/dist/runtime/cameraDirector.d.ts +10 -0
  195. package/dist/runtime/cameraDirector.js +25 -0
  196. package/dist/runtime/gameContext.d.ts +107 -3
  197. package/dist/runtime/gameContext.js +253 -7
  198. package/dist/runtime/hostPersistence.d.ts +39 -0
  199. package/dist/runtime/hostPersistence.js +54 -0
  200. package/dist/runtime/inputSnapshot.d.ts +7 -0
  201. package/dist/runtime/inputSnapshot.js +12 -0
  202. package/dist/runtime/motionIntents.d.ts +25 -0
  203. package/dist/runtime/motionIntents.js +25 -0
  204. package/dist/runtime/persistenceScope.d.ts +32 -0
  205. package/dist/runtime/persistenceScope.js +64 -0
  206. package/dist/runtime/transport.d.ts +32 -0
  207. package/dist/scene/autoTarget.d.ts +15 -0
  208. package/dist/scene/autoTarget.js +54 -0
  209. package/dist/scene/behaviors.d.ts +13 -1
  210. package/dist/scene/behaviors.js +3 -0
  211. package/dist/scene/captureCheck.d.ts +7 -0
  212. package/dist/scene/captureCheck.js +11 -0
  213. package/dist/scene/entityStore.d.ts +9 -1
  214. package/dist/scene/entityStore.js +25 -2
  215. package/dist/scene/form.d.ts +40 -0
  216. package/dist/scene/form.js +56 -0
  217. package/dist/scene/mount.d.ts +62 -0
  218. package/dist/scene/mount.js +107 -0
  219. package/dist/scene/movementSpeed.d.ts +35 -0
  220. package/dist/scene/movementSpeed.js +20 -0
  221. package/dist/scene/objectQuery.d.ts +18 -0
  222. package/dist/scene/objectQuery.js +108 -0
  223. package/dist/scene/objectStore.d.ts +11 -0
  224. package/dist/scene/objectStore.js +109 -4
  225. package/dist/scene/paintLayer.d.ts +15 -0
  226. package/dist/scene/paintLayer.js +53 -0
  227. package/dist/scene/possession.d.ts +30 -0
  228. package/dist/scene/possession.js +51 -0
  229. package/dist/scene/roster.d.ts +25 -0
  230. package/dist/scene/roster.js +57 -0
  231. package/dist/scene/selection.d.ts +30 -0
  232. package/dist/scene/selection.js +38 -0
  233. package/dist/scene/spatial.d.ts +21 -0
  234. package/dist/scene/spatial.js +63 -1
  235. package/dist/scene/stationClaim.d.ts +43 -0
  236. package/dist/scene/stationClaim.js +109 -0
  237. package/dist/sensor/concealment.d.ts +21 -0
  238. package/dist/sensor/concealment.js +63 -0
  239. package/dist/sensor/freezeMonitor.d.ts +17 -0
  240. package/dist/sensor/freezeMonitor.js +35 -0
  241. package/dist/sensor/frustumSensor.d.ts +60 -0
  242. package/dist/sensor/frustumSensor.js +104 -0
  243. package/dist/sensor/hiddenStateProbe.d.ts +31 -0
  244. package/dist/sensor/hiddenStateProbe.js +40 -0
  245. package/dist/sensor/recordingBuffer.d.ts +29 -0
  246. package/dist/sensor/recordingBuffer.js +61 -0
  247. package/dist/sensor/revealQuery.d.ts +22 -0
  248. package/dist/sensor/revealQuery.js +36 -0
  249. package/dist/session/contestedChannel.d.ts +36 -0
  250. package/dist/session/contestedChannel.js +100 -0
  251. package/dist/session/extraction.d.ts +54 -0
  252. package/dist/session/extraction.js +108 -0
  253. package/dist/session/ring.d.ts +36 -0
  254. package/dist/session/ring.js +88 -0
  255. package/dist/session/roles.d.ts +6 -0
  256. package/dist/session/roles.js +32 -0
  257. package/dist/session/roundState.d.ts +79 -0
  258. package/dist/session/roundState.js +158 -0
  259. package/dist/stats/accumulatorMeter.d.ts +31 -0
  260. package/dist/stats/accumulatorMeter.js +81 -0
  261. package/dist/stats/eventMeter.d.ts +34 -0
  262. package/dist/stats/eventMeter.js +76 -0
  263. package/dist/stats/rollCheck.d.ts +15 -0
  264. package/dist/stats/rollCheck.js +15 -0
  265. package/dist/survival/decayMeter.d.ts +59 -0
  266. package/dist/survival/decayMeter.js +95 -0
  267. package/dist/survival/moodle.d.ts +48 -0
  268. package/dist/survival/moodle.js +91 -0
  269. package/dist/survival/regionHealth.d.ts +72 -0
  270. package/dist/survival/regionHealth.js +129 -0
  271. package/dist/tactics/fallingGrid.d.ts +62 -0
  272. package/dist/tactics/fallingGrid.js +158 -0
  273. package/dist/tactics/predictiveQuery.d.ts +34 -0
  274. package/dist/tactics/predictiveQuery.js +44 -0
  275. package/dist/tactics/snapshot.d.ts +18 -0
  276. package/dist/tactics/snapshot.js +65 -0
  277. package/dist/tactics/surface.d.ts +52 -0
  278. package/dist/tactics/surface.js +135 -0
  279. package/dist/tactics/tacticalGrid.d.ts +68 -0
  280. package/dist/tactics/tacticalGrid.js +261 -0
  281. package/dist/time/beatClock.d.ts +40 -0
  282. package/dist/time/beatClock.js +73 -0
  283. package/dist/time/simClock.d.ts +25 -1
  284. package/dist/time/simClock.js +28 -3
  285. package/dist/turn/commit.d.ts +27 -0
  286. package/dist/turn/commit.js +72 -0
  287. package/dist/turn/intent.d.ts +14 -0
  288. package/dist/turn/intent.js +29 -0
  289. package/dist/turn/turnLoop.d.ts +60 -0
  290. package/dist/turn/turnLoop.js +223 -0
  291. package/dist/world/buildPermissions.d.ts +47 -0
  292. package/dist/world/buildPermissions.js +134 -0
  293. package/dist/world/carve.d.ts +105 -0
  294. package/dist/world/carve.js +175 -0
  295. package/dist/world/connectors.d.ts +40 -0
  296. package/dist/world/connectors.js +67 -0
  297. package/dist/world/envField.d.ts +71 -0
  298. package/dist/world/envField.js +96 -0
  299. package/dist/world/environmentSummary.d.ts +53 -0
  300. package/dist/world/environmentSummary.js +129 -0
  301. package/dist/world/features.d.ts +87 -6
  302. package/dist/world/features.js +52 -2
  303. package/dist/world/fog.d.ts +40 -0
  304. package/dist/world/fog.js +137 -0
  305. package/dist/world/gridInstances.d.ts +13 -0
  306. package/dist/world/gridInstances.js +22 -0
  307. package/dist/world/markers.d.ts +52 -0
  308. package/dist/world/markers.js +96 -0
  309. package/dist/world/minimap.d.ts +42 -0
  310. package/dist/world/minimap.js +72 -0
  311. package/dist/world/placedStructureStore.d.ts +39 -0
  312. package/dist/world/placedStructureStore.js +95 -0
  313. package/dist/world/placementController.d.ts +69 -0
  314. package/dist/world/placementController.js +153 -0
  315. package/dist/world/realm.d.ts +61 -0
  316. package/dist/world/realm.js +66 -0
  317. package/dist/world/support.d.ts +27 -0
  318. package/dist/world/support.js +75 -0
  319. package/dist/world/terraform.d.ts +54 -0
  320. package/dist/world/terraform.js +186 -0
  321. package/dist/world/terrain.d.ts +23 -1
  322. package/dist/world/terrain.js +64 -1
  323. package/dist/world/voxelField.d.ts +54 -0
  324. package/dist/world/voxelField.js +241 -0
  325. package/dist/world/walls.d.ts +61 -0
  326. package/dist/world/walls.js +221 -0
  327. package/dist/world/weather.d.ts +92 -0
  328. package/dist/world/weather.js +169 -0
  329. package/llms.txt +4102 -0
  330. package/package.json +3 -2
@@ -0,0 +1,129 @@
1
+ import { generateBuildingDistrict } from "./buildings.js";
2
+ import { resolveTerrainField } from "./terrain.js";
3
+ export function resolveStructureBuildings(descriptor) {
4
+ const columns = Math.max(1, Math.ceil(Math.sqrt(descriptor.count)));
5
+ const rows = Math.max(1, Math.ceil(descriptor.count / columns));
6
+ const spacing = descriptor.spacing;
7
+ const [centerX, centerZ] = descriptor.position ?? [0, 0];
8
+ const origin = [
9
+ centerX - ((columns - 1) * (descriptor.footprint.w + spacing)) / 2,
10
+ centerZ - ((rows - 1) * (descriptor.footprint.d + spacing)) / 2,
11
+ ];
12
+ return generateBuildingDistrict({
13
+ rows,
14
+ columns,
15
+ origin,
16
+ lotSize: descriptor.footprint,
17
+ streetWidth: spacing,
18
+ floorRange: descriptor.stories,
19
+ base: { floorHeight: descriptor.storyHeight },
20
+ ...(descriptor.seed === undefined ? {} : { seed: descriptor.seed }),
21
+ }).slice(0, descriptor.count);
22
+ }
23
+ const TERRAIN_SAMPLE_RESOLUTION = 16;
24
+ function sampleTerrainHeights(descriptor) {
25
+ const field = resolveTerrainField(descriptor);
26
+ const halfW = descriptor.bounds.w / 2;
27
+ const halfD = descriptor.bounds.d / 2;
28
+ const steps = TERRAIN_SAMPLE_RESOLUTION;
29
+ let min = Infinity;
30
+ let max = -Infinity;
31
+ let sum = 0;
32
+ let finite = true;
33
+ for (let ix = 0; ix <= steps; ix += 1) {
34
+ for (let iz = 0; iz <= steps; iz += 1) {
35
+ const x = -halfW + (descriptor.bounds.w * ix) / steps;
36
+ const z = -halfD + (descriptor.bounds.d * iz) / steps;
37
+ const height = field.sampleHeight(x, z);
38
+ if (!Number.isFinite(height)) {
39
+ finite = false;
40
+ continue;
41
+ }
42
+ if (height < min)
43
+ min = height;
44
+ if (height > max)
45
+ max = height;
46
+ sum += height;
47
+ }
48
+ }
49
+ const count = (steps + 1) * (steps + 1);
50
+ if (!finite)
51
+ return { min: NaN, max: NaN, mean: NaN, finite: false };
52
+ return { min, max, mean: sum / count, finite: true };
53
+ }
54
+ function unionAabb(buildings) {
55
+ if (buildings.length === 0)
56
+ return { minX: 0, minZ: 0, maxX: 0, maxZ: 0 };
57
+ let minX = Infinity;
58
+ let minZ = Infinity;
59
+ let maxX = -Infinity;
60
+ let maxZ = -Infinity;
61
+ for (const building of buildings) {
62
+ if (building.bounds.minX < minX)
63
+ minX = building.bounds.minX;
64
+ if (building.bounds.minZ < minZ)
65
+ minZ = building.bounds.minZ;
66
+ if (building.bounds.maxX > maxX)
67
+ maxX = building.bounds.maxX;
68
+ if (building.bounds.maxZ > maxZ)
69
+ maxZ = building.bounds.maxZ;
70
+ }
71
+ return { minX, minZ, maxX, maxZ };
72
+ }
73
+ function summarizeStructures(descriptor) {
74
+ const buildings = resolveStructureBuildings(descriptor);
75
+ return {
76
+ style: descriptor.style,
77
+ requested: descriptor.count,
78
+ buildings: buildings.length,
79
+ parts: buildings.reduce((sum, building) => sum + building.parts.length, 0),
80
+ bounds: unionAabb(buildings),
81
+ };
82
+ }
83
+ function summarizeTerrain(descriptor) {
84
+ return {
85
+ bounds: descriptor.bounds,
86
+ height: sampleTerrainHeights(descriptor),
87
+ ...(descriptor.waterLevel === undefined ? {} : { waterLevel: descriptor.waterLevel }),
88
+ };
89
+ }
90
+ export function summarizeEnvironment(feature) {
91
+ const terrain = feature.terrain === undefined ? undefined : summarizeTerrain(feature.terrain);
92
+ const structures = (feature.structures ?? []).map(summarizeStructures);
93
+ const vegetation = (feature.vegetation ?? []).map((entry) => ({
94
+ area: entry.area,
95
+ density: entry.density,
96
+ }));
97
+ const water = (feature.water ?? []).map((entry) => ({
98
+ level: entry.level,
99
+ bounds: entry.bounds,
100
+ }));
101
+ const weather = (feature.weather ?? []).map((entry) => ({
102
+ kind: entry.kind,
103
+ density: entry.density,
104
+ }));
105
+ const buildings = structures.reduce((sum, entry) => sum + entry.buildings, 0);
106
+ const counts = {
107
+ terrain: terrain === undefined ? 0 : 1,
108
+ structureGroups: structures.length,
109
+ buildings,
110
+ buildingParts: structures.reduce((sum, entry) => sum + entry.parts, 0),
111
+ vegetationFields: vegetation.length,
112
+ waterBodies: water.length,
113
+ weatherSystems: weather.length,
114
+ };
115
+ const isEmpty = counts.terrain === 0 &&
116
+ buildings === 0 &&
117
+ counts.vegetationFields === 0 &&
118
+ counts.waterBodies === 0 &&
119
+ counts.weatherSystems === 0;
120
+ return {
121
+ ...(terrain === undefined ? {} : { terrain }),
122
+ structures,
123
+ vegetation,
124
+ water,
125
+ weather,
126
+ counts,
127
+ isEmpty,
128
+ };
129
+ }
@@ -6,17 +6,36 @@ export type EnvironmentVec2 = readonly [number, number];
6
6
  export interface EnvironmentArea extends WorldBounds {
7
7
  h?: number;
8
8
  }
9
+ export interface TerrainColors {
10
+ low?: string;
11
+ high?: string;
12
+ waterline?: string;
13
+ }
14
+ export interface TerrainFlattenMask {
15
+ center: EnvironmentVec2;
16
+ radius: number;
17
+ /** Target flat height; defaults to the noise field's height at `center`. */
18
+ height?: number;
19
+ /** Extra blend-ring width outside `radius` where the flat height smoothsteps back to the noise value; default `radius * 0.5`. */
20
+ falloff?: number;
21
+ }
9
22
  export interface TerrainEnvironmentConfig {
10
23
  bounds?: WorldBounds;
11
24
  height?: number;
12
25
  heightMap?: string;
26
+ /** Named palette preset (see `TERRAIN_MATERIAL_PALETTES` in `world/terrain`); default "grass". Overridden field-by-field by `colors`. */
13
27
  material?: string;
28
+ /** Explicit low/high/waterline hex colors; any field left unset falls back to the resolved `material` preset. */
29
+ colors?: TerrainColors;
30
+ segments?: number;
14
31
  seed?: string;
15
32
  frequency?: number;
16
33
  octaves?: number;
17
34
  ridged?: boolean;
18
35
  baseHeight?: number;
19
36
  waterLevel?: number;
37
+ /** Flat pads carved into the noise field, e.g. for building pads or spawn circles. */
38
+ flatten?: readonly TerrainFlattenMask[];
20
39
  }
21
40
  export interface RainEnvironmentConfig {
22
41
  area?: EnvironmentArea;
@@ -46,14 +65,31 @@ export interface GrassEnvironmentConfig {
46
65
  }
47
66
  export interface OceanEnvironmentConfig {
48
67
  bounds?: WorldBounds;
68
+ position?: EnvironmentVec2;
49
69
  level?: number;
50
70
  waveHeight?: number;
51
71
  waveScale?: number;
52
72
  waveSpeed?: number;
53
73
  color?: string;
54
74
  }
75
+ export interface SkyEnvironmentConfig {
76
+ /** Fixed look used when `timeOfDay` is off (or no clock is available); default "day". */
77
+ preset?: "day" | "dusk" | "night";
78
+ /** Drive sun/sky from the world clock's `calendar().dayFraction` instead of the fixed `preset`. */
79
+ timeOfDay?: boolean;
80
+ horizonColor?: string;
81
+ zenithColor?: string;
82
+ sunIntensity?: number;
83
+ ambientIntensity?: number;
84
+ fog?: {
85
+ color?: string;
86
+ near?: number;
87
+ far?: number;
88
+ };
89
+ }
55
90
  export interface BuildingEnvironmentConfig {
56
91
  count?: number;
92
+ position?: EnvironmentVec2;
57
93
  footprint?: WorldBounds;
58
94
  stories?: readonly [number, number];
59
95
  storyHeight?: number;
@@ -61,6 +97,18 @@ export interface BuildingEnvironmentConfig {
61
97
  style?: string;
62
98
  seed?: string;
63
99
  }
100
+ export type PadSize = readonly [number, number] | {
101
+ radius: number;
102
+ };
103
+ export interface PadEnvironmentConfig {
104
+ center: EnvironmentVec2;
105
+ size: PadSize;
106
+ /** Surface offset above the terrain height at `center`; default `0.05` — just proud of the ground. */
107
+ height?: number;
108
+ color?: string;
109
+ /** Rotation applied to rectangular pads; ignored for circular pads. */
110
+ rotationY?: number;
111
+ }
64
112
  export type TerrainEnvironmentDescriptor = {
65
113
  kind: "terrain";
66
114
  } & Required<Pick<TerrainEnvironmentConfig, "bounds" | "height">> & Omit<TerrainEnvironmentConfig, "bounds" | "height">;
@@ -75,10 +123,16 @@ export type GrassEnvironmentDescriptor = {
75
123
  } & Required<Pick<GrassEnvironmentConfig, "area" | "density" | "bladeHeight" | "bladeWidth" | "windStrength" | "colors">> & Pick<GrassEnvironmentConfig, "seed">;
76
124
  export type OceanEnvironmentDescriptor = {
77
125
  kind: "ocean";
78
- } & Required<Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color">>;
126
+ } & Required<Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color">> & Pick<OceanEnvironmentConfig, "position">;
79
127
  export type BuildingEnvironmentDescriptor = {
80
128
  kind: "building";
81
- } & Required<Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style">> & Pick<BuildingEnvironmentConfig, "seed">;
129
+ } & Required<Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style">> & Pick<BuildingEnvironmentConfig, "seed" | "position">;
130
+ export type PadEnvironmentDescriptor = {
131
+ kind: "pad";
132
+ } & Required<Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color">> & Pick<PadEnvironmentConfig, "rotationY">;
133
+ export type SkyEnvironmentDescriptor = {
134
+ kind: "sky";
135
+ } & Required<Pick<SkyEnvironmentConfig, "preset" | "timeOfDay">> & Omit<SkyEnvironmentConfig, "preset" | "timeOfDay">;
82
136
  export type WeatherEnvironmentDescriptor = RainEnvironmentDescriptor | SnowEnvironmentDescriptor;
83
137
  export type VegetationEnvironmentDescriptor = GrassEnvironmentDescriptor;
84
138
  export type WaterEnvironmentDescriptor = OceanEnvironmentDescriptor;
@@ -86,36 +140,59 @@ export type StructureEnvironmentDescriptor = BuildingEnvironmentDescriptor;
86
140
  export type EnvironmentDescriptorList<T> = T | readonly T[];
87
141
  export interface EnvironmentWorldConfig {
88
142
  terrain?: TerrainEnvironmentDescriptor;
143
+ sky?: SkyEnvironmentDescriptor;
89
144
  weather?: EnvironmentDescriptorList<WeatherEnvironmentDescriptor>;
90
145
  vegetation?: EnvironmentDescriptorList<VegetationEnvironmentDescriptor>;
91
146
  water?: EnvironmentDescriptorList<WaterEnvironmentDescriptor>;
92
147
  structures?: EnvironmentDescriptorList<StructureEnvironmentDescriptor>;
148
+ /** Ground pads, e.g. platforms or paved patches; each implicitly flattens the terrain beneath it. */
149
+ pads?: readonly PadEnvironmentDescriptor[];
93
150
  }
94
151
  export interface EnvironmentWorldFeature {
95
152
  kind: "environment";
96
153
  terrain?: TerrainEnvironmentDescriptor;
154
+ sky?: SkyEnvironmentDescriptor;
97
155
  weather?: readonly WeatherEnvironmentDescriptor[];
98
156
  vegetation?: readonly VegetationEnvironmentDescriptor[];
99
157
  water?: readonly WaterEnvironmentDescriptor[];
100
158
  structures?: readonly StructureEnvironmentDescriptor[];
159
+ pads?: readonly PadEnvironmentDescriptor[];
160
+ }
161
+ export interface WorldGridCell {
162
+ x: number;
163
+ z: number;
164
+ /** Extruded box height for this cell; falls back to the config's `baseHeight`, then `1`. */
165
+ height?: number;
166
+ /** Per-cell tint; falls back to the config's `defaultColor`. */
167
+ color?: string;
168
+ }
169
+ /** Shared by `biomes()`/`voxel()`/`plots()`/`tilemap()` so the shell can render their declared content as instanced boxes without a hand-written renderer. */
170
+ export interface WorldGridConfig {
171
+ cells?: readonly WorldGridCell[];
172
+ /** World-unit size of one grid cell. Default 1. */
173
+ cellSize?: number;
174
+ /** Height used for cells that omit their own `height`. Default 1. */
175
+ baseHeight?: number;
176
+ /** Color used for cells that omit their own `color`. */
177
+ defaultColor?: string;
101
178
  }
102
- export interface BiomesWorldConfig {
179
+ export interface BiomesWorldConfig extends WorldGridConfig {
103
180
  map: string;
104
181
  zones: string;
105
182
  bounds?: WorldBounds;
106
183
  }
107
- export interface VoxelWorldConfig {
184
+ export interface VoxelWorldConfig extends WorldGridConfig {
108
185
  seed: string;
109
186
  generate?: string;
110
187
  streaming?: {
111
188
  radius: number;
112
189
  };
113
190
  }
114
- export interface PlotsWorldConfig {
191
+ export interface PlotsWorldConfig extends WorldGridConfig {
115
192
  city?: string;
116
193
  interiors?: string;
117
194
  }
118
- export interface TilemapWorldConfig {
195
+ export interface TilemapWorldConfig extends WorldGridConfig {
119
196
  map: string;
120
197
  }
121
198
  export type WorldFeature = ({
@@ -129,13 +206,17 @@ export type WorldFeature = ({
129
206
  } & TilemapWorldConfig) | EnvironmentWorldFeature | {
130
207
  kind: "flat";
131
208
  };
209
+ /** Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. */
210
+ export declare function padFlattenMasks(pads: readonly PadEnvironmentDescriptor[]): readonly TerrainFlattenMask[];
132
211
  export declare function environment(config?: EnvironmentWorldConfig): EnvironmentWorldFeature;
133
212
  export declare function terrain(config?: TerrainEnvironmentConfig): TerrainEnvironmentDescriptor;
213
+ export declare function sky(config?: SkyEnvironmentConfig): SkyEnvironmentDescriptor;
134
214
  export declare function rain(config?: RainEnvironmentConfig): RainEnvironmentDescriptor;
135
215
  export declare function snow(config?: SnowEnvironmentConfig): SnowEnvironmentDescriptor;
136
216
  export declare function grass(config?: GrassEnvironmentConfig): GrassEnvironmentDescriptor;
137
217
  export declare function ocean(config?: OceanEnvironmentConfig): OceanEnvironmentDescriptor;
138
218
  export declare function building(config?: BuildingEnvironmentConfig): BuildingEnvironmentDescriptor;
219
+ export declare function pad(config: PadEnvironmentConfig): PadEnvironmentDescriptor;
139
220
  export declare function biomes(config: BiomesWorldConfig): WorldFeature;
140
221
  export declare function voxel(config: VoxelWorldConfig): WorldFeature;
141
222
  export declare function plots(config?: PlotsWorldConfig): WorldFeature;
@@ -7,18 +7,39 @@ function list(value) {
7
7
  function withOptional(base, optional) {
8
8
  return optional === undefined ? base : { ...base, ...optional };
9
9
  }
10
+ /** Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. */
11
+ export function padFlattenMasks(pads) {
12
+ return pads.map((padDescriptor) => ({
13
+ center: padDescriptor.center,
14
+ radius: padHalfExtent(padDescriptor.size) * 1.2,
15
+ }));
16
+ }
17
+ function padHalfExtent(size) {
18
+ return "radius" in size ? size.radius : Math.max(size[0], size[1]) / 2;
19
+ }
20
+ function withPadFlatten(terrainDescriptor, pads) {
21
+ if (terrainDescriptor === undefined || pads === undefined || pads.length === 0)
22
+ return terrainDescriptor;
23
+ return {
24
+ ...terrainDescriptor,
25
+ flatten: [...(terrainDescriptor.flatten ?? []), ...padFlattenMasks(pads)],
26
+ };
27
+ }
10
28
  export function environment(config = {}) {
11
29
  const weather = list(config.weather);
12
30
  const vegetation = list(config.vegetation);
13
31
  const water = list(config.water);
14
32
  const structures = list(config.structures);
33
+ const terrainDescriptor = withPadFlatten(config.terrain, config.pads);
15
34
  return {
16
35
  kind: "environment",
17
- ...(config.terrain === undefined ? {} : { terrain: config.terrain }),
36
+ ...(terrainDescriptor === undefined ? {} : { terrain: terrainDescriptor }),
37
+ ...(config.sky === undefined ? {} : { sky: config.sky }),
18
38
  ...(weather === undefined ? {} : { weather }),
19
39
  ...(vegetation === undefined ? {} : { vegetation }),
20
40
  ...(water === undefined ? {} : { water }),
21
41
  ...(structures === undefined ? {} : { structures }),
42
+ ...(config.pads === undefined ? {} : { pads: config.pads }),
22
43
  };
23
44
  }
24
45
  export function terrain(config = {}) {
@@ -29,12 +50,28 @@ export function terrain(config = {}) {
29
50
  }, {
30
51
  ...(config.heightMap === undefined ? {} : { heightMap: config.heightMap }),
31
52
  ...(config.material === undefined ? {} : { material: config.material }),
53
+ ...(config.colors === undefined ? {} : { colors: config.colors }),
54
+ ...(config.segments === undefined ? {} : { segments: config.segments }),
32
55
  ...(config.seed === undefined ? {} : { seed: config.seed }),
33
56
  ...(config.frequency === undefined ? {} : { frequency: config.frequency }),
34
57
  ...(config.octaves === undefined ? {} : { octaves: config.octaves }),
35
58
  ...(config.ridged === undefined ? {} : { ridged: config.ridged }),
36
59
  ...(config.baseHeight === undefined ? {} : { baseHeight: config.baseHeight }),
37
60
  ...(config.waterLevel === undefined ? {} : { waterLevel: config.waterLevel }),
61
+ ...(config.flatten === undefined ? {} : { flatten: config.flatten }),
62
+ });
63
+ }
64
+ export function sky(config = {}) {
65
+ return withOptional({
66
+ kind: "sky",
67
+ preset: config.preset ?? "day",
68
+ timeOfDay: config.timeOfDay ?? false,
69
+ }, {
70
+ ...(config.horizonColor === undefined ? {} : { horizonColor: config.horizonColor }),
71
+ ...(config.zenithColor === undefined ? {} : { zenithColor: config.zenithColor }),
72
+ ...(config.sunIntensity === undefined ? {} : { sunIntensity: config.sunIntensity }),
73
+ ...(config.ambientIntensity === undefined ? {} : { ambientIntensity: config.ambientIntensity }),
74
+ ...(config.fog === undefined ? {} : { fog: config.fog }),
38
75
  });
39
76
  }
40
77
  export function rain(config = {}) {
@@ -80,6 +117,7 @@ export function ocean(config = {}) {
80
117
  waveScale: config.waveScale ?? 18,
81
118
  waveSpeed: config.waveSpeed ?? 0.55,
82
119
  color: config.color ?? "#1d7fa3",
120
+ ...(config.position === undefined ? {} : { position: config.position }),
83
121
  };
84
122
  }
85
123
  export function building(config = {}) {
@@ -91,7 +129,19 @@ export function building(config = {}) {
91
129
  storyHeight: config.storyHeight ?? 3,
92
130
  spacing: config.spacing ?? 2,
93
131
  style: config.style ?? "generic",
94
- }, config.seed === undefined ? undefined : { seed: config.seed });
132
+ }, {
133
+ ...(config.seed === undefined ? {} : { seed: config.seed }),
134
+ ...(config.position === undefined ? {} : { position: config.position }),
135
+ });
136
+ }
137
+ export function pad(config) {
138
+ return withOptional({
139
+ kind: "pad",
140
+ center: config.center,
141
+ size: config.size,
142
+ height: config.height ?? 0.05,
143
+ color: config.color ?? "#8b8680",
144
+ }, config.rotationY === undefined ? undefined : { rotationY: config.rotationY });
95
145
  }
96
146
  export function biomes(config) {
97
147
  return { kind: "biomes", ...config };
@@ -0,0 +1,40 @@
1
+ export interface FogBounds {
2
+ minX: number;
3
+ minZ: number;
4
+ maxX: number;
5
+ maxZ: number;
6
+ }
7
+ export interface FogConfig {
8
+ bounds: FogBounds;
9
+ cellSize: number;
10
+ /** Cells revealed on construction (start-of-run known area). */
11
+ revealed?: readonly number[];
12
+ }
13
+ export interface FogCells {
14
+ cols: number;
15
+ rows: number;
16
+ minX: number;
17
+ minZ: number;
18
+ cellSize: number;
19
+ /** Row-major flags; index = row * cols + col. */
20
+ revealed: readonly boolean[];
21
+ revealedCount: number;
22
+ }
23
+ /**
24
+ * Reveal-on-event fog of war over a fixed grid. Walking (`revealAlong`) and
25
+ * digging/acting (`reveal`) clear cells; once revealed a cell stays revealed.
26
+ * Pure and renderer-free — the shell/react map draws `cells()`.
27
+ */
28
+ export interface FogField {
29
+ reveal(x: number, z: number, radius?: number): number;
30
+ revealAlong(from: readonly [number, number], to: readonly [number, number], radius?: number): number;
31
+ isRevealed(x: number, z: number): boolean;
32
+ isRevealedCell(col: number, row: number): boolean;
33
+ revealedCount(): number;
34
+ cellCount(): number;
35
+ fraction(): number;
36
+ cells(): FogCells;
37
+ reset(): void;
38
+ subscribe(listener: () => void): () => void;
39
+ }
40
+ export declare function createFogField(config: FogConfig): FogField;
@@ -0,0 +1,137 @@
1
+ function clampInt(value, min, max) {
2
+ const rounded = Math.floor(value);
3
+ return rounded < min ? min : rounded > max ? max : rounded;
4
+ }
5
+ export function createFogField(config) {
6
+ const { minX, minZ, maxX, maxZ } = config.bounds;
7
+ const cellSize = config.cellSize;
8
+ if (cellSize <= 0)
9
+ throw new Error("createFogField: cellSize must be positive");
10
+ const cols = Math.max(1, Math.ceil((maxX - minX) / cellSize));
11
+ const rows = Math.max(1, Math.ceil((maxZ - minZ) / cellSize));
12
+ const total = cols * rows;
13
+ const revealed = new Array(total).fill(false);
14
+ let revealedCount = 0;
15
+ const listeners = new Set();
16
+ let snapshotCache = null;
17
+ function colOf(x) {
18
+ return clampInt((x - minX) / cellSize, 0, cols - 1);
19
+ }
20
+ function rowOf(z) {
21
+ return clampInt((z - minZ) / cellSize, 0, rows - 1);
22
+ }
23
+ function markCell(col, row) {
24
+ if (col < 0 || col >= cols || row < 0 || row >= rows)
25
+ return false;
26
+ const index = row * cols + col;
27
+ if (revealed[index])
28
+ return false;
29
+ revealed[index] = true;
30
+ revealedCount += 1;
31
+ return true;
32
+ }
33
+ function notify(changed) {
34
+ if (changed === 0)
35
+ return;
36
+ snapshotCache = null;
37
+ for (const listener of listeners)
38
+ listener();
39
+ }
40
+ function revealAt(x, z, radius) {
41
+ const centerCol = colOf(x);
42
+ const centerRow = rowOf(z);
43
+ let changed = markCell(centerCol, centerRow) ? 1 : 0;
44
+ if (radius <= 0)
45
+ return changed;
46
+ const span = Math.ceil(radius / cellSize);
47
+ const radiusSq = radius * radius;
48
+ for (let dr = -span; dr <= span; dr += 1) {
49
+ for (let dc = -span; dc <= span; dc += 1) {
50
+ if (dc === 0 && dr === 0)
51
+ continue;
52
+ const cellX = minX + (centerCol + dc + 0.5) * cellSize;
53
+ const cellZ = minZ + (centerRow + dr + 0.5) * cellSize;
54
+ const dx = cellX - x;
55
+ const dz = cellZ - z;
56
+ if (dx * dx + dz * dz > radiusSq)
57
+ continue;
58
+ if (markCell(centerCol + dc, centerRow + dr))
59
+ changed += 1;
60
+ }
61
+ }
62
+ return changed;
63
+ }
64
+ if (config.revealed !== undefined) {
65
+ for (const index of config.revealed) {
66
+ if (index >= 0 && index < total && !revealed[index]) {
67
+ revealed[index] = true;
68
+ revealedCount += 1;
69
+ }
70
+ }
71
+ snapshotCache = null;
72
+ }
73
+ return {
74
+ reveal(x, z, radius = 0) {
75
+ const changed = revealAt(x, z, radius);
76
+ notify(changed);
77
+ return changed;
78
+ },
79
+ revealAlong(from, to, radius = cellSize) {
80
+ const dx = to[0] - from[0];
81
+ const dz = to[1] - from[1];
82
+ const distance = Math.hypot(dx, dz);
83
+ const steps = Math.max(1, Math.ceil(distance / cellSize));
84
+ let changed = 0;
85
+ for (let step = 0; step <= steps; step += 1) {
86
+ const t = step / steps;
87
+ changed += revealAt(from[0] + dx * t, from[1] + dz * t, radius);
88
+ }
89
+ notify(changed);
90
+ return changed;
91
+ },
92
+ isRevealed(x, z) {
93
+ return revealed[rowOf(z) * cols + colOf(x)] === true;
94
+ },
95
+ isRevealedCell(col, row) {
96
+ if (col < 0 || col >= cols || row < 0 || row >= rows)
97
+ return false;
98
+ return revealed[row * cols + col] === true;
99
+ },
100
+ revealedCount() {
101
+ return revealedCount;
102
+ },
103
+ cellCount() {
104
+ return total;
105
+ },
106
+ fraction() {
107
+ return total === 0 ? 0 : revealedCount / total;
108
+ },
109
+ cells() {
110
+ if (snapshotCache === null) {
111
+ snapshotCache = {
112
+ cols,
113
+ rows,
114
+ minX,
115
+ minZ,
116
+ cellSize,
117
+ revealed: revealed.slice(),
118
+ revealedCount,
119
+ };
120
+ }
121
+ return snapshotCache;
122
+ },
123
+ reset() {
124
+ if (revealedCount === 0)
125
+ return;
126
+ revealed.fill(false);
127
+ revealedCount = 0;
128
+ notify(1);
129
+ },
130
+ subscribe(listener) {
131
+ listeners.add(listener);
132
+ return () => {
133
+ listeners.delete(listener);
134
+ };
135
+ },
136
+ };
137
+ }
@@ -0,0 +1,13 @@
1
+ import type { WorldGridConfig } from "./features.js";
2
+ export interface GridInstanceTransform {
3
+ position: readonly [number, number, number];
4
+ scale: readonly [number, number, number];
5
+ color: string;
6
+ }
7
+ /**
8
+ * Pure cell→instance math for `biomes()`/`voxel()`/`plots()`/`tilemap()` world features: one
9
+ * extruded box per declared cell, grounded at y=0 and centered on its cell in x/z. Consumed by
10
+ * the shell's grid-world renderer so it can drive a single `THREE.InstancedMesh` without
11
+ * duplicating this math per game.
12
+ */
13
+ export declare function resolveGridInstances(config: WorldGridConfig): readonly GridInstanceTransform[];
@@ -0,0 +1,22 @@
1
+ const DEFAULT_CELL_SIZE = 1;
2
+ const DEFAULT_BASE_HEIGHT = 1;
3
+ const DEFAULT_GRID_COLOR = "#8a8f98";
4
+ /**
5
+ * Pure cell→instance math for `biomes()`/`voxel()`/`plots()`/`tilemap()` world features: one
6
+ * extruded box per declared cell, grounded at y=0 and centered on its cell in x/z. Consumed by
7
+ * the shell's grid-world renderer so it can drive a single `THREE.InstancedMesh` without
8
+ * duplicating this math per game.
9
+ */
10
+ export function resolveGridInstances(config) {
11
+ const cells = config.cells ?? [];
12
+ const cellSize = config.cellSize ?? DEFAULT_CELL_SIZE;
13
+ const defaultColor = config.defaultColor ?? DEFAULT_GRID_COLOR;
14
+ return cells.map((cell) => {
15
+ const height = cell.height ?? config.baseHeight ?? DEFAULT_BASE_HEIGHT;
16
+ return {
17
+ position: [cell.x * cellSize, height / 2, cell.z * cellSize],
18
+ scale: [cellSize, height, cellSize],
19
+ color: cell.color ?? defaultColor,
20
+ };
21
+ });
22
+ }
@@ -0,0 +1,52 @@
1
+ export type MarkerPosition = readonly [number, number, number];
2
+ export interface MapMarker<TMeta = unknown> {
3
+ id: string;
4
+ kind: string;
5
+ position: MarkerPosition;
6
+ label?: string;
7
+ owner?: string;
8
+ createdAt: number;
9
+ expiresAt?: number;
10
+ meta?: TMeta;
11
+ }
12
+ export interface MarkerInput<TMeta = unknown> {
13
+ id?: string;
14
+ kind: string;
15
+ position: MarkerPosition;
16
+ label?: string;
17
+ owner?: string;
18
+ createdAt?: number;
19
+ expiresAt?: number;
20
+ meta?: TMeta;
21
+ }
22
+ export interface MarkerQuery {
23
+ kind?: string;
24
+ owner?: string;
25
+ near?: MarkerPosition;
26
+ radius?: number;
27
+ }
28
+ /**
29
+ * Visual descriptor for a marker kind. Games supply their own palette; the
30
+ * engine ships `DEFAULT_MARKER_KINDS` as a content-agnostic starting set that
31
+ * the react minimap/compass read for colors and glyphs.
32
+ */
33
+ export interface MarkerKindStyle {
34
+ id: string;
35
+ color: string;
36
+ glyph: string;
37
+ priority: number;
38
+ }
39
+ export declare const DEFAULT_MARKER_KINDS: Record<string, MarkerKindStyle>;
40
+ export declare function markerKindStyle(kind: string, styles?: Record<string, MarkerKindStyle>): MarkerKindStyle;
41
+ export interface MarkerSet<TMeta = unknown> {
42
+ add(marker: MarkerInput<TMeta>): string;
43
+ remove(id: string): boolean;
44
+ get(id: string): MapMarker<TMeta> | null;
45
+ list(): readonly MapMarker<TMeta>[];
46
+ query(query: MarkerQuery): MapMarker<TMeta>[];
47
+ prune(now: number): number;
48
+ clear(): void;
49
+ subscribe(listener: () => void): () => void;
50
+ snapshot(): readonly MapMarker<TMeta>[];
51
+ }
52
+ export declare function createMarkerSet<TMeta = unknown>(now?: () => number): MarkerSet<TMeta>;