@jgengine/core 0.7.0 → 0.9.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 (329) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +2 -0
  3. package/dist/ai/flock.d.ts +36 -0
  4. package/dist/ai/flock.js +182 -0
  5. package/dist/ai/groupAssist.d.ts +19 -0
  6. package/dist/ai/groupAssist.js +59 -0
  7. package/dist/ai/laneSelect.d.ts +21 -0
  8. package/dist/ai/laneSelect.js +37 -0
  9. package/dist/ai/mobBrain.d.ts +68 -0
  10. package/dist/ai/mobBrain.js +115 -0
  11. package/dist/ai/spawnDirector.d.ts +5 -0
  12. package/dist/ai/spawnDirector.js +14 -2
  13. package/dist/ai/threat.d.ts +2 -0
  14. package/dist/ai/threat.js +34 -0
  15. package/dist/anim/easing.d.ts +14 -0
  16. package/dist/anim/easing.js +55 -0
  17. package/dist/behaviour/behaviour.d.ts +136 -0
  18. package/dist/behaviour/behaviour.js +388 -0
  19. package/dist/cards/cardPile.d.ts +1 -0
  20. package/dist/cards/cardPile.js +12 -3
  21. package/dist/cartridge/runtime.d.ts +15 -0
  22. package/dist/cartridge/runtime.js +556 -0
  23. package/dist/cartridge/spec.d.ts +188 -0
  24. package/dist/cartridge/spec.js +19 -0
  25. package/dist/cartridge/validate.d.ts +2 -0
  26. package/dist/cartridge/validate.js +78 -0
  27. package/dist/combat/abilityKit.d.ts +22 -2
  28. package/dist/combat/abilityKit.js +72 -13
  29. package/dist/combat/castRunner.d.ts +40 -0
  30. package/dist/combat/castRunner.js +56 -0
  31. package/dist/combat/comboPoints.d.ts +14 -0
  32. package/dist/combat/comboPoints.js +52 -0
  33. package/dist/combat/defensiveWindow.d.ts +1 -1
  34. package/dist/combat/defensiveWindow.js +5 -3
  35. package/dist/combat/effects.d.ts +1 -1
  36. package/dist/combat/effects.js +9 -6
  37. package/dist/combat/projectiles.d.ts +64 -6
  38. package/dist/combat/projectiles.js +232 -74
  39. package/dist/combat/resistance.d.ts +16 -4
  40. package/dist/combat/resistance.js +43 -7
  41. package/dist/combat/resourcePool.d.ts +20 -0
  42. package/dist/combat/resourcePool.js +55 -0
  43. package/dist/combat/shotOrigin.d.ts +40 -0
  44. package/dist/combat/shotOrigin.js +57 -0
  45. package/dist/combat/telegraph.d.ts +30 -1
  46. package/dist/combat/telegraph.js +61 -0
  47. package/dist/commands/commandRegistry.d.ts +1 -1
  48. package/dist/commands/commandRegistry.js +2 -1
  49. package/dist/crafting/recipe.js +4 -1
  50. package/dist/data/dataSource.d.ts +26 -0
  51. package/dist/data/dataSource.js +93 -0
  52. package/dist/data/devProxy.d.ts +11 -0
  53. package/dist/data/devProxy.js +38 -0
  54. package/dist/data/fetchJson.d.ts +20 -0
  55. package/dist/data/fetchJson.js +43 -0
  56. package/dist/data/jsonDataSource.d.ts +4 -0
  57. package/dist/data/jsonDataSource.js +6 -0
  58. package/dist/devtools/devtools.d.ts +205 -0
  59. package/dist/devtools/devtools.js +832 -0
  60. package/dist/devtools/transformTunables.d.ts +14 -0
  61. package/dist/devtools/transformTunables.js +38 -0
  62. package/dist/devtools/tunableSchema.d.ts +140 -0
  63. package/dist/devtools/tunableSchema.js +476 -0
  64. package/dist/faction/factions.d.ts +33 -0
  65. package/dist/faction/factions.js +76 -0
  66. package/dist/faction/reputation.d.ts +32 -0
  67. package/dist/faction/reputation.js +82 -0
  68. package/dist/game/chat.d.ts +68 -0
  69. package/dist/game/chat.js +173 -0
  70. package/dist/game/chatFilter.d.ts +15 -0
  71. package/dist/game/chatFilter.js +55 -0
  72. package/dist/game/controlGate.d.ts +4 -0
  73. package/dist/game/controlGate.js +7 -0
  74. package/dist/game/defineGame.d.ts +10 -2
  75. package/dist/game/defineGame.js +2 -1
  76. package/dist/game/events.d.ts +35 -0
  77. package/dist/game/gamePhase.d.ts +20 -0
  78. package/dist/game/gamePhase.js +21 -0
  79. package/dist/game/levelSequence.d.ts +44 -0
  80. package/dist/game/levelSequence.js +78 -0
  81. package/dist/game/lootTable.d.ts +10 -1
  82. package/dist/game/lootTable.js +27 -7
  83. package/dist/game/playableGame.d.ts +286 -6
  84. package/dist/game/playableGame.js +6 -1
  85. package/dist/game/progression.d.ts +1 -0
  86. package/dist/game/progression.js +23 -2
  87. package/dist/game/quest.d.ts +7 -3
  88. package/dist/game/quest.js +17 -8
  89. package/dist/game/race.d.ts +44 -1
  90. package/dist/game/race.js +116 -11
  91. package/dist/game/recordBook.d.ts +29 -0
  92. package/dist/game/recordBook.js +86 -0
  93. package/dist/game/social.d.ts +42 -0
  94. package/dist/game/social.js +122 -0
  95. package/dist/game/spawnPoints.d.ts +19 -0
  96. package/dist/game/spawnPoints.js +23 -0
  97. package/dist/game/talents.d.ts +47 -0
  98. package/dist/game/talents.js +140 -0
  99. package/dist/game/worldItem.d.ts +3 -0
  100. package/dist/game/worldItem.js +12 -6
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/input/actionBindings.d.ts +10 -0
  104. package/dist/input/actionBindings.js +14 -0
  105. package/dist/input/axisInput.d.ts +26 -3
  106. package/dist/input/axisInput.js +44 -4
  107. package/dist/input/bindingOverrides.d.ts +25 -0
  108. package/dist/input/bindingOverrides.js +95 -0
  109. package/dist/input/gestureSurface.d.ts +31 -0
  110. package/dist/input/gestureSurface.js +120 -0
  111. package/dist/input/pointer.d.ts +45 -0
  112. package/dist/input/pointer.js +48 -0
  113. package/dist/input/pointerAxis.d.ts +30 -0
  114. package/dist/input/pointerAxis.js +31 -0
  115. package/dist/input/touchScheme.d.ts +89 -0
  116. package/dist/input/touchScheme.js +153 -0
  117. package/dist/item/affix.d.ts +1 -1
  118. package/dist/item/affix.js +1 -19
  119. package/dist/meta/changelog.d.ts +1 -1
  120. package/dist/meta/changelog.js +59 -1
  121. package/dist/movement/dash.d.ts +3 -0
  122. package/dist/movement/dash.js +19 -5
  123. package/dist/movement/glideModel.d.ts +44 -0
  124. package/dist/movement/glideModel.js +54 -0
  125. package/dist/movement/grappleSwing.d.ts +34 -0
  126. package/dist/movement/grappleSwing.js +56 -0
  127. package/dist/movement/leaderTrail.d.ts +29 -0
  128. package/dist/movement/leaderTrail.js +67 -0
  129. package/dist/movement/movementModel.d.ts +36 -3
  130. package/dist/movement/movementModel.js +81 -5
  131. package/dist/movement/steering.d.ts +27 -0
  132. package/dist/movement/steering.js +40 -0
  133. package/dist/movement/voxelController.d.ts +61 -0
  134. package/dist/movement/voxelController.js +229 -0
  135. package/dist/multiplayer/chatContract.d.ts +42 -0
  136. package/dist/multiplayer/chatContract.js +57 -0
  137. package/dist/multiplayer/feedWriteGate.d.ts +10 -0
  138. package/dist/multiplayer/feedWriteGate.js +12 -0
  139. package/dist/multiplayer/identity.d.ts +13 -0
  140. package/dist/multiplayer/identity.js +20 -0
  141. package/dist/multiplayer/poseSyncGate.d.ts +1 -0
  142. package/dist/multiplayer/poseSyncGate.js +13 -1
  143. package/dist/multiplayer/presenceModel.d.ts +17 -2
  144. package/dist/multiplayer/presenceModel.js +30 -4
  145. package/dist/multiplayer/voiceContract.d.ts +44 -0
  146. package/dist/multiplayer/voiceContract.js +108 -0
  147. package/dist/nav/corridors.d.ts +30 -0
  148. package/dist/nav/corridors.js +69 -0
  149. package/dist/nav/navConstrain.d.ts +14 -0
  150. package/dist/nav/navConstrain.js +26 -0
  151. package/dist/nav/navFromEnvironment.d.ts +11 -0
  152. package/dist/nav/navFromEnvironment.js +16 -0
  153. package/dist/nav/navGrid.d.ts +10 -0
  154. package/dist/nav/navGrid.js +48 -5
  155. package/dist/nav/pathFollow.d.ts +4 -2
  156. package/dist/nav/pathFollow.js +5 -3
  157. package/dist/nav/railGraph.d.ts +66 -0
  158. package/dist/nav/railGraph.js +129 -0
  159. package/dist/nav/timetable.d.ts +41 -0
  160. package/dist/nav/timetable.js +94 -0
  161. package/dist/physics/ballisticSweep.d.ts +18 -0
  162. package/dist/physics/ballisticSweep.js +49 -0
  163. package/dist/physics/buoyancy.d.ts +6 -0
  164. package/dist/physics/buoyancy.js +14 -0
  165. package/dist/physics/flowTube.d.ts +32 -0
  166. package/dist/physics/flowTube.js +64 -0
  167. package/dist/physics/forceVolume.d.ts +27 -0
  168. package/dist/physics/forceVolume.js +43 -5
  169. package/dist/physics/kinematicVehicle.d.ts +52 -0
  170. package/dist/physics/kinematicVehicle.js +81 -0
  171. package/dist/physics/physicsWorld.d.ts +54 -8
  172. package/dist/physics/physicsWorld.js +400 -79
  173. package/dist/physics/ragdoll.js +1 -1
  174. package/dist/physics/spatialGrid.d.ts +1 -1
  175. package/dist/physics/spatialGrid.js +5 -0
  176. package/dist/physics/vehicleBody.d.ts +6 -0
  177. package/dist/physics/vehicleBody.js +18 -18
  178. package/dist/puzzle/cellGrid.d.ts +26 -0
  179. package/dist/puzzle/cellGrid.js +110 -0
  180. package/dist/puzzle/fallingPiece.d.ts +24 -0
  181. package/dist/puzzle/fallingPiece.js +51 -0
  182. package/dist/random/nameGen.d.ts +18 -0
  183. package/dist/random/nameGen.js +35 -0
  184. package/dist/random/rng.d.ts +2 -0
  185. package/dist/random/rng.js +23 -0
  186. package/dist/random/seedLink.d.ts +5 -0
  187. package/dist/random/seedLink.js +35 -0
  188. package/dist/runtime/adapter.d.ts +35 -0
  189. package/dist/runtime/adapter.js +37 -1
  190. package/dist/runtime/cameraDirector.d.ts +15 -0
  191. package/dist/runtime/cameraDirector.js +31 -0
  192. package/dist/runtime/commandRunner.d.ts +2 -2
  193. package/dist/runtime/commandRunner.js +2 -2
  194. package/dist/runtime/gameContext.d.ts +65 -2
  195. package/dist/runtime/gameContext.js +280 -9
  196. package/dist/runtime/gameRuntime.d.ts +5 -1
  197. package/dist/runtime/gameRuntime.js +13 -12
  198. package/dist/runtime/hostPersistence.d.ts +30 -1
  199. package/dist/runtime/hostPersistence.js +70 -5
  200. package/dist/runtime/inputSnapshot.d.ts +12 -0
  201. package/dist/runtime/inputSnapshot.js +17 -0
  202. package/dist/runtime/motionIntents.d.ts +33 -0
  203. package/dist/runtime/motionIntents.js +43 -0
  204. package/dist/runtime/snapshot.d.ts +1 -0
  205. package/dist/runtime/snapshot.js +1 -1
  206. package/dist/runtime/transport.d.ts +32 -0
  207. package/dist/runtime/visibility.d.ts +74 -0
  208. package/dist/runtime/visibility.js +127 -0
  209. package/dist/scene/colliders.d.ts +44 -0
  210. package/dist/scene/colliders.js +77 -0
  211. package/dist/scene/entityStore.d.ts +30 -5
  212. package/dist/scene/entityStore.js +83 -13
  213. package/dist/scene/movementSpeed.d.ts +35 -0
  214. package/dist/scene/movementSpeed.js +20 -0
  215. package/dist/scene/objectQuery.d.ts +29 -0
  216. package/dist/scene/objectQuery.js +140 -0
  217. package/dist/scene/objectStore.d.ts +13 -0
  218. package/dist/scene/objectStore.js +130 -6
  219. package/dist/scene/paintLayer.d.ts +15 -0
  220. package/dist/scene/paintLayer.js +53 -0
  221. package/dist/scene/sceneRaycast.d.ts +80 -0
  222. package/dist/scene/sceneRaycast.js +284 -0
  223. package/dist/scene/spatial.d.ts +17 -1
  224. package/dist/scene/spatial.js +140 -17
  225. package/dist/sensor/concealment.d.ts +21 -0
  226. package/dist/sensor/concealment.js +63 -0
  227. package/dist/sensor/freezeMonitor.d.ts +17 -0
  228. package/dist/sensor/freezeMonitor.js +35 -0
  229. package/dist/sensor/recordingBuffer.d.ts +6 -0
  230. package/dist/sensor/recordingBuffer.js +7 -0
  231. package/dist/sensor/replayLoop.d.ts +39 -0
  232. package/dist/sensor/replayLoop.js +64 -0
  233. package/dist/sensor/visionCone.d.ts +31 -0
  234. package/dist/sensor/visionCone.js +72 -0
  235. package/dist/session/extraction.d.ts +2 -2
  236. package/dist/session/extraction.js +13 -2
  237. package/dist/session/roles.d.ts +6 -0
  238. package/dist/session/roles.js +32 -0
  239. package/dist/session/roundState.d.ts +41 -17
  240. package/dist/session/roundState.js +67 -38
  241. package/dist/settings/settingsModel.d.ts +97 -0
  242. package/dist/settings/settingsModel.js +100 -0
  243. package/dist/stats/statModifiers.d.ts +5 -2
  244. package/dist/stats/statModifiers.js +32 -14
  245. package/dist/store/observableKeyedStore.js +8 -2
  246. package/dist/survival/decayMeter.d.ts +1 -1
  247. package/dist/survival/decayMeter.js +0 -1
  248. package/dist/tactics/fallingGrid.d.ts +62 -0
  249. package/dist/tactics/fallingGrid.js +158 -0
  250. package/dist/tactics/tacticalGrid.d.ts +11 -0
  251. package/dist/tactics/tacticalGrid.js +17 -0
  252. package/dist/time/beatClock.d.ts +18 -0
  253. package/dist/time/beatClock.js +24 -0
  254. package/dist/time/idleProgress.d.ts +29 -0
  255. package/dist/time/idleProgress.js +31 -0
  256. package/dist/time/serverTick.d.ts +7 -1
  257. package/dist/time/serverTick.js +27 -4
  258. package/dist/time/simClock.d.ts +25 -1
  259. package/dist/time/simClock.js +28 -3
  260. package/dist/time/stateSchedule.d.ts +57 -0
  261. package/dist/time/stateSchedule.js +121 -0
  262. package/dist/turn/commit.d.ts +2 -2
  263. package/dist/turn/commit.js +2 -2
  264. package/dist/turn/intent.d.ts +14 -0
  265. package/dist/turn/intent.js +29 -0
  266. package/dist/turn/turnLoop.d.ts +2 -0
  267. package/dist/turn/turnLoop.js +8 -1
  268. package/dist/ui/hudLayout.d.ts +58 -0
  269. package/dist/ui/hudLayout.js +207 -0
  270. package/dist/ui/hudScale.d.ts +44 -0
  271. package/dist/ui/hudScale.js +55 -0
  272. package/dist/visibility/assetStreaming.d.ts +66 -0
  273. package/dist/visibility/assetStreaming.js +201 -0
  274. package/dist/visibility/bounds.d.ts +66 -0
  275. package/dist/visibility/bounds.js +113 -0
  276. package/dist/visibility/camera.d.ts +21 -0
  277. package/dist/visibility/camera.js +3 -0
  278. package/dist/visibility/config.d.ts +21 -0
  279. package/dist/visibility/config.js +1 -0
  280. package/dist/visibility/diagnostics.d.ts +26 -0
  281. package/dist/visibility/diagnostics.js +36 -0
  282. package/dist/visibility/distance.d.ts +11 -0
  283. package/dist/visibility/distance.js +30 -0
  284. package/dist/visibility/frustum.d.ts +47 -0
  285. package/dist/visibility/frustum.js +186 -0
  286. package/dist/visibility/occlusion.d.ts +36 -0
  287. package/dist/visibility/occlusion.js +45 -0
  288. package/dist/visibility/settings.d.ts +75 -0
  289. package/dist/visibility/settings.js +49 -0
  290. package/dist/visibility/simulationCulling.d.ts +34 -0
  291. package/dist/visibility/simulationCulling.js +36 -0
  292. package/dist/visibility/spatialIndex.d.ts +45 -0
  293. package/dist/visibility/spatialIndex.js +198 -0
  294. package/dist/visibility/visibilitySystem.d.ts +90 -0
  295. package/dist/visibility/visibilitySystem.js +257 -0
  296. package/dist/world/buildings.d.ts +6 -0
  297. package/dist/world/buildings.js +152 -0
  298. package/dist/world/carve.d.ts +7 -2
  299. package/dist/world/carve.js +17 -7
  300. package/dist/world/cellStates.d.ts +45 -0
  301. package/dist/world/cellStates.js +103 -0
  302. package/dist/world/environmentSummary.d.ts +61 -0
  303. package/dist/world/environmentSummary.js +138 -0
  304. package/dist/world/features.d.ts +138 -9
  305. package/dist/world/features.js +73 -2
  306. package/dist/world/gridInstances.d.ts +12 -0
  307. package/dist/world/gridInstances.js +160 -0
  308. package/dist/world/lod.d.ts +17 -0
  309. package/dist/world/lod.js +66 -0
  310. package/dist/world/mapLayers.d.ts +60 -0
  311. package/dist/world/mapLayers.js +44 -0
  312. package/dist/world/massing.d.ts +45 -0
  313. package/dist/world/massing.js +581 -0
  314. package/dist/world/minimap.d.ts +13 -1
  315. package/dist/world/minimap.js +21 -2
  316. package/dist/world/placementController.d.ts +22 -1
  317. package/dist/world/placementController.js +56 -0
  318. package/dist/world/terrain.d.ts +62 -1
  319. package/dist/world/terrain.js +253 -9
  320. package/dist/world/voxelField.d.ts +54 -0
  321. package/dist/world/voxelField.js +241 -0
  322. package/dist/world/water.d.ts +7 -0
  323. package/dist/world/water.js +19 -4
  324. package/dist/world/weather.d.ts +3 -18
  325. package/dist/world/weather.js +4 -17
  326. package/dist/world/windZones.d.ts +47 -0
  327. package/dist/world/windZones.js +59 -0
  328. package/llms.txt +4983 -0
  329. package/package.json +14 -3
