@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,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
+ }
@@ -0,0 +1,61 @@
1
+ import { type Aabb, type Vec2 } from "./geometry.js";
2
+ export type WallVec3 = readonly [number, number, number];
3
+ export interface WallSegment {
4
+ from: Vec2;
5
+ to: Vec2;
6
+ length: number;
7
+ angle: number;
8
+ }
9
+ export interface EnclosedFootprint {
10
+ polygon: readonly Vec2[];
11
+ aabb: Aabb;
12
+ area: number;
13
+ perimeter: number;
14
+ closed: boolean;
15
+ }
16
+ export type RoofStyle = "hip" | "gable" | "flat";
17
+ export interface RoofPlan {
18
+ style: RoofStyle;
19
+ ridge: readonly [Vec2, Vec2];
20
+ apexHeight: number;
21
+ eaveHeight: number;
22
+ faces: readonly (readonly WallVec3[])[];
23
+ }
24
+ export interface RoofConfig {
25
+ style?: RoofStyle;
26
+ eaveHeight?: number;
27
+ pitch?: number;
28
+ overhang?: number;
29
+ }
30
+ export declare function wallSegments(points: readonly Vec2[], closed: boolean): WallSegment[];
31
+ export declare function isEnclosed(points: readonly Vec2[], tolerance?: number): boolean;
32
+ export declare function enclosePath(points: readonly Vec2[], tolerance?: number): readonly Vec2[];
33
+ export declare function polygonArea(polygon: readonly Vec2[]): number;
34
+ export declare function footprintFromWalls(points: readonly Vec2[], tolerance?: number): EnclosedFootprint | null;
35
+ export declare function autoRoof(footprint: EnclosedFootprint, config?: RoofConfig): RoofPlan;
36
+ export type PaintTarget = "floor" | "wall";
37
+ export interface SurfacePaintStore {
38
+ paint(target: PaintTarget, key: string, surface: string): void;
39
+ clear(target: PaintTarget, key: string): void;
40
+ get(target: PaintTarget, key: string): string | null;
41
+ entries(target: PaintTarget): readonly (readonly [string, string])[];
42
+ snapshot(): Record<PaintTarget, Record<string, string>>;
43
+ restore(snapshot: Record<PaintTarget, Record<string, string>>): void;
44
+ }
45
+ export declare function createSurfacePaint(): SurfacePaintStore;
46
+ export interface WallDrawTool {
47
+ addPoint(point: Vec2, snap?: number): Vec2;
48
+ undo(): void;
49
+ close(): void;
50
+ clear(): void;
51
+ points(): readonly Vec2[];
52
+ segments(): WallSegment[];
53
+ isClosed(): boolean;
54
+ footprint(): EnclosedFootprint | null;
55
+ roof(config?: RoofConfig): RoofPlan | null;
56
+ }
57
+ export declare function createWallDrawTool(config?: {
58
+ snap?: number;
59
+ closeTolerance?: number;
60
+ }): WallDrawTool;
61
+ export declare function wallSegmentBounds(segment: WallSegment, thickness: number): Aabb;
@@ -0,0 +1,221 @@
1
+ import { footprintAabb } from "./geometry.js";
2
+ export function wallSegments(points, closed) {
3
+ const segments = [];
4
+ const count = closed ? points.length : points.length - 1;
5
+ for (let index = 0; index < count; index += 1) {
6
+ const from = points[index];
7
+ const to = points[(index + 1) % points.length];
8
+ const dx = to[0] - from[0];
9
+ const dz = to[1] - from[1];
10
+ segments.push({ from, to, length: Math.hypot(dx, dz), angle: Math.atan2(dz, dx) });
11
+ }
12
+ return segments;
13
+ }
14
+ export function isEnclosed(points, tolerance = 0.5) {
15
+ if (points.length < 3)
16
+ return false;
17
+ const first = points[0];
18
+ const last = points[points.length - 1];
19
+ return Math.hypot(first[0] - last[0], first[1] - last[1]) <= tolerance;
20
+ }
21
+ export function enclosePath(points, tolerance = 0.5) {
22
+ if (points.length < 3)
23
+ return points;
24
+ if (!isEnclosed(points, tolerance))
25
+ return points;
26
+ return points.slice(0, points.length - 1);
27
+ }
28
+ export function polygonArea(polygon) {
29
+ let sum = 0;
30
+ for (let index = 0; index < polygon.length; index += 1) {
31
+ const a = polygon[index];
32
+ const b = polygon[(index + 1) % polygon.length];
33
+ sum += a[0] * b[1] - b[0] * a[1];
34
+ }
35
+ return Math.abs(sum) / 2;
36
+ }
37
+ export function footprintFromWalls(points, tolerance = 0.5) {
38
+ const closed = isEnclosed(points, tolerance);
39
+ const polygon = enclosePath(points, tolerance);
40
+ if (polygon.length < 3)
41
+ return null;
42
+ let minX = Infinity;
43
+ let minZ = Infinity;
44
+ let maxX = -Infinity;
45
+ let maxZ = -Infinity;
46
+ let perimeter = 0;
47
+ for (let index = 0; index < polygon.length; index += 1) {
48
+ const point = polygon[index];
49
+ if (point[0] < minX)
50
+ minX = point[0];
51
+ if (point[0] > maxX)
52
+ maxX = point[0];
53
+ if (point[1] < minZ)
54
+ minZ = point[1];
55
+ if (point[1] > maxZ)
56
+ maxZ = point[1];
57
+ const next = polygon[(index + 1) % polygon.length];
58
+ perimeter += Math.hypot(next[0] - point[0], next[1] - point[1]);
59
+ }
60
+ return {
61
+ polygon,
62
+ aabb: { minX, minZ, maxX, maxZ },
63
+ area: polygonArea(polygon),
64
+ perimeter,
65
+ closed,
66
+ };
67
+ }
68
+ export function autoRoof(footprint, config = {}) {
69
+ const style = config.style ?? "hip";
70
+ const eaveHeight = config.eaveHeight ?? 3;
71
+ const pitch = config.pitch ?? 0.5;
72
+ const overhang = config.overhang ?? 0.4;
73
+ const aabb = footprint.aabb;
74
+ const minX = aabb.minX - overhang;
75
+ const maxX = aabb.maxX + overhang;
76
+ const minZ = aabb.minZ - overhang;
77
+ const maxZ = aabb.maxZ + overhang;
78
+ const width = maxX - minX;
79
+ const depth = maxZ - minZ;
80
+ const cx = (minX + maxX) / 2;
81
+ const cz = (minZ + maxZ) / 2;
82
+ const ridgeAlongX = width >= depth;
83
+ const halfSpan = (ridgeAlongX ? depth : width) / 2;
84
+ const apexHeight = style === "flat" ? eaveHeight : eaveHeight + halfSpan * pitch;
85
+ if (style === "flat") {
86
+ const face = [
87
+ [minX, eaveHeight, minZ],
88
+ [maxX, eaveHeight, minZ],
89
+ [maxX, eaveHeight, maxZ],
90
+ [minX, eaveHeight, maxZ],
91
+ ];
92
+ const flatRidge = [
93
+ [minX, cz],
94
+ [maxX, cz],
95
+ ];
96
+ return { style, ridge: flatRidge, apexHeight, eaveHeight, faces: [face] };
97
+ }
98
+ const ridge = ridgeAlongX
99
+ ? [
100
+ [minX + (style === "hip" ? halfSpan : 0), cz],
101
+ [maxX - (style === "hip" ? halfSpan : 0), cz],
102
+ ]
103
+ : [
104
+ [cx, minZ + (style === "hip" ? halfSpan : 0)],
105
+ [cx, maxZ - (style === "hip" ? halfSpan : 0)],
106
+ ];
107
+ const r0 = [ridge[0][0], apexHeight, ridge[0][1]];
108
+ const r1 = [ridge[1][0], apexHeight, ridge[1][1]];
109
+ const corners = {
110
+ nw: [minX, eaveHeight, minZ],
111
+ ne: [maxX, eaveHeight, minZ],
112
+ se: [maxX, eaveHeight, maxZ],
113
+ sw: [minX, eaveHeight, maxZ],
114
+ };
115
+ const faces = ridgeAlongX
116
+ ? [
117
+ [corners.nw, corners.ne, r1, r0],
118
+ [corners.se, corners.sw, r0, r1],
119
+ ]
120
+ : [
121
+ [corners.ne, corners.se, r1, r0],
122
+ [corners.sw, corners.nw, r0, r1],
123
+ ];
124
+ if (style === "hip") {
125
+ faces.push(ridgeAlongX
126
+ ? [corners.sw, corners.nw, r0]
127
+ : [corners.nw, corners.ne, r0]);
128
+ faces.push(ridgeAlongX
129
+ ? [corners.ne, corners.se, r1]
130
+ : [corners.se, corners.sw, r1]);
131
+ }
132
+ return { style, ridge, apexHeight, eaveHeight, faces };
133
+ }
134
+ export function createSurfacePaint() {
135
+ const maps = {
136
+ floor: new Map(),
137
+ wall: new Map(),
138
+ };
139
+ return {
140
+ paint(target, key, surface) {
141
+ maps[target].set(key, surface);
142
+ },
143
+ clear(target, key) {
144
+ maps[target].delete(key);
145
+ },
146
+ get(target, key) {
147
+ return maps[target].get(key) ?? null;
148
+ },
149
+ entries(target) {
150
+ return Array.from(maps[target].entries());
151
+ },
152
+ snapshot() {
153
+ return {
154
+ floor: Object.fromEntries(maps.floor),
155
+ wall: Object.fromEntries(maps.wall),
156
+ };
157
+ },
158
+ restore(snapshot) {
159
+ maps.floor = new Map(Object.entries(snapshot.floor ?? {}));
160
+ maps.wall = new Map(Object.entries(snapshot.wall ?? {}));
161
+ },
162
+ };
163
+ }
164
+ function snapPoint(point, grid) {
165
+ if (!(grid > 0))
166
+ return point;
167
+ return [Math.round(point[0] / grid) * grid, Math.round(point[1] / grid) * grid];
168
+ }
169
+ export function createWallDrawTool(config = {}) {
170
+ const defaultSnap = config.snap ?? 0;
171
+ const closeTolerance = config.closeTolerance ?? 0.5;
172
+ let pts = [];
173
+ let closed = false;
174
+ return {
175
+ addPoint(point, snap) {
176
+ const grid = snap ?? defaultSnap;
177
+ const next = snapPoint(point, grid);
178
+ if (pts.length >= 3 && Math.hypot(next[0] - pts[0][0], next[1] - pts[0][1]) <= closeTolerance) {
179
+ closed = true;
180
+ return pts[0];
181
+ }
182
+ pts.push(next);
183
+ return next;
184
+ },
185
+ undo() {
186
+ pts.pop();
187
+ closed = false;
188
+ },
189
+ close() {
190
+ if (pts.length >= 3)
191
+ closed = true;
192
+ },
193
+ clear() {
194
+ pts = [];
195
+ closed = false;
196
+ },
197
+ points() {
198
+ return pts;
199
+ },
200
+ segments() {
201
+ return wallSegments(pts, closed);
202
+ },
203
+ isClosed() {
204
+ return closed;
205
+ },
206
+ footprint() {
207
+ if (!closed && !isEnclosed(pts, closeTolerance))
208
+ return null;
209
+ return footprintFromWalls(pts, closeTolerance);
210
+ },
211
+ roof(roofConfig) {
212
+ const fp = this.footprint();
213
+ return fp === null ? null : autoRoof(fp, roofConfig);
214
+ },
215
+ };
216
+ }
217
+ export function wallSegmentBounds(segment, thickness) {
218
+ const center = [(segment.from[0] + segment.to[0]) / 2, (segment.from[1] + segment.to[1]) / 2];
219
+ const quarterTurns = Math.abs(Math.round(segment.angle / (Math.PI / 2))) % 2;
220
+ return footprintAabb(center, { w: segment.length, d: thickness }, quarterTurns);
221
+ }
@@ -0,0 +1,92 @@
1
+ import type { WindVector } from "./wind.js";
2
+ export type WeatherKind = string;
3
+ export interface WeatherState {
4
+ /** Game-defined kind: "clear", "rain", "storm", "snow", "fog", "sandstorm". */
5
+ kind: WeatherKind;
6
+ /** 0..1 severity — scales every modifier. */
7
+ intensity: number;
8
+ wind?: WindVector;
9
+ }
10
+ /** Per-kind gameplay effect values at full intensity. Intensity interpolates from neutral. */
11
+ export interface WeatherModifier {
12
+ /** Movement traction multiplier at full intensity — <1 = mud/ice slows, 1 = normal. */
13
+ grip?: number;
14
+ /** View distance multiplier at full intensity — 0..1, 1 = clear. */
15
+ visibility?: number;
16
+ /** Structure HP lost per game-second at full intensity (hail, storm). */
17
+ structureDamage?: number;
18
+ /** Temperature delta at full intensity (a blizzard chills). */
19
+ chill?: number;
20
+ /** Lightning ignition chance per game-second at full intensity. */
21
+ ignition?: number;
22
+ /** Fire spread-rate multiplier at full intensity — rain suppresses (<1). Default neutral 1. */
23
+ spread?: number;
24
+ }
25
+ export type WeatherModifierTable = Record<WeatherKind, WeatherModifier>;
26
+ export interface ResolvedWeather {
27
+ grip: number;
28
+ visibility: number;
29
+ structureDamage: number;
30
+ chill: number;
31
+ ignition: number;
32
+ spread: number;
33
+ }
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;
42
+ export type FireCellState = "unburnt" | "burning" | "burnt";
43
+ export interface FireCell {
44
+ fuel: number;
45
+ heat: number;
46
+ state: FireCellState;
47
+ }
48
+ export interface FireGridConfig {
49
+ cols: number;
50
+ rows: number;
51
+ cellSize: number;
52
+ /** World x,z of the center of cell (0,0). Default [0,0]. */
53
+ origin?: readonly [number, number];
54
+ /** Initial fuel 0..1 per cell; default 1 everywhere. */
55
+ fuelAt?: (col: number, row: number) => number;
56
+ /** Accumulated heat needed to ignite a fuelled cell. Default 1. */
57
+ ignitionThreshold?: number;
58
+ /** Heat pushed to each neighbour per second from a burning cell. Default 0.6. */
59
+ spreadRate?: number;
60
+ /** Fuel consumed per second while burning. Default 0.25. */
61
+ burnRate?: number;
62
+ /** Prevailing wind biasing spread direction. */
63
+ wind?: WindVector;
64
+ /** How strongly wind biases downwind vs upwind spread, 0..1. Default 0.6. */
65
+ windBias?: number;
66
+ }
67
+ export interface FireStepOptions {
68
+ /** Global spread multiplier this step (rain suppression from resolveWeather().spread). Default 1. */
69
+ spread?: number;
70
+ /** Per-cell wetness 0..1 that resists ignition — from an environment field. */
71
+ wetnessAt?: (col: number, row: number) => number;
72
+ }
73
+ export interface FireGrid {
74
+ readonly cols: number;
75
+ readonly rows: number;
76
+ /** Advance the cellular propagation by game-time `dt`. */
77
+ step(dt: number, options?: FireStepOptions): void;
78
+ igniteCell(col: number, row: number): void;
79
+ /** Ignite the cell under a world position; no-op if outside the grid. */
80
+ ignite(x: number, z: number): boolean;
81
+ cell(col: number, row: number): FireCell;
82
+ cellAt(x: number, z: number): FireCell | null;
83
+ cellCoord(x: number, z: number): {
84
+ col: number;
85
+ row: number;
86
+ } | null;
87
+ /** Count of currently burning cells. */
88
+ readonly burning: number;
89
+ /** Flat snapshot (row-major) for rendering. */
90
+ snapshot(): FireCell[];
91
+ }
92
+ export declare function createFireGrid(config: FireGridConfig): FireGrid;