@@ -0,0 +1,241 @@
1
+ export const VOXEL_FACES = ["px", "nx", "py", "ny", "pz", "nz"];
2
+ export const VOXEL_FACE_NORMALS = {
3
+ px: [1, 0, 0],
4
+ nx: [-1, 0, 0],
5
+ py: [0, 1, 0],
6
+ ny: [0, -1, 0],
7
+ pz: [0, 0, 1],
8
+ nz: [0, 0, -1],
9
+ };
10
+ function cellKey(x, y, z) {
11
+ return `${x},${y},${z}`;
12
+ }
13
+ function entryFace(dx, dy, dz) {
14
+ const ax = Math.abs(dx);
15
+ const ay = Math.abs(dy);
16
+ const az = Math.abs(dz);
17
+ if (ax >= ay && ax >= az && ax > 0)
18
+ return dx > 0 ? "nx" : "px";
19
+ if (az >= ay && az > 0)
20
+ return dz > 0 ? "nz" : "pz";
21
+ return dy > 0 ? "ny" : "py";
22
+ }
23
+ export function createVoxelField(config) {
24
+ const chunkSize = config?.chunkSize ?? 16;
25
+ if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
26
+ throw new Error("createVoxelField: chunkSize must be a positive integer");
27
+ }
28
+ const cells = new Map();
29
+ const chunkVersions = new Map();
30
+ const listeners = new Set();
31
+ function chunkOf(x, y, z) {
32
+ return {
33
+ cx: Math.floor(x / chunkSize),
34
+ cy: Math.floor(y / chunkSize),
35
+ cz: Math.floor(z / chunkSize),
36
+ };
37
+ }
38
+ function bumpChunk(x, y, z) {
39
+ const { cx, cy, cz } = chunkOf(x, y, z);
40
+ const key = cellKey(cx, cy, cz);
41
+ chunkVersions.set(key, (chunkVersions.get(key) ?? 0) + 1);
42
+ }
43
+ function notify() {
44
+ for (const listener of listeners)
45
+ listener();
46
+ }
47
+ function setCell(x, y, z, type) {
48
+ const key = cellKey(x, y, z);
49
+ const existing = cells.get(key);
50
+ if (existing !== undefined && existing.type === type)
51
+ return false;
52
+ cells.set(key, { x, y, z, type });
53
+ bumpChunk(x, y, z);
54
+ return true;
55
+ }
56
+ function getCell(x, y, z) {
57
+ return cells.get(cellKey(x, y, z));
58
+ }
59
+ function computeBounds() {
60
+ if (cells.size === 0)
61
+ return null;
62
+ let minX = Number.POSITIVE_INFINITY;
63
+ let minY = Number.POSITIVE_INFINITY;
64
+ let minZ = Number.POSITIVE_INFINITY;
65
+ let maxX = Number.NEGATIVE_INFINITY;
66
+ let maxY = Number.NEGATIVE_INFINITY;
67
+ let maxZ = Number.NEGATIVE_INFINITY;
68
+ for (const cell of cells.values()) {
69
+ if (cell.x < minX)
70
+ minX = cell.x;
71
+ if (cell.y < minY)
72
+ minY = cell.y;
73
+ if (cell.z < minZ)
74
+ minZ = cell.z;
75
+ if (cell.x > maxX)
76
+ maxX = cell.x;
77
+ if (cell.y > maxY)
78
+ maxY = cell.y;
79
+ if (cell.z > maxZ)
80
+ maxZ = cell.z;
81
+ }
82
+ return { min: [minX, minY, minZ], max: [maxX, maxY, maxZ] };
83
+ }
84
+ return {
85
+ set(x, y, z, type) {
86
+ const changed = setCell(x, y, z, type);
87
+ if (changed)
88
+ notify();
89
+ return changed;
90
+ },
91
+ remove(x, y, z) {
92
+ const removed = cells.delete(cellKey(x, y, z));
93
+ if (removed) {
94
+ bumpChunk(x, y, z);
95
+ notify();
96
+ }
97
+ return removed;
98
+ },
99
+ get(x, y, z) {
100
+ return getCell(x, y, z)?.type ?? null;
101
+ },
102
+ has(x, y, z) {
103
+ return cells.has(cellKey(x, y, z));
104
+ },
105
+ fill(min, max, type) {
106
+ let changed = 0;
107
+ for (let x = min[0]; x <= max[0]; x += 1) {
108
+ for (let y = min[1]; y <= max[1]; y += 1) {
109
+ for (let z = min[2]; z <= max[2]; z += 1) {
110
+ if (setCell(x, y, z, type))
111
+ changed += 1;
112
+ }
113
+ }
114
+ }
115
+ if (changed > 0)
116
+ notify();
117
+ return changed;
118
+ },
119
+ clear() {
120
+ if (cells.size === 0)
121
+ return;
122
+ for (const cell of cells.values())
123
+ bumpChunk(cell.x, cell.y, cell.z);
124
+ cells.clear();
125
+ notify();
126
+ },
127
+ count() {
128
+ return cells.size;
129
+ },
130
+ cells() {
131
+ return cells.values();
132
+ },
133
+ bounds() {
134
+ return computeBounds();
135
+ },
136
+ neighbors(x, y, z) {
137
+ const out = [];
138
+ for (const face of VOXEL_FACES) {
139
+ const [nx, ny, nz] = VOXEL_FACE_NORMALS[face];
140
+ const cell = getCell(x + nx, y + ny, z + nz);
141
+ if (cell !== undefined)
142
+ out.push(cell);
143
+ }
144
+ return out;
145
+ },
146
+ exposedFaces(x, y, z) {
147
+ if (!cells.has(cellKey(x, y, z)))
148
+ return [];
149
+ return VOXEL_FACES.filter((face) => {
150
+ const [nx, ny, nz] = VOXEL_FACE_NORMALS[face];
151
+ return !cells.has(cellKey(x + nx, y + ny, z + nz));
152
+ });
153
+ },
154
+ raycast(origin, direction, maxDistance) {
155
+ const [ox, oy, oz] = origin;
156
+ const [dx, dy, dz] = direction;
157
+ let ix = Math.floor(ox);
158
+ let iy = Math.floor(oy);
159
+ let iz = Math.floor(oz);
160
+ const startCell = getCell(ix, iy, iz);
161
+ if (startCell !== undefined) {
162
+ return {
163
+ x: ix,
164
+ y: iy,
165
+ z: iz,
166
+ type: startCell.type,
167
+ face: entryFace(dx, dy, dz),
168
+ adjacent: [ix, iy, iz],
169
+ distance: 0,
170
+ };
171
+ }
172
+ const stepX = dx > 0 ? 1 : dx < 0 ? -1 : 0;
173
+ const stepY = dy > 0 ? 1 : dy < 0 ? -1 : 0;
174
+ const stepZ = dz > 0 ? 1 : dz < 0 ? -1 : 0;
175
+ const tDeltaX = dx !== 0 ? Math.abs(1 / dx) : Number.POSITIVE_INFINITY;
176
+ const tDeltaY = dy !== 0 ? Math.abs(1 / dy) : Number.POSITIVE_INFINITY;
177
+ const tDeltaZ = dz !== 0 ? Math.abs(1 / dz) : Number.POSITIVE_INFINITY;
178
+ const boundary = (i, o, step) => (step > 0 ? i + 1 - o : o - i);
179
+ let tMaxX = dx !== 0 ? boundary(ix, ox, stepX) * tDeltaX : Number.POSITIVE_INFINITY;
180
+ let tMaxY = dy !== 0 ? boundary(iy, oy, stepY) * tDeltaY : Number.POSITIVE_INFINITY;
181
+ let tMaxZ = dz !== 0 ? boundary(iz, oz, stepZ) * tDeltaZ : Number.POSITIVE_INFINITY;
182
+ let face = "py";
183
+ let travelled = 0;
184
+ while (travelled <= maxDistance) {
185
+ let px = ix;
186
+ let py = iy;
187
+ let pz = iz;
188
+ if (tMaxX < tMaxY && tMaxX < tMaxZ) {
189
+ ix += stepX;
190
+ travelled = tMaxX;
191
+ tMaxX += tDeltaX;
192
+ face = stepX > 0 ? "nx" : "px";
193
+ }
194
+ else if (tMaxY < tMaxZ) {
195
+ iy += stepY;
196
+ travelled = tMaxY;
197
+ tMaxY += tDeltaY;
198
+ face = stepY > 0 ? "ny" : "py";
199
+ }
200
+ else {
201
+ iz += stepZ;
202
+ travelled = tMaxZ;
203
+ tMaxZ += tDeltaZ;
204
+ face = stepZ > 0 ? "nz" : "pz";
205
+ }
206
+ if (travelled > maxDistance)
207
+ break;
208
+ const cell = getCell(ix, iy, iz);
209
+ if (cell !== undefined) {
210
+ return {
211
+ x: ix,
212
+ y: iy,
213
+ z: iz,
214
+ type: cell.type,
215
+ face,
216
+ adjacent: [px, py, pz],
217
+ distance: travelled,
218
+ };
219
+ }
220
+ }
221
+ return null;
222
+ },
223
+ chunkOf,
224
+ chunkVersion(cx, cy, cz) {
225
+ return chunkVersions.get(cellKey(cx, cy, cz)) ?? 0;
226
+ },
227
+ subscribe(listener) {
228
+ listeners.add(listener);
229
+ return () => {
230
+ listeners.delete(listener);
231
+ };
232
+ },
233
+ summary() {
234
+ const types = {};
235
+ for (const cell of cells.values()) {
236
+ types[cell.type] = (types[cell.type] ?? 0) + 1;
237
+ }
238
+ return { blocks: cells.size, types, bounds: computeBounds() };
239
+ },
240
+ };
241
+ }
@@ -9,6 +9,8 @@ export interface GerstnerWave {
9
9
  }
10
10
  export interface WaterSurfaceConfig {
11
11
  level?: number;
12
+ /** Scheduled base level as a function of time — tides, floods (#284.1). Wins over `level`/`setLevel` whenever set. */
13
+ levelAt?: (time: number) => number;
12
14
  waveHeight?: number;
13
15
  waveScale?: number;
14
16
  waveSpeed?: number;
@@ -16,11 +18,16 @@ export interface WaterSurfaceConfig {
16
18
  choppiness?: number;
17
19
  }
18
20
  export interface WaterSurface {
21
+ /** The current base level — the initial config value until `setLevel` replaces it. With a `levelAt` schedule, use `levelAtTime` instead. */
19
22
  readonly level: number;
20
23
  readonly waves: readonly GerstnerWave[];
21
24
  height(x: number, z: number, time: number): number;
22
25
  normal(x: number, z: number, time: number): WaterNormal;
23
26
  displace(x: number, z: number, time: number): readonly [number, number, number];
27
+ /** Raise or drain the water at runtime (#284.1) — every `height`/`displace` consumer (buoyancy included) sees it immediately. Ignored while a `levelAt` schedule is set. */
28
+ setLevel(level: number): void;
29
+ /** The effective base level at `time`: the `levelAt` schedule when set, else the current `level`. */
30
+ levelAtTime(time: number): number;
24
31
  }
25
32
  export declare function synthesizeWaves(config?: WaterSurfaceConfig): GerstnerWave[];
26
33
  export declare function waterSurface(config?: WaterSurfaceConfig): WaterSurface;
@@ -41,11 +41,13 @@ export function synthesizeWaves(config = {}) {
41
41
  }
42
42
  export function waterSurface(config = {}) {
43
43
  const resolved = resolveConfig(config);
44
- const level = resolved.level;
44
+ let currentLevel = resolved.level;
45
+ const levelAt = config.levelAt;
46
+ const levelAtTime = (time) => (levelAt !== undefined ? levelAt(time) : currentLevel);
45
47
  const choppiness = resolved.choppiness;
46
48
  const waves = synthesizeWaves(config);
47
49
  const height = (x, z, time) => {
48
- let y = level;
50
+ let y = levelAtTime(time);
49
51
  for (const wave of waves) {
50
52
  const k = TWO_PI / wave.wavelength;
51
53
  const omega = wave.speed * k;
@@ -57,7 +59,7 @@ export function waterSurface(config = {}) {
57
59
  const displace = (x, z, time) => {
58
60
  let dx = x;
59
61
  let dz = z;
60
- let dy = level;
62
+ let dy = levelAtTime(time);
61
63
  for (const wave of waves) {
62
64
  const k = TWO_PI / wave.wavelength;
63
65
  const omega = wave.speed * k;
@@ -112,11 +114,24 @@ export function waterSurface(config = {}) {
112
114
  }
113
115
  return [nx, ny, nz];
114
116
  };
115
- return { level, waves, height, normal, displace };
117
+ return {
118
+ get level() {
119
+ return currentLevel;
120
+ },
121
+ waves,
122
+ height,
123
+ normal,
124
+ displace,
125
+ setLevel(nextLevel) {
126
+ currentLevel = nextLevel;
127
+ },
128
+ levelAtTime,
129
+ };
116
130
  }
117
131
  export function waterSurfaceFromDescriptor(descriptor, waves) {
118
132
  return waterSurface({
119
133
  level: descriptor.level,
134
+ ...(descriptor.levelAt === undefined ? {} : { levelAt: descriptor.levelAt }),
120
135
  waveHeight: descriptor.waveHeight,
121
136
  waveScale: descriptor.waveScale,
122
137
  waveSpeed: descriptor.waveSpeed,
@@ -1,28 +1,20 @@
1
1
  import type { WindVector } from "./wind.js";
2
2
  export type WeatherKind = string;
3
3
  export interface WeatherState {
4
- /** Game-defined kind: "clear", "rain", "storm", "snow", "fog", "sandstorm". */
5
4
  kind: WeatherKind;
6
- /** 0..1 severity — scales every modifier. */
7
5
  intensity: number;
8
6
  wind?: WindVector;
9
7
  }
10
- /** Per-kind gameplay effect values at full intensity. Intensity interpolates from neutral. */
11
8
  export interface WeatherModifier {
12
- /** Movement traction multiplier at full intensity — <1 = mud/ice slows, 1 = normal. */
13
9
  grip?: number;
14
- /** View distance multiplier at full intensity — 0..1, 1 = clear. */
15
10
  visibility?: number;
16
- /** Structure HP lost per game-second at full intensity (hail, storm). */
17
11
  structureDamage?: number;
18
- /** Temperature delta at full intensity (a blizzard chills). */
19
12
  chill?: number;
20
- /** Lightning ignition chance per game-second at full intensity. */
21
13
  ignition?: number;
22
- /** Fire spread-rate multiplier at full intensity — rain suppresses (<1). Default neutral 1. */
23
14
  spread?: number;
24
15
  }
25
- export type WeatherModifierTable = Record<WeatherKind, WeatherModifier>;
16
+ export type WeatherModifierTable<K extends string = string> = Record<K, WeatherModifier>;
17
+ export type WeatherKindOf<TTable extends WeatherModifierTable> = Extract<keyof TTable, string>;
26
18
  export interface ResolvedWeather {
27
19
  grip: number;
28
20
  visibility: number;
@@ -31,14 +23,7 @@ export interface ResolvedWeather {
31
23
  ignition: number;
32
24
  spread: number;
33
25
  }
34
- /**
35
- * Resolve a weather state against a game-owned modifier table into concrete gameplay
36
- * numbers. Multiplier-style effects (grip, visibility, spread) interpolate from the
37
- * neutral 1 by intensity; rate-style effects (structureDamage, ignition, chill) scale
38
- * linearly. Games read `grip`/`visibility` in movement and AI, `structureDamage` on a
39
- * building tick, and `spread`/`ignition` feed the fire grid.
40
- */
41
- export declare function resolveWeather(state: WeatherState, table: WeatherModifierTable): ResolvedWeather;
26
+ export declare function resolveWeather<TTable extends WeatherModifierTable>(state: WeatherState, table: TTable): ResolvedWeather;
42
27
  export type FireCellState = "unburnt" | "burning" | "burnt";
43
28
  export interface FireCell {
44
29
  fuel: number;
@@ -1,25 +1,12 @@
1
- const NEUTRAL = {
2
- grip: 1,
3
- visibility: 1,
4
- structureDamage: 0,
5
- chill: 0,
6
- ignition: 0,
7
- spread: 1,
8
- };
9
1
  function lerp(from, to, t) {
10
2
  return from + (to - from) * t;
11
3
  }
12
- /**
13
- * Resolve a weather state against a game-owned modifier table into concrete gameplay
14
- * numbers. Multiplier-style effects (grip, visibility, spread) interpolate from the
15
- * neutral 1 by intensity; rate-style effects (structureDamage, ignition, chill) scale
16
- * linearly. Games read `grip`/`visibility` in movement and AI, `structureDamage` on a
17
- * building tick, and `spread`/`ignition` feed the fire grid.
18
- */
19
4
  export function resolveWeather(state, table) {
20
5
  const entry = table[state.kind];
21
- if (entry === undefined)
22
- return { ...NEUTRAL };
6
+ if (entry === undefined) {
7
+ const known = Object.keys(table).sort().join(", ");
8
+ throw new Error(`Unknown weather kind "${state.kind}". Known kinds: ${known.length > 0 ? known : "(none)"}.`);
9
+ }
23
10
  const t = Math.max(0, Math.min(1, state.intensity));
24
11
  return {
25
12
  grip: lerp(1, entry.grip ?? 1, t),
@@ -0,0 +1,47 @@
1
+ import { type SchedulePhase } from "../time/stateSchedule.js";
2
+ import type { WindField, WindVector } from "./wind.js";
3
+ export interface WindZoneState {
4
+ direction: WindVector;
5
+ speed: number;
6
+ /** Announcement label for HUD countdowns ("gale", "calm", "tailwind"). */
7
+ label?: string;
8
+ }
9
+ export interface WindZoneConfig {
10
+ id: string;
11
+ center: readonly [number, number];
12
+ radius: number;
13
+ /** The zone's scheduled wind states; a single phase makes a static zone. */
14
+ phases: readonly SchedulePhase<WindZoneState>[];
15
+ /** Staggers this zone's schedule against identical ones. */
16
+ offsetSeconds?: number;
17
+ }
18
+ export interface WindZonesConfig {
19
+ zones: readonly WindZoneConfig[];
20
+ /** Wind outside every zone; omit for dead air. */
21
+ ambient?: WindField;
22
+ }
23
+ export interface WindShiftForecast {
24
+ zoneId: string;
25
+ /** Absolute time the zone's next state begins. */
26
+ at: number;
27
+ /** Seconds from the queried time until the shift. */
28
+ inSeconds: number;
29
+ next: WindZoneState;
30
+ }
31
+ /**
32
+ * Named, discrete wind zones over an ambient field — each zone runs a deterministic
33
+ * `StateSchedule` of wind states, so games get scheduled shifts with advance announcement
34
+ * (`forecastShift` drives the "gale in 12s" countdown) instead of one continuous field.
35
+ */
36
+ export interface WindZones {
37
+ zoneIds(): readonly string[];
38
+ /** Innermost zone containing the point (later declarations win ties); `null` in open air. */
39
+ zoneAt(x: number, z: number): string | null;
40
+ /** The wind vector at a point and time: the containing zone's scheduled state, else ambient. */
41
+ windAt(x: number, z: number, t: number): WindVector;
42
+ /** A zone's live state; `null` for an unknown zone. */
43
+ stateAt(zoneId: string, t: number): WindZoneState | null;
44
+ /** The zone's next scheduled shift after `t`; `null` for unknown or single-phase zones. */
45
+ forecastShift(zoneId: string, t: number): WindShiftForecast | null;
46
+ }
47
+ export declare function createWindZones(config: WindZonesConfig): WindZones;
@@ -0,0 +1,59 @@
1
+ import { createStateSchedule } from "../time/stateSchedule.js";
2
+ function windVectorOf(state) {
3
+ const length = Math.hypot(state.direction[0], state.direction[1]);
4
+ if (length === 0 || state.speed === 0)
5
+ return [0, 0];
6
+ return [(state.direction[0] / length) * state.speed, (state.direction[1] / length) * state.speed];
7
+ }
8
+ export function createWindZones(config) {
9
+ const zones = new Map();
10
+ const order = [];
11
+ for (const zone of config.zones) {
12
+ if (zones.has(zone.id))
13
+ throw new Error(`duplicate wind zone: ${zone.id}`);
14
+ zones.set(zone.id, {
15
+ config: zone,
16
+ schedule: createStateSchedule({
17
+ phases: zone.phases,
18
+ ...(zone.offsetSeconds === undefined ? {} : { offsetSeconds: zone.offsetSeconds }),
19
+ }),
20
+ });
21
+ order.push(zone.id);
22
+ }
23
+ function zoneAt(x, z) {
24
+ let best = null;
25
+ let bestRadius = Number.POSITIVE_INFINITY;
26
+ for (const id of order) {
27
+ const zone = zones.get(id);
28
+ const distance = Math.hypot(x - zone.config.center[0], z - zone.config.center[1]);
29
+ if (distance <= zone.config.radius && zone.config.radius <= bestRadius) {
30
+ best = id;
31
+ bestRadius = zone.config.radius;
32
+ }
33
+ }
34
+ return best;
35
+ }
36
+ return {
37
+ zoneIds: () => order.slice(),
38
+ zoneAt,
39
+ windAt(x, z, t) {
40
+ const id = zoneAt(x, z);
41
+ if (id !== null)
42
+ return windVectorOf(zones.get(id).schedule.stateAt(t));
43
+ return config.ambient === undefined ? [0, 0] : config.ambient.atPoint(x, z, t);
44
+ },
45
+ stateAt(zoneId, t) {
46
+ const zone = zones.get(zoneId);
47
+ return zone === undefined ? null : zone.schedule.stateAt(t);
48
+ },
49
+ forecastShift(zoneId, t) {
50
+ const zone = zones.get(zoneId);
51
+ if (zone === undefined || zone.config.phases.length < 2)
52
+ return null;
53
+ const at = zone.schedule.nextTransitionAt(t);
54
+ if (!Number.isFinite(at))
55
+ return null;
56
+ return { zoneId, at, inSeconds: at - t, next: zone.schedule.stateAt(at) };
57
+ },
58
+ };
59
+ }