@jgengine/core 0.10.0 → 0.11.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 (607) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/LICENSE +203 -661
  3. package/README.md +1 -1
  4. package/dist/ai/crowd.d.ts +55 -0
  5. package/dist/ai/crowd.js +114 -0
  6. package/dist/ai/flock.d.ts +5 -2
  7. package/dist/ai/flock.js +5 -2
  8. package/dist/ai/groupAssist.d.ts +1 -0
  9. package/dist/ai/groupAssist.js +1 -0
  10. package/dist/ai/heatSystem.d.ts +76 -0
  11. package/dist/ai/heatSystem.js +97 -0
  12. package/dist/ai/interestScheduler.d.ts +107 -0
  13. package/dist/ai/interestScheduler.js +118 -0
  14. package/dist/ai/jobBoard.d.ts +1 -0
  15. package/dist/ai/jobBoard.js +1 -0
  16. package/dist/ai/laneSelect.d.ts +5 -2
  17. package/dist/ai/laneSelect.js +5 -2
  18. package/dist/ai/mobBrain.d.ts +1 -0
  19. package/dist/ai/mobBrain.js +1 -0
  20. package/dist/ai/spawnDirector.d.ts +7 -2
  21. package/dist/ai/spawnDirector.js +18 -15
  22. package/dist/ai/spawnPoint.d.ts +3 -1
  23. package/dist/ai/spawnPoint.js +3 -1
  24. package/dist/ai/targetAcquisition.d.ts +84 -0
  25. package/dist/ai/targetAcquisition.js +92 -0
  26. package/dist/anim/easing.d.ts +19 -0
  27. package/dist/anim/easing.js +19 -0
  28. package/dist/anim/oscillator.d.ts +12 -0
  29. package/dist/anim/oscillator.js +25 -0
  30. package/dist/area/areaEffectField.d.ts +127 -0
  31. package/dist/area/areaEffectField.js +175 -0
  32. package/dist/area/stackPolicy.d.ts +47 -0
  33. package/dist/area/stackPolicy.js +99 -0
  34. package/dist/authoring.d.ts +1 -0
  35. package/dist/authoring.js +1 -0
  36. package/dist/board/laneBoard.d.ts +13 -0
  37. package/dist/board/laneBoard.js +13 -0
  38. package/dist/board/timelineBoard.d.ts +10 -0
  39. package/dist/board/timelineBoard.js +10 -0
  40. package/dist/combat/abilityKit.d.ts +18 -0
  41. package/dist/combat/abilityKit.js +24 -9
  42. package/dist/combat/animationState.d.ts +5 -0
  43. package/dist/combat/animationState.js +5 -0
  44. package/dist/combat/breakMeters.d.ts +10 -0
  45. package/dist/combat/breakMeters.js +10 -0
  46. package/dist/combat/castRunner.d.ts +5 -0
  47. package/dist/combat/castRunner.js +5 -0
  48. package/dist/combat/comboPoints.d.ts +5 -0
  49. package/dist/combat/comboPoints.js +5 -0
  50. package/dist/combat/comboString.d.ts +5 -0
  51. package/dist/combat/comboString.js +5 -0
  52. package/dist/combat/damageInterceptors.d.ts +192 -0
  53. package/dist/combat/damageInterceptors.js +251 -0
  54. package/dist/combat/damageMatchup.d.ts +68 -0
  55. package/dist/combat/damageMatchup.js +42 -0
  56. package/dist/combat/damageResolution.d.ts +63 -0
  57. package/dist/combat/damageResolution.js +50 -0
  58. package/dist/combat/death.d.ts +7 -1
  59. package/dist/combat/death.js +5 -0
  60. package/dist/combat/deathReason.d.ts +14 -0
  61. package/dist/combat/deathReason.js +1 -0
  62. package/dist/combat/defensiveWindow.d.ts +5 -0
  63. package/dist/combat/defensiveWindow.js +5 -0
  64. package/dist/combat/dotField.d.ts +5 -1
  65. package/dist/combat/dotField.js +5 -1
  66. package/dist/combat/downed.d.ts +5 -0
  67. package/dist/combat/downed.js +5 -0
  68. package/dist/combat/effects.d.ts +5 -0
  69. package/dist/combat/effects.js +5 -0
  70. package/dist/combat/encounterSequence.d.ts +258 -0
  71. package/dist/combat/encounterSequence.js +426 -0
  72. package/dist/combat/hitReaction.d.ts +57 -1
  73. package/dist/combat/hitReaction.js +45 -5
  74. package/dist/combat/magazine.d.ts +54 -0
  75. package/dist/combat/magazine.js +96 -0
  76. package/dist/combat/projectiles.d.ts +5 -0
  77. package/dist/combat/projectiles.js +32 -14
  78. package/dist/combat/receivedDamage.d.ts +117 -0
  79. package/dist/combat/receivedDamage.js +92 -0
  80. package/dist/combat/regenShield.d.ts +5 -1
  81. package/dist/combat/regenShield.js +5 -1
  82. package/dist/combat/renderCues.d.ts +51 -0
  83. package/dist/combat/renderCues.js +44 -0
  84. package/dist/combat/resourcePool.d.ts +5 -0
  85. package/dist/combat/resourcePool.js +5 -0
  86. package/dist/combat/rewardAllocation.d.ts +121 -0
  87. package/dist/combat/rewardAllocation.js +251 -0
  88. package/dist/combat/shotOrigin.d.ts +18 -1
  89. package/dist/combat/shotOrigin.js +39 -0
  90. package/dist/combat/statusApplication.d.ts +96 -0
  91. package/dist/combat/statusApplication.js +81 -0
  92. package/dist/combat/telegraph.d.ts +11 -2
  93. package/dist/combat/telegraph.js +11 -2
  94. package/dist/combat/thresholdCrossings.d.ts +85 -0
  95. package/dist/combat/thresholdCrossings.js +116 -0
  96. package/dist/combat.d.ts +34 -0
  97. package/dist/combat.js +34 -0
  98. package/dist/commands/commandRegistry.d.ts +11 -0
  99. package/dist/commands/commandRegistry.js +9 -2
  100. package/dist/crafting/crop.d.ts +18 -0
  101. package/dist/crafting/crop.js +18 -0
  102. package/dist/crafting/production.d.ts +5 -0
  103. package/dist/crafting/production.js +5 -0
  104. package/dist/devtools/rewriteTunables.d.ts +17 -0
  105. package/dist/devtools/rewriteTunables.js +276 -0
  106. package/dist/devtools/saveEndpoint.d.ts +46 -0
  107. package/dist/devtools/saveEndpoint.js +48 -0
  108. package/dist/devtools/transformTunables.d.ts +3 -0
  109. package/dist/devtools/transformTunables.js +3 -0
  110. package/dist/devtools/tunableSchema.d.ts +31 -0
  111. package/dist/devtools/tunableSchema.js +31 -0
  112. package/dist/economy/currency.d.ts +12 -1
  113. package/dist/economy/currency.js +12 -1
  114. package/dist/economy/listingBook.d.ts +91 -0
  115. package/dist/economy/listingBook.js +144 -0
  116. package/dist/economy/resourceLedger.d.ts +222 -0
  117. package/dist/economy/resourceLedger.js +353 -0
  118. package/dist/economy/sharedWallet.d.ts +14 -0
  119. package/dist/economy/sharedWallet.js +14 -0
  120. package/dist/economy/techTree.d.ts +12 -0
  121. package/dist/economy/techTree.js +12 -0
  122. package/dist/economy/wallet.d.ts +26 -2
  123. package/dist/economy/wallet.js +35 -6
  124. package/dist/editor/commands.d.ts +190 -7
  125. package/dist/editor/commands.js +727 -55
  126. package/dist/editor/document.d.ts +159 -13
  127. package/dist/editor/document.js +750 -19
  128. package/dist/editor/grid.d.ts +229 -0
  129. package/dist/editor/grid.js +366 -0
  130. package/dist/editor/gridAdapters.d.ts +80 -0
  131. package/dist/editor/gridAdapters.js +134 -0
  132. package/dist/editor/index.d.ts +73 -6
  133. package/dist/editor/index.js +6 -2
  134. package/dist/editor/liveSync.d.ts +127 -0
  135. package/dist/editor/liveSync.js +249 -0
  136. package/dist/editor/runtimeInspector.d.ts +88 -0
  137. package/dist/editor/runtimeInspector.js +239 -0
  138. package/dist/editor/types.d.ts +107 -1
  139. package/dist/faction/reputation.d.ts +5 -0
  140. package/dist/faction/reputation.js +5 -0
  141. package/dist/format/distance.d.ts +17 -0
  142. package/dist/format/distance.js +18 -0
  143. package/dist/format/duration.d.ts +27 -0
  144. package/dist/format/duration.js +45 -0
  145. package/dist/format/speed.d.ts +17 -0
  146. package/dist/format/speed.js +25 -0
  147. package/dist/game/connectedPlayers.d.ts +6 -4
  148. package/dist/game/connectedPlayers.js +2 -2
  149. package/dist/game/cosmetics.d.ts +9 -0
  150. package/dist/game/cosmetics.js +16 -0
  151. package/dist/game/defineGame.d.ts +100 -2
  152. package/dist/game/defineGame.js +39 -2
  153. package/dist/game/defineSystem.d.ts +88 -0
  154. package/dist/game/defineSystem.js +45 -0
  155. package/dist/game/dialogue.d.ts +33 -0
  156. package/dist/game/dialogue.js +21 -0
  157. package/dist/game/events.d.ts +35 -13
  158. package/dist/game/events.js +5 -0
  159. package/dist/game/feed.d.ts +33 -0
  160. package/dist/game/feed.js +35 -2
  161. package/dist/game/gamePhase.d.ts +11 -0
  162. package/dist/game/gamePhase.js +16 -0
  163. package/dist/game/leaderboard.d.ts +5 -0
  164. package/dist/game/leaderboard.js +5 -0
  165. package/dist/game/loadout.d.ts +5 -0
  166. package/dist/game/loadout.js +5 -0
  167. package/dist/game/lootFilter.d.ts +2 -0
  168. package/dist/game/lootFilter.js +2 -0
  169. package/dist/game/lootPipeline.d.ts +164 -0
  170. package/dist/game/lootPipeline.js +216 -0
  171. package/dist/game/lootTable.d.ts +19 -2
  172. package/dist/game/lootTable.js +15 -5
  173. package/dist/game/objectives.d.ts +43 -0
  174. package/dist/game/objectives.js +36 -0
  175. package/dist/game/paramLayers.d.ts +148 -0
  176. package/dist/game/paramLayers.js +185 -0
  177. package/dist/game/ping.d.ts +5 -0
  178. package/dist/game/ping.js +5 -0
  179. package/dist/game/playableGame.d.ts +92 -7
  180. package/dist/game/quest.d.ts +8 -0
  181. package/dist/game/quest.js +29 -0
  182. package/dist/game/race.d.ts +141 -0
  183. package/dist/game/race.js +169 -0
  184. package/dist/game/recordBook.d.ts +3 -1
  185. package/dist/game/recordBook.js +4 -1
  186. package/dist/game/ruleSelection.d.ts +75 -0
  187. package/dist/game/ruleSelection.js +127 -0
  188. package/dist/game/runDraft.d.ts +5 -0
  189. package/dist/game/runDraft.js +5 -0
  190. package/dist/game/saveStore.d.ts +111 -0
  191. package/dist/game/saveStore.js +312 -0
  192. package/dist/game/snapshotHistory.d.ts +67 -0
  193. package/dist/game/snapshotHistory.js +95 -0
  194. package/dist/game/social.d.ts +38 -1
  195. package/dist/game/social.js +71 -0
  196. package/dist/game/spawnPoints.d.ts +5 -0
  197. package/dist/game/spawnPoints.js +5 -0
  198. package/dist/game/systemRuntime.d.ts +50 -0
  199. package/dist/game/systemRuntime.js +157 -0
  200. package/dist/game/systemSchedule.d.ts +72 -0
  201. package/dist/game/systemSchedule.js +242 -0
  202. package/dist/game/toasts.d.ts +43 -0
  203. package/dist/game/toasts.js +44 -0
  204. package/dist/game/trade.d.ts +10 -0
  205. package/dist/game/trade.js +10 -0
  206. package/dist/game/unlocks.d.ts +8 -0
  207. package/dist/game/unlocks.js +16 -0
  208. package/dist/game/vfxInstance.d.ts +138 -0
  209. package/dist/game/vfxInstance.js +96 -0
  210. package/dist/game/worldItem.d.ts +17 -4
  211. package/dist/game/worldItem.js +17 -4
  212. package/dist/gameplay.d.ts +91 -0
  213. package/dist/gameplay.js +91 -0
  214. package/dist/index.d.ts +5 -14
  215. package/dist/index.js +5 -14
  216. package/dist/input/actionBindings.d.ts +23 -6
  217. package/dist/input/actionBindings.js +23 -6
  218. package/dist/input/axisInput.d.ts +7 -2
  219. package/dist/input/axisInput.js +7 -2
  220. package/dist/input/controlGroups.d.ts +67 -0
  221. package/dist/input/controlGroups.js +41 -0
  222. package/dist/input/touchScheme.d.ts +82 -6
  223. package/dist/input/touchScheme.js +123 -10
  224. package/dist/interaction/lockpick.d.ts +61 -0
  225. package/dist/interaction/lockpick.js +168 -0
  226. package/dist/interaction/proximityPrompt.d.ts +2 -0
  227. package/dist/interaction/proximityPrompt.js +2 -0
  228. package/dist/interaction/qte.d.ts +5 -0
  229. package/dist/interaction/qte.js +5 -0
  230. package/dist/interaction/skillCheck.d.ts +5 -0
  231. package/dist/interaction/skillCheck.js +5 -0
  232. package/dist/inventory/inventoryModel.d.ts +12 -0
  233. package/dist/inventory/inventoryModel.js +12 -0
  234. package/dist/inventory/shapedGrid.d.ts +16 -0
  235. package/dist/inventory/shapedGrid.js +16 -0
  236. package/dist/inventory/slotModel.d.ts +10 -0
  237. package/dist/inventory/slotModel.js +10 -0
  238. package/dist/item/durability.d.ts +5 -0
  239. package/dist/item/durability.js +5 -0
  240. package/dist/item/itemIdentity.d.ts +171 -0
  241. package/dist/item/itemIdentity.js +138 -0
  242. package/dist/item/itemInstanceRegistry.d.ts +32 -0
  243. package/dist/item/itemInstanceRegistry.js +34 -0
  244. package/dist/item/itemgen.d.ts +163 -0
  245. package/dist/item/itemgen.js +151 -0
  246. package/dist/item/modularItem.d.ts +5 -0
  247. package/dist/item/modularItem.js +5 -0
  248. package/dist/item/use.d.ts +5 -0
  249. package/dist/item/use.js +5 -0
  250. package/dist/item/useBehavior.d.ts +142 -0
  251. package/dist/item/useBehavior.js +105 -0
  252. package/dist/item/weapon.d.ts +5 -0
  253. package/dist/item/weapon.js +5 -0
  254. package/dist/math/scalar.d.ts +25 -0
  255. package/dist/math/scalar.js +47 -0
  256. package/dist/meta/changelog.d.ts +4 -1
  257. package/dist/meta/changelog.js +16 -2
  258. package/dist/movement/avoidance.d.ts +45 -0
  259. package/dist/movement/avoidance.js +123 -0
  260. package/dist/movement/cameraRig.d.ts +8 -0
  261. package/dist/movement/cameraRig.js +8 -0
  262. package/dist/movement/dash.d.ts +11 -0
  263. package/dist/movement/dash.js +11 -0
  264. package/dist/movement/formation.d.ts +135 -0
  265. package/dist/movement/formation.js +194 -0
  266. package/dist/movement/glideModel.d.ts +5 -0
  267. package/dist/movement/glideModel.js +5 -0
  268. package/dist/movement/grappleSwing.d.ts +5 -0
  269. package/dist/movement/grappleSwing.js +5 -0
  270. package/dist/movement/leaderTrail.d.ts +5 -0
  271. package/dist/movement/leaderTrail.js +5 -0
  272. package/dist/movement/movementModel.d.ts +35 -8
  273. package/dist/movement/movementModel.js +42 -14
  274. package/dist/movement/playerMovement.js +65 -10
  275. package/dist/movement/poseState.d.ts +13 -0
  276. package/dist/movement/poseState.js +16 -0
  277. package/dist/movement/steering.d.ts +8 -0
  278. package/dist/movement/steering.js +18 -0
  279. package/dist/multiplayer/combatSnapshot.d.ts +3 -0
  280. package/dist/multiplayer/combatSnapshot.js +3 -0
  281. package/dist/multiplayer/lagCompensation.d.ts +5 -0
  282. package/dist/multiplayer/lagCompensation.js +5 -0
  283. package/dist/multiplayer/presenceModel.d.ts +9 -1
  284. package/dist/multiplayer/presenceModel.js +9 -1
  285. package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
  286. package/dist/multiplayer/simultaneousCommit.js +3 -0
  287. package/dist/multiplayer.d.ts +11 -0
  288. package/dist/multiplayer.js +11 -0
  289. package/dist/nav/pathFollow.d.ts +57 -0
  290. package/dist/nav/pathFollow.js +109 -0
  291. package/dist/orders/orderKinds.d.ts +156 -0
  292. package/dist/orders/orderKinds.js +197 -0
  293. package/dist/orders/orderQueue.d.ts +208 -0
  294. package/dist/orders/orderQueue.js +211 -0
  295. package/dist/physics/drivableVehicle.d.ts +29 -0
  296. package/dist/physics/drivableVehicle.js +16 -0
  297. package/dist/physics/kinematicVehicle.d.ts +21 -1
  298. package/dist/physics/kinematicVehicle.js +23 -8
  299. package/dist/physics/physicsWorld.d.ts +24 -12
  300. package/dist/physics/physicsWorld.js +11 -5
  301. package/dist/physics/radialImpulse.d.ts +25 -0
  302. package/dist/physics/radialImpulse.js +27 -0
  303. package/dist/procedural.d.ts +3 -0
  304. package/dist/procedural.js +3 -0
  305. package/dist/progression/statGraph.d.ts +116 -0
  306. package/dist/progression/statGraph.js +318 -0
  307. package/dist/puzzle/cellGrid.d.ts +9 -0
  308. package/dist/puzzle/cellGrid.js +9 -0
  309. package/dist/puzzle/fallingPiece.d.ts +9 -0
  310. package/dist/puzzle/fallingPiece.js +9 -0
  311. package/dist/puzzle/nonogram.d.ts +39 -0
  312. package/dist/puzzle/nonogram.js +136 -0
  313. package/dist/random/nameGen.d.ts +5 -0
  314. package/dist/random/nameGen.js +5 -0
  315. package/dist/random/pick.d.ts +8 -0
  316. package/dist/random/pick.js +31 -0
  317. package/dist/random/rng.d.ts +22 -0
  318. package/dist/random/rng.js +28 -8
  319. package/dist/random/seedLink.d.ts +4 -0
  320. package/dist/random/seedLink.js +4 -0
  321. package/dist/relation/keyedValues.d.ts +54 -0
  322. package/dist/relation/keyedValues.js +115 -0
  323. package/dist/relation/thresholds.d.ts +62 -0
  324. package/dist/relation/thresholds.js +62 -0
  325. package/dist/render/color.d.ts +12 -0
  326. package/dist/render/color.js +26 -0
  327. package/dist/render/lookPreset.d.ts +41 -0
  328. package/dist/render/lookPreset.js +29 -0
  329. package/dist/render/postProcessing.d.ts +20 -0
  330. package/dist/render/postProcessing.js +21 -1
  331. package/dist/rules/predicate.d.ts +53 -0
  332. package/dist/rules/predicate.js +64 -0
  333. package/dist/rules/ruleEffects.d.ts +38 -0
  334. package/dist/rules/ruleEffects.js +37 -0
  335. package/dist/rules/triggeredRules.d.ts +160 -0
  336. package/dist/rules/triggeredRules.js +241 -0
  337. package/dist/runtime/adapter.d.ts +42 -3
  338. package/dist/runtime/adapter.js +44 -1
  339. package/dist/runtime/cameraDirector.d.ts +1 -0
  340. package/dist/runtime/cameraDirector.js +1 -0
  341. package/dist/runtime/commandRunner.d.ts +1 -0
  342. package/dist/runtime/commandRunner.js +1 -0
  343. package/dist/runtime/context/combatFx.d.ts +23 -0
  344. package/dist/runtime/context/combatFx.js +161 -0
  345. package/dist/runtime/context/registries.d.ts +13 -0
  346. package/dist/runtime/context/registries.js +84 -0
  347. package/dist/runtime/context/worldItems.d.ts +21 -0
  348. package/dist/runtime/context/worldItems.js +37 -0
  349. package/dist/runtime/descriptors/baseline.d.ts +48 -0
  350. package/dist/runtime/descriptors/baseline.js +134 -0
  351. package/dist/runtime/descriptors/features.d.ts +74 -0
  352. package/dist/runtime/descriptors/features.js +281 -0
  353. package/dist/runtime/gameContext.d.ts +114 -13
  354. package/dist/runtime/gameContext.js +303 -395
  355. package/dist/runtime/gameRuntime.d.ts +1 -0
  356. package/dist/runtime/gameRuntime.js +1 -0
  357. package/dist/runtime/headlessRunner.d.ts +60 -0
  358. package/dist/runtime/headlessRunner.js +44 -0
  359. package/dist/runtime/hostPersistence.d.ts +12 -0
  360. package/dist/runtime/hostPersistence.js +12 -0
  361. package/dist/runtime/hostedGameRunner.d.ts +8 -3
  362. package/dist/runtime/hostedGameRunner.js +26 -3
  363. package/dist/runtime/hostedWorldSession.d.ts +12 -4
  364. package/dist/runtime/hostedWorldSession.js +11 -4
  365. package/dist/runtime/inputSnapshot.d.ts +8 -3
  366. package/dist/runtime/inputSnapshot.js +6 -2
  367. package/dist/runtime/motionIntents.d.ts +11 -2
  368. package/dist/runtime/motionIntents.js +16 -2
  369. package/dist/runtime/perContext.d.ts +15 -0
  370. package/dist/runtime/perContext.js +21 -0
  371. package/dist/runtime/runtimeSave.d.ts +79 -0
  372. package/dist/runtime/runtimeSave.js +105 -0
  373. package/dist/runtime/save.d.ts +4 -0
  374. package/dist/runtime/save.js +4 -0
  375. package/dist/runtime/snapshot.d.ts +7 -0
  376. package/dist/runtime/snapshot.js +7 -0
  377. package/dist/runtime/visibility.d.ts +2 -1
  378. package/dist/runtime/visibility.js +2 -1
  379. package/dist/runtime/worldChannel.d.ts +7 -3
  380. package/dist/runtime/worldChannel.js +51 -6
  381. package/dist/runtime/worldMirror.d.ts +14 -2
  382. package/dist/runtime/worldMirror.js +15 -2
  383. package/dist/runtime/worldProjection.d.ts +47 -0
  384. package/dist/runtime/worldProjection.js +52 -0
  385. package/dist/runtime/worldReplication.d.ts +30 -7
  386. package/dist/runtime/worldReplication.js +41 -9
  387. package/dist/runtime/worldSnapshot.d.ts +30 -3
  388. package/dist/runtime/worldSnapshot.js +20 -5
  389. package/dist/scene/assetCatalog.d.ts +21 -0
  390. package/dist/scene/assetCatalog.js +4 -0
  391. package/dist/scene/assetDiagnostics.d.ts +69 -0
  392. package/dist/scene/assetDiagnostics.js +110 -0
  393. package/dist/scene/assetGenerator.d.ts +65 -0
  394. package/dist/scene/assetGenerator.js +64 -0
  395. package/dist/scene/assetSpace.d.ts +137 -0
  396. package/dist/scene/assetSpace.js +259 -0
  397. package/dist/scene/authoredTriggers.d.ts +169 -0
  398. package/dist/scene/authoredTriggers.js +340 -0
  399. package/dist/scene/behaviorRuntime.d.ts +75 -0
  400. package/dist/scene/behaviorRuntime.js +217 -0
  401. package/dist/scene/behaviors.d.ts +14 -2
  402. package/dist/scene/behaviors.js +7 -2
  403. package/dist/scene/bodyBind.d.ts +60 -0
  404. package/dist/scene/bodyBind.js +51 -0
  405. package/dist/scene/builtinSceneKinds.d.ts +5 -0
  406. package/dist/scene/builtinSceneKinds.js +62 -0
  407. package/dist/scene/colliders.d.ts +13 -1
  408. package/dist/scene/colliders.js +37 -1
  409. package/dist/scene/entityStats.d.ts +11 -2
  410. package/dist/scene/entityStats.js +11 -2
  411. package/dist/scene/entityStore.d.ts +12 -1
  412. package/dist/scene/entityStore.js +15 -0
  413. package/dist/scene/facing.d.ts +18 -0
  414. package/dist/scene/facing.js +26 -0
  415. package/dist/scene/modelSockets.d.ts +33 -0
  416. package/dist/scene/modelSockets.js +29 -0
  417. package/dist/scene/objectQuery.d.ts +2 -0
  418. package/dist/scene/objectQuery.js +2 -0
  419. package/dist/scene/objectStore.d.ts +4 -0
  420. package/dist/scene/objectStore.js +6 -0
  421. package/dist/scene/possession.d.ts +8 -0
  422. package/dist/scene/possession.js +14 -0
  423. package/dist/scene/roster.d.ts +3 -0
  424. package/dist/scene/roster.js +12 -0
  425. package/dist/scene/sceneKinds.d.ts +240 -0
  426. package/dist/scene/sceneKinds.js +214 -0
  427. package/dist/scene/sceneOwnership.d.ts +169 -0
  428. package/dist/scene/sceneOwnership.js +179 -0
  429. package/dist/scene/selectionBookmarks.d.ts +93 -0
  430. package/dist/scene/selectionBookmarks.js +122 -0
  431. package/dist/scene/vehicleSeat.d.ts +68 -0
  432. package/dist/scene/vehicleSeat.js +59 -0
  433. package/dist/sensor/concealment.d.ts +3 -0
  434. package/dist/sensor/concealment.js +3 -0
  435. package/dist/sensor/freezeMonitor.d.ts +1 -0
  436. package/dist/sensor/freezeMonitor.js +1 -0
  437. package/dist/sensor/frustumSensor.d.ts +7 -3
  438. package/dist/sensor/frustumSensor.js +7 -3
  439. package/dist/sensor/hiddenStateProbe.d.ts +5 -2
  440. package/dist/sensor/hiddenStateProbe.js +5 -2
  441. package/dist/sensor/recordingBuffer.d.ts +2 -1
  442. package/dist/sensor/recordingBuffer.js +2 -1
  443. package/dist/sensor/replayLoop.d.ts +6 -2
  444. package/dist/sensor/replayLoop.js +6 -2
  445. package/dist/sensor/revealQuery.d.ts +2 -1
  446. package/dist/sensor/revealQuery.js +2 -1
  447. package/dist/sensor/visionCone.d.ts +10 -3
  448. package/dist/sensor/visionCone.js +10 -3
  449. package/dist/session/contestedChannel.d.ts +1 -0
  450. package/dist/session/contestedChannel.js +1 -0
  451. package/dist/session/extraction.d.ts +1 -0
  452. package/dist/session/extraction.js +1 -0
  453. package/dist/session/roles.d.ts +6 -0
  454. package/dist/session/roles.js +6 -0
  455. package/dist/session/roundState.d.ts +8 -0
  456. package/dist/session/roundState.js +2 -0
  457. package/dist/settings/settingsModel.d.ts +1 -0
  458. package/dist/settings/settingsModel.js +1 -0
  459. package/dist/stats/accumulatorMeter.d.ts +6 -0
  460. package/dist/stats/accumulatorMeter.js +6 -0
  461. package/dist/stats/eventMeter.d.ts +5 -0
  462. package/dist/stats/eventMeter.js +5 -0
  463. package/dist/stats/rollCheck.d.ts +5 -0
  464. package/dist/stats/rollCheck.js +5 -0
  465. package/dist/stats/statModifiers.d.ts +5 -0
  466. package/dist/stats/statModifiers.js +5 -0
  467. package/dist/store/defineKeyedStore.d.ts +38 -0
  468. package/dist/store/defineKeyedStore.js +42 -0
  469. package/dist/store/defineStore.d.ts +33 -0
  470. package/dist/store/defineStore.js +35 -0
  471. package/dist/store/observableKeyedStore.d.ts +5 -0
  472. package/dist/store/observableKeyedStore.js +34 -2
  473. package/dist/survival/decayMeter.d.ts +55 -0
  474. package/dist/survival/decayMeter.js +130 -55
  475. package/dist/survival/moodle.d.ts +2 -0
  476. package/dist/survival/regionHealth.d.ts +6 -0
  477. package/dist/survival/regionHealth.js +5 -0
  478. package/dist/tactics/fallingGrid.d.ts +4 -2
  479. package/dist/tactics/fallingGrid.js +4 -2
  480. package/dist/tactics/predictiveQuery.d.ts +3 -0
  481. package/dist/tactics/predictiveQuery.js +3 -0
  482. package/dist/tactics/snapshot.d.ts +2 -0
  483. package/dist/tactics/snapshot.js +2 -0
  484. package/dist/tactics/surface.d.ts +1 -0
  485. package/dist/tactics/surface.js +1 -0
  486. package/dist/tactics/tacticalGrid.d.ts +1 -0
  487. package/dist/tactics/tacticalGrid.js +1 -0
  488. package/dist/time/beatClock.d.ts +14 -4
  489. package/dist/time/beatClock.js +14 -4
  490. package/dist/time/calendarClock.d.ts +3 -0
  491. package/dist/time/calendarClock.js +3 -0
  492. package/dist/time/idleProgress.d.ts +4 -0
  493. package/dist/time/idleProgress.js +4 -0
  494. package/dist/time/simClock.d.ts +2 -0
  495. package/dist/time/simClock.js +20 -5
  496. package/dist/ui/actionModel.d.ts +110 -0
  497. package/dist/ui/actionModel.js +159 -0
  498. package/dist/ui/gameLayout.d.ts +30 -11
  499. package/dist/ui/gameLayout.js +30 -11
  500. package/dist/ui/hudDocument.d.ts +114 -0
  501. package/dist/ui/hudDocument.js +220 -0
  502. package/dist/ui/hudLayout.d.ts +33 -1
  503. package/dist/ui/hudLayout.js +204 -17
  504. package/dist/ui/selectionModel.d.ts +92 -0
  505. package/dist/ui/selectionModel.js +88 -0
  506. package/dist/ui/tooltipModel.d.ts +63 -0
  507. package/dist/ui/tooltipModel.js +82 -0
  508. package/dist/ui.d.ts +15 -0
  509. package/dist/ui.js +15 -0
  510. package/dist/visibility/bounds.d.ts +8 -1
  511. package/dist/visibility/bounds.js +8 -1
  512. package/dist/visibility/frustum.d.ts +12 -3
  513. package/dist/visibility/frustum.js +12 -3
  514. package/dist/work/jobQueue.d.ts +201 -0
  515. package/dist/work/jobQueue.js +253 -0
  516. package/dist/work/unitTraining.d.ts +58 -0
  517. package/dist/work/unitTraining.js +52 -0
  518. package/dist/world/authoredObjects.d.ts +58 -0
  519. package/dist/world/authoredObjects.js +69 -0
  520. package/dist/world/buildingGenerator.d.ts +18 -0
  521. package/dist/world/buildingGenerator.js +62 -0
  522. package/dist/world/buildings.d.ts +5 -0
  523. package/dist/world/buildings.js +5 -0
  524. package/dist/world/catenary.d.ts +22 -0
  525. package/dist/world/catenary.js +69 -0
  526. package/dist/world/features.d.ts +115 -7
  527. package/dist/world/features.js +56 -5
  528. package/dist/world/footprintGrid.d.ts +59 -0
  529. package/dist/world/footprintGrid.js +141 -0
  530. package/dist/world/geometry.d.ts +64 -0
  531. package/dist/world/geometry.js +94 -0
  532. package/dist/world/grassKind.d.ts +51 -0
  533. package/dist/world/grassKind.js +101 -0
  534. package/dist/world/pathInstances.d.ts +27 -0
  535. package/dist/world/pathInstances.js +27 -0
  536. package/dist/world/pathTerrain.d.ts +80 -0
  537. package/dist/world/pathTerrain.js +142 -0
  538. package/dist/world/placeAsset.d.ts +81 -0
  539. package/dist/world/placeAsset.js +77 -0
  540. package/dist/world/placement.d.ts +4 -0
  541. package/dist/world/placement.js +4 -0
  542. package/dist/world/placementController.d.ts +6 -0
  543. package/dist/world/placementController.js +6 -0
  544. package/dist/world/polyline.d.ts +34 -0
  545. package/dist/world/polyline.js +81 -0
  546. package/dist/world/roads.d.ts +8 -0
  547. package/dist/world/roads.js +38 -0
  548. package/dist/world/scatterRegion.d.ts +164 -0
  549. package/dist/world/scatterRegion.js +409 -0
  550. package/dist/world/sceneShapes.d.ts +43 -0
  551. package/dist/world/sceneShapes.js +1 -0
  552. package/dist/world/segment.d.ts +27 -0
  553. package/dist/world/segment.js +43 -0
  554. package/dist/world/soilKind.d.ts +48 -0
  555. package/dist/world/soilKind.js +92 -0
  556. package/dist/world/spatialSample.d.ts +241 -0
  557. package/dist/world/spatialSample.js +465 -0
  558. package/dist/world/terraform.d.ts +213 -1
  559. package/dist/world/terraform.js +655 -20
  560. package/dist/world/terrain.d.ts +119 -20
  561. package/dist/world/terrain.js +247 -41
  562. package/dist/world/terrainGuides.d.ts +182 -0
  563. package/dist/world/terrainGuides.js +343 -0
  564. package/dist/world/vec2.d.ts +81 -0
  565. package/dist/world/vec2.js +128 -0
  566. package/dist/world/vec3.d.ts +49 -0
  567. package/dist/world/vec3.js +76 -0
  568. package/dist/world/vegetation.d.ts +71 -0
  569. package/dist/world/vegetation.js +151 -0
  570. package/dist/world/visibilityField.d.ts +154 -0
  571. package/dist/world/visibilityField.js +271 -0
  572. package/dist/world/volumetricClouds.d.ts +52 -0
  573. package/dist/world/volumetricClouds.js +43 -0
  574. package/dist/world/walls.d.ts +9 -0
  575. package/dist/world/walls.js +9 -0
  576. package/dist/world/waterKind.d.ts +55 -0
  577. package/dist/world/waterKind.js +99 -0
  578. package/dist/world.d.ts +132 -0
  579. package/dist/world.js +132 -0
  580. package/package.json +32 -8
  581. package/skills/jgengine/SKILL.md +51 -0
  582. package/skills/jgengine/api.md +434 -0
  583. package/skills/jgengine/capabilities.md +29 -0
  584. package/skills/jgengine/reference-cartridge.md +72 -0
  585. package/skills/jgengine-combat/SKILL.md +34 -0
  586. package/skills/jgengine-combat/api.md +549 -0
  587. package/skills/jgengine-combat/capabilities.md +169 -0
  588. package/skills/jgengine-combat/reference.md +98 -0
  589. package/skills/jgengine-gameplay/SKILL.md +39 -0
  590. package/skills/jgengine-gameplay/api.md +1794 -0
  591. package/skills/jgengine-gameplay/capabilities.md +378 -0
  592. package/skills/jgengine-gameplay/reference-systems.md +60 -0
  593. package/skills/jgengine-world/SKILL.md +38 -0
  594. package/skills/jgengine-world/api.md +2652 -0
  595. package/skills/jgengine-world/capabilities.md +370 -0
  596. package/skills/jgengine-world/reference.md +222 -0
  597. package/dist/cartridge/runtime.d.ts +0 -15
  598. package/dist/cartridge/runtime.js +0 -556
  599. package/dist/cartridge/spec.d.ts +0 -188
  600. package/dist/cartridge/spec.js +0 -19
  601. package/dist/cartridge/validate.d.ts +0 -2
  602. package/dist/cartridge/validate.js +0 -78
  603. package/dist/store/engineStore.d.ts +0 -22
  604. package/dist/store/engineStore.js +0 -50
  605. package/dist/store/features.d.ts +0 -10
  606. package/dist/store/features.js +0 -23
  607. package/llms.txt +0 -5389
@@ -1,4 +1,5 @@
1
- import { FLAT_FIELD, withNormal } from "./terrain.js";
1
+ import { fractalNoise, FLAT_FIELD, withNormal } from "./terrain.js";
2
+ /** Smooth/linear/hard brush weight for a sample `distance` from the brush center. */
2
3
  export function brushWeight(distance, radius, falloff) {
3
4
  if (radius <= 0)
4
5
  return distance <= 0 ? 1 : 0;
@@ -15,6 +16,13 @@ export function brushWeight(distance, radius, falloff) {
15
16
  function clampIndex(value, max) {
16
17
  return value < 0 ? 0 : value > max ? max : value;
17
18
  }
19
+ function clampRange(value, min, max) {
20
+ if (min !== undefined && value < min)
21
+ return min;
22
+ if (max !== undefined && value > max)
23
+ return max;
24
+ return value;
25
+ }
18
26
  export function createEditableTerrain(config) {
19
27
  const base = config.base ?? FLAT_FIELD;
20
28
  const bounds = config.bounds;
@@ -27,12 +35,22 @@ export function createEditableTerrain(config) {
27
35
  const vertsZ = rows + 1;
28
36
  const offsets = new Float32Array(vertsX * vertsZ);
29
37
  const surfaces = new Array(cols * rows).fill(null);
38
+ const cellCount = cols * rows;
39
+ let layers = [];
40
+ // Lazily allocated: null until the first blend, so single-layer terrains carry no weight buffer.
41
+ let weights = null;
30
42
  function gridX(x) {
31
43
  return ((x - bounds.minX) / spanX) * cols;
32
44
  }
33
45
  function gridZ(z) {
34
46
  return ((z - bounds.minZ) / spanZ) * rows;
35
47
  }
48
+ function vertexWorldX(gx) {
49
+ return bounds.minX + (gx / cols) * spanX;
50
+ }
51
+ function vertexWorldZ(gz) {
52
+ return bounds.minZ + (gz / rows) * spanZ;
53
+ }
36
54
  function heightOffsetAt(x, z) {
37
55
  const fx = gridX(x);
38
56
  const fz = gridZ(z);
@@ -52,10 +70,55 @@ export function createEditableTerrain(config) {
52
70
  }
53
71
  const sampleHeight = (x, z) => base.sampleHeight(x, z) + heightOffsetAt(x, z);
54
72
  const sampleNormal = withNormal(sampleHeight);
55
- function editHeight(edit) {
73
+ function totalHeightAtVertex(gx, gz) {
74
+ const wx = vertexWorldX(gx);
75
+ const wz = vertexWorldZ(gz);
76
+ return base.sampleHeight(wx, wz) + offsets[gz * vertsX + gx];
77
+ }
78
+ function neighborAverageHeight(gx, gz) {
79
+ let sum = 0;
80
+ let count = 0;
81
+ for (const [dx, dz] of [
82
+ [-1, 0],
83
+ [1, 0],
84
+ [0, -1],
85
+ [0, 1],
86
+ ]) {
87
+ const nx = gx + dx;
88
+ const nz = gz + dz;
89
+ if (nx < 0 || nx >= vertsX || nz < 0 || nz >= vertsZ)
90
+ continue;
91
+ sum += totalHeightAtVertex(nx, nz);
92
+ count += 1;
93
+ }
94
+ return count === 0 ? totalHeightAtVertex(gx, gz) : sum / count;
95
+ }
96
+ function writeOffset(index, next, limit, wx, wz, record) {
97
+ let value = next;
98
+ if (limit !== undefined) {
99
+ value = clampRange(base.sampleHeight(wx, wz) + value, limit.min, limit.max) - base.sampleHeight(wx, wz);
100
+ }
101
+ const before = offsets[index];
102
+ if (value === before)
103
+ return false;
104
+ offsets[index] = value;
105
+ record?.(index, before, value);
106
+ return true;
107
+ }
108
+ function editHeight(edit, record) {
56
109
  const falloff = edit.falloff ?? "smooth";
110
+ const shape = edit.shape ?? "circle";
57
111
  const strength = edit.strength ?? 1;
58
- const target = edit.mode === "flatten" ? edit.target ?? sampleHeight(edit.center[0], edit.center[1]) : 0;
112
+ const limit = edit.heightLimit;
113
+ const flattenTarget = edit.mode === "flatten" ? edit.target ?? sampleHeight(edit.center[0], edit.center[1]) : 0;
114
+ const noiseConfig = {
115
+ seed: edit.seed ?? 1337,
116
+ frequency: edit.frequency ?? 0.15,
117
+ octaves: 3,
118
+ lacunarity: 2,
119
+ persistence: 0.5,
120
+ ridged: false,
121
+ };
59
122
  const rCells = Math.ceil(edit.radius / cellSize) + 1;
60
123
  const cx = gridX(edit.center[0]);
61
124
  const cz = gridZ(edit.center[1]);
@@ -68,28 +131,96 @@ export function createEditableTerrain(config) {
68
131
  const gx = Math.round(cx) + ix;
69
132
  if (gx < 0 || gx >= vertsX)
70
133
  continue;
71
- const wx = bounds.minX + (gx / cols) * spanX;
72
- const wz = bounds.minZ + (gz / rows) * spanZ;
73
- const dist = Math.hypot(wx - edit.center[0], wz - edit.center[1]);
134
+ const wx = vertexWorldX(gx);
135
+ const wz = vertexWorldZ(gz);
136
+ const dx = wx - edit.center[0];
137
+ const dz = wz - edit.center[1];
138
+ const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
74
139
  const weight = brushWeight(dist, edit.radius, falloff);
75
140
  if (weight <= 0)
76
141
  continue;
77
142
  const index = gz * vertsX + gx;
143
+ const current = offsets[index];
144
+ let next = current;
78
145
  if (edit.mode === "raise")
79
- offsets[index] += strength * weight;
146
+ next = current + strength * weight;
80
147
  else if (edit.mode === "lower")
81
- offsets[index] -= strength * weight;
148
+ next = current - strength * weight;
149
+ else if (edit.mode === "noise")
150
+ next = current + fractalNoise(wx, wz, noiseConfig) * strength * weight;
151
+ else if (edit.mode === "smooth") {
152
+ const target = neighborAverageHeight(gx, gz);
153
+ const currentTotal = base.sampleHeight(wx, wz) + current;
154
+ next = current + (target - currentTotal) * weight * strength;
155
+ }
82
156
  else {
83
- const current = base.sampleHeight(wx, wz) + offsets[index];
84
- offsets[index] += (target - current) * weight * strength;
157
+ const currentTotal = base.sampleHeight(wx, wz) + current;
158
+ next = current + (flattenTarget - currentTotal) * weight * strength;
85
159
  }
86
- changed += 1;
160
+ if (writeOffset(index, next, limit, wx, wz, record))
161
+ changed += 1;
162
+ }
163
+ }
164
+ return changed;
165
+ }
166
+ function editRamp(edit, record) {
167
+ const to = edit.to;
168
+ if (to === undefined)
169
+ return 0;
170
+ const falloff = edit.falloff ?? "smooth";
171
+ const strength = edit.strength ?? 1;
172
+ const limit = edit.heightLimit;
173
+ const [ax, az] = edit.center;
174
+ const [bx, bz] = to;
175
+ const hFrom = edit.target ?? sampleHeight(ax, az);
176
+ const hTo = edit.targetTo ?? sampleHeight(bx, bz);
177
+ const segX = bx - ax;
178
+ const segZ = bz - az;
179
+ const segLenSq = segX * segX + segZ * segZ || 1;
180
+ const minGx = clampIndex(Math.floor(gridX(Math.min(ax, bx) - edit.radius)), vertsX - 1);
181
+ const maxGx = clampIndex(Math.ceil(gridX(Math.max(ax, bx) + edit.radius)), vertsX - 1);
182
+ const minGz = clampIndex(Math.floor(gridZ(Math.min(az, bz) - edit.radius)), vertsZ - 1);
183
+ const maxGz = clampIndex(Math.ceil(gridZ(Math.max(az, bz) + edit.radius)), vertsZ - 1);
184
+ let changed = 0;
185
+ for (let gz = minGz; gz <= maxGz; gz += 1) {
186
+ for (let gx = minGx; gx <= maxGx; gx += 1) {
187
+ const wx = vertexWorldX(gx);
188
+ const wz = vertexWorldZ(gz);
189
+ let t = ((wx - ax) * segX + (wz - az) * segZ) / segLenSq;
190
+ t = t < 0 ? 0 : t > 1 ? 1 : t;
191
+ const px = ax + segX * t;
192
+ const pz = az + segZ * t;
193
+ const weight = brushWeight(Math.hypot(wx - px, wz - pz), edit.radius, falloff);
194
+ if (weight <= 0)
195
+ continue;
196
+ const index = gz * vertsX + gx;
197
+ const current = offsets[index];
198
+ const target = hFrom + (hTo - hFrom) * t;
199
+ const currentTotal = base.sampleHeight(wx, wz) + current;
200
+ const next = current + (target - currentTotal) * weight * strength;
201
+ if (writeOffset(index, next, limit, wx, wz, record))
202
+ changed += 1;
87
203
  }
88
204
  }
89
205
  return changed;
90
206
  }
91
- function paintSurface(edit) {
207
+ function cellWorldX(gx) {
208
+ return bounds.minX + ((gx + 0.5) / cols) * spanX;
209
+ }
210
+ function cellWorldZ(gz) {
211
+ return bounds.minZ + ((gz + 0.5) / rows) * spanZ;
212
+ }
213
+ function writeSurface(index, surface, record) {
214
+ const before = surfaces[index] ?? null;
215
+ if (before === surface)
216
+ return false;
217
+ surfaces[index] = surface;
218
+ record?.(index, before, surface);
219
+ return true;
220
+ }
221
+ function paintSurface(edit, record) {
92
222
  const surface = edit.surface ?? null;
223
+ const shape = edit.shape ?? "circle";
93
224
  const rCells = Math.ceil(edit.radius / cellSize) + 1;
94
225
  const cx = gridX(edit.center[0]);
95
226
  const cz = gridZ(edit.center[1]);
@@ -102,36 +233,283 @@ export function createEditableTerrain(config) {
102
233
  const gx = Math.floor(cx) + ix;
103
234
  if (gx < 0 || gx >= cols)
104
235
  continue;
105
- const wx = bounds.minX + ((gx + 0.5) / cols) * spanX;
106
- const wz = bounds.minZ + ((gz + 0.5) / rows) * spanZ;
107
- if (Math.hypot(wx - edit.center[0], wz - edit.center[1]) > edit.radius)
236
+ const dx = cellWorldX(gx) - edit.center[0];
237
+ const dz = cellWorldZ(gz) - edit.center[1];
238
+ const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
239
+ if (dist > edit.radius)
108
240
  continue;
109
- surfaces[gz * cols + gx] = surface;
110
- painted += 1;
241
+ if (writeSurface(gz * cols + gx, surface, record))
242
+ painted += 1;
111
243
  }
112
244
  }
113
245
  return painted;
114
246
  }
115
- function surfaceAt(x, z) {
247
+ function localNormal(wx, wz) {
248
+ const eps = Math.max(cellSize, 0.5);
249
+ const hx = sampleHeight(wx + eps, wz) - sampleHeight(wx - eps, wz);
250
+ const hz = sampleHeight(wx, wz + eps) - sampleHeight(wx, wz - eps);
251
+ const nx = -hx;
252
+ const nz = -hz;
253
+ const ny = 2 * eps;
254
+ const length = Math.hypot(nx, ny, nz) || 1;
255
+ return [nx / length, ny / length, nz / length];
256
+ }
257
+ function autoPaint(rule, record) {
258
+ let painted = 0;
259
+ for (let gz = 0; gz < rows; gz += 1) {
260
+ for (let gx = 0; gx < cols; gx += 1) {
261
+ const wx = cellWorldX(gx);
262
+ const wz = cellWorldZ(gz);
263
+ const height = sampleHeight(wx, wz);
264
+ if (rule.minHeight !== undefined && height < rule.minHeight)
265
+ continue;
266
+ if (rule.maxHeight !== undefined && height > rule.maxHeight)
267
+ continue;
268
+ if (rule.minSlope !== undefined || rule.maxSlope !== undefined) {
269
+ const [nx, ny, nz] = localNormal(wx, wz);
270
+ const slope = Math.hypot(nx, nz) / Math.max(ny, 1e-9);
271
+ if (rule.minSlope !== undefined && slope < rule.minSlope)
272
+ continue;
273
+ if (rule.maxSlope !== undefined && slope > rule.maxSlope)
274
+ continue;
275
+ }
276
+ if (writeSurface(gz * cols + gx, rule.surface, record))
277
+ painted += 1;
278
+ }
279
+ }
280
+ return painted;
281
+ }
282
+ function fillSurface(surface, record) {
283
+ let painted = 0;
284
+ for (let index = 0; index < surfaces.length; index += 1) {
285
+ if (writeSurface(index, surface, record))
286
+ painted += 1;
287
+ }
288
+ return painted;
289
+ }
290
+ function cellIndexAt(x, z) {
116
291
  const gx = Math.floor(gridX(x));
117
292
  const gz = Math.floor(gridZ(z));
118
293
  if (gx < 0 || gx >= cols || gz < 0 || gz >= rows)
294
+ return -1;
295
+ return gz * cols + gx;
296
+ }
297
+ function dominantLayerAt(cell) {
298
+ if (weights === null || layers.length === 0)
299
+ return -1;
300
+ const base = cell * layers.length;
301
+ let best = -1;
302
+ let bestWeight = 0;
303
+ for (let l = 0; l < layers.length; l += 1) {
304
+ const w = weights[base + l];
305
+ if (w > bestWeight) {
306
+ bestWeight = w;
307
+ best = l;
308
+ }
309
+ }
310
+ return best;
311
+ }
312
+ function surfaceAt(x, z) {
313
+ const cell = cellIndexAt(x, z);
314
+ if (cell < 0)
119
315
  return null;
120
- return surfaces[gz * cols + gx] ?? null;
316
+ const dominant = dominantLayerAt(cell);
317
+ if (dominant >= 0)
318
+ return layers[dominant].surface;
319
+ return surfaces[cell] ?? null;
320
+ }
321
+ function weightsAt(x, z) {
322
+ const cell = cellIndexAt(x, z);
323
+ if (cell < 0 || weights === null || layers.length === 0)
324
+ return [];
325
+ const base = cell * layers.length;
326
+ return Array.from({ length: layers.length }, (_, l) => weights[base + l]);
327
+ }
328
+ /** Materializes the weight buffer, seeding each cell to weight 1 on its current dominant surface. */
329
+ function ensureWeights() {
330
+ if (weights !== null)
331
+ return weights;
332
+ const buffer = new Float32Array(cellCount * Math.max(1, layers.length));
333
+ if (layers.length > 0) {
334
+ for (let cell = 0; cell < cellCount; cell += 1) {
335
+ const surface = surfaces[cell];
336
+ const layer = surface === null ? -1 : layers.findIndex((entry) => entry.surface === surface);
337
+ if (layer >= 0)
338
+ buffer[cell * layers.length + layer] = 1;
339
+ }
340
+ }
341
+ weights = buffer;
342
+ return buffer;
343
+ }
344
+ function blendCell(cell, target, amount, record) {
345
+ const buffer = ensureWeights();
346
+ const L = layers.length;
347
+ const base = cell * L;
348
+ const prevTarget = buffer[base + target];
349
+ const nextTarget = prevTarget + (1 - prevTarget) * amount;
350
+ if (nextTarget === prevTarget)
351
+ return false;
352
+ let othersSum = 0;
353
+ for (let l = 0; l < L; l += 1)
354
+ if (l !== target)
355
+ othersSum += buffer[base + l];
356
+ const remainder = 1 - nextTarget;
357
+ for (let l = 0; l < L; l += 1) {
358
+ const prev = buffer[base + l];
359
+ const next = l === target ? nextTarget : othersSum > 0 ? (prev / othersSum) * remainder : 0;
360
+ if (next !== prev) {
361
+ buffer[base + l] = next;
362
+ record?.(base + l, prev, next);
363
+ }
364
+ }
365
+ return true;
366
+ }
367
+ function blendPaint(edit, record) {
368
+ const surface = edit.surface;
369
+ if (surface === undefined || layers.length === 0)
370
+ return 0;
371
+ const target = layers.findIndex((entry) => entry.surface === surface);
372
+ if (target < 0)
373
+ return 0;
374
+ const falloff = edit.falloff ?? "smooth";
375
+ const shape = edit.shape ?? "circle";
376
+ const strength = edit.strength ?? 1;
377
+ const rCells = Math.ceil(edit.radius / cellSize) + 1;
378
+ const cx = gridX(edit.center[0]);
379
+ const cz = gridZ(edit.center[1]);
380
+ let painted = 0;
381
+ for (let iz = -rCells; iz <= rCells; iz += 1) {
382
+ const gz = Math.floor(cz) + iz;
383
+ if (gz < 0 || gz >= rows)
384
+ continue;
385
+ for (let ix = -rCells; ix <= rCells; ix += 1) {
386
+ const gx = Math.floor(cx) + ix;
387
+ if (gx < 0 || gx >= cols)
388
+ continue;
389
+ const dx = cellWorldX(gx) - edit.center[0];
390
+ const dz = cellWorldZ(gz) - edit.center[1];
391
+ const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
392
+ const weight = brushWeight(dist, edit.radius, falloff);
393
+ if (weight <= 0)
394
+ continue;
395
+ if (blendCell(gz * cols + gx, target, weight * strength, record))
396
+ painted += 1;
397
+ }
398
+ }
399
+ return painted;
400
+ }
401
+ function runHeightEdit(edit, record) {
402
+ if (edit.mode === "ramp")
403
+ return editRamp(edit, record);
404
+ return editHeight(edit, record);
405
+ }
406
+ function surfaceDeltaFrom(run) {
407
+ const indices = [];
408
+ const before = [];
409
+ const after = [];
410
+ run((index, prev, next) => {
411
+ indices.push(index);
412
+ before.push(prev);
413
+ after.push(next);
414
+ });
415
+ return { indices, before, after };
121
416
  }
122
417
  const field = {
418
+ cols,
419
+ rows,
420
+ cellSize,
123
421
  sampleHeight,
124
422
  sampleNormal,
125
423
  ...(base.bounds === undefined ? {} : { bounds: base.bounds }),
126
424
  ...(base.waterLevel === undefined ? {} : { waterLevel: base.waterLevel }),
127
425
  apply(edit) {
128
- return edit.mode === "paint" ? paintSurface(edit) : editHeight(edit);
426
+ return edit.mode === "paint" ? paintSurface(edit, null) : runHeightEdit(edit, null);
427
+ },
428
+ applyRecording(edit, record) {
429
+ return runHeightEdit(edit, record);
430
+ },
431
+ editDelta(edit) {
432
+ const indices = [];
433
+ const before = [];
434
+ const after = [];
435
+ runHeightEdit(edit, (index, prev, next) => {
436
+ indices.push(index);
437
+ before.push(prev);
438
+ after.push(next);
439
+ });
440
+ return { indices, before, after };
441
+ },
442
+ applyDelta(delta) {
443
+ for (let i = 0; i < delta.indices.length; i += 1)
444
+ offsets[delta.indices[i]] = delta.after[i];
445
+ },
446
+ revertDelta(delta) {
447
+ for (let i = 0; i < delta.indices.length; i += 1)
448
+ offsets[delta.indices[i]] = delta.before[i];
449
+ },
450
+ paintRecording(edit, record) {
451
+ return paintSurface(edit, record);
452
+ },
453
+ paintDelta(edit) {
454
+ return surfaceDeltaFrom((record) => paintSurface(edit, record));
455
+ },
456
+ fillSurfaceDelta(surface) {
457
+ return surfaceDeltaFrom((record) => fillSurface(surface, record));
458
+ },
459
+ autoPaintDelta(rule) {
460
+ return surfaceDeltaFrom((record) => autoPaint(rule, record));
461
+ },
462
+ applySurfaceDelta(delta) {
463
+ for (let i = 0; i < delta.indices.length; i += 1)
464
+ surfaces[delta.indices[i]] = delta.after[i] ?? null;
465
+ },
466
+ revertSurfaceDelta(delta) {
467
+ for (let i = 0; i < delta.indices.length; i += 1)
468
+ surfaces[delta.indices[i]] = delta.before[i] ?? null;
129
469
  },
470
+ get layers() {
471
+ return layers;
472
+ },
473
+ setLayers(next) {
474
+ layers = next.map((layer) => ({ ...layer }));
475
+ // Layer count changed shape: drop the weight buffer, blends re-seed from surfaces on demand.
476
+ weights = null;
477
+ },
478
+ blendRecording(edit, record) {
479
+ return blendPaint(edit, record);
480
+ },
481
+ blendPaintDelta(edit) {
482
+ const indices = [];
483
+ const before = [];
484
+ const after = [];
485
+ blendPaint(edit, (index, prev, next) => {
486
+ indices.push(index);
487
+ before.push(prev);
488
+ after.push(next);
489
+ });
490
+ return { layerCount: layers.length, indices, before, after };
491
+ },
492
+ applyWeightDelta(delta) {
493
+ if (delta.layerCount !== layers.length)
494
+ return;
495
+ const buffer = ensureWeights();
496
+ for (let i = 0; i < delta.indices.length; i += 1)
497
+ buffer[delta.indices[i]] = delta.after[i];
498
+ },
499
+ revertWeightDelta(delta) {
500
+ if (delta.layerCount !== layers.length)
501
+ return;
502
+ const buffer = ensureWeights();
503
+ for (let i = 0; i < delta.indices.length; i += 1)
504
+ buffer[delta.indices[i]] = delta.before[i];
505
+ },
506
+ weightsAt,
130
507
  heightOffsetAt,
131
508
  surfaceAt,
132
509
  reset() {
133
510
  offsets.fill(0);
134
511
  surfaces.fill(null);
512
+ weights = null;
135
513
  },
136
514
  snapshot() {
137
515
  return {
@@ -141,6 +519,8 @@ export function createEditableTerrain(config) {
141
519
  rows,
142
520
  offsets: Array.from(offsets),
143
521
  surfaces: surfaces.slice(),
522
+ ...(layers.length === 0 ? {} : { layers: layers.map((layer) => ({ ...layer })) }),
523
+ ...(weights === null ? {} : { weights: Array.from(weights) }),
144
524
  };
145
525
  },
146
526
  restore(snapshot) {
@@ -150,10 +530,265 @@ export function createEditableTerrain(config) {
150
530
  const surfaceCount = Math.min(surfaces.length, snapshot.surfaces.length);
151
531
  for (let index = 0; index < surfaceCount; index += 1)
152
532
  surfaces[index] = snapshot.surfaces[index] ?? null;
533
+ layers = (snapshot.layers ?? []).map((layer) => ({ ...layer }));
534
+ if (snapshot.weights === undefined || layers.length === 0) {
535
+ weights = null;
536
+ }
537
+ else {
538
+ const buffer = new Float32Array(cellCount * layers.length);
539
+ const copy = Math.min(buffer.length, snapshot.weights.length);
540
+ for (let index = 0; index < copy; index += 1)
541
+ buffer[index] = snapshot.weights[index];
542
+ weights = buffer;
543
+ }
153
544
  },
154
545
  };
155
546
  return field;
156
547
  }
548
+ /** Opens a stroke recorder over `terrain`; stamp edits into it, then read one net delta. */
549
+ export function beginTerraformStroke(terrain) {
550
+ const first = new Map();
551
+ const latest = new Map();
552
+ return {
553
+ stamp(edit) {
554
+ return terrain.applyRecording(edit, (index, before, after) => {
555
+ if (!first.has(index))
556
+ first.set(index, before);
557
+ latest.set(index, after);
558
+ });
559
+ },
560
+ delta() {
561
+ const indices = [];
562
+ const before = [];
563
+ const after = [];
564
+ for (const [index, next] of latest) {
565
+ const prev = first.get(index);
566
+ if (next === prev)
567
+ continue;
568
+ indices.push(index);
569
+ before.push(prev);
570
+ after.push(next);
571
+ }
572
+ return { indices, before, after };
573
+ },
574
+ isEmpty() {
575
+ return latest.size === 0;
576
+ },
577
+ };
578
+ }
579
+ /** Opens a paint-stroke recorder over `terrain`; stamp paint edits into it, then read one net delta. */
580
+ export function beginSurfaceStroke(terrain) {
581
+ const first = new Map();
582
+ const latest = new Map();
583
+ return {
584
+ stamp(edit) {
585
+ return terrain.paintRecording(edit, (index, before, after) => {
586
+ if (!first.has(index))
587
+ first.set(index, before);
588
+ latest.set(index, after);
589
+ });
590
+ },
591
+ delta() {
592
+ const indices = [];
593
+ const before = [];
594
+ const after = [];
595
+ for (const [index, next] of latest) {
596
+ const prev = first.get(index) ?? null;
597
+ if (next === prev)
598
+ continue;
599
+ indices.push(index);
600
+ before.push(prev);
601
+ after.push(next);
602
+ }
603
+ return { indices, before, after };
604
+ },
605
+ isEmpty() {
606
+ return latest.size === 0;
607
+ },
608
+ };
609
+ }
610
+ /** Returns a new snapshot with a surface delta's `after` ids applied (copy-on-write). */
611
+ export function applySurfaceDeltaToSnapshot(snapshot, delta) {
612
+ const surfaces = snapshot.surfaces.slice();
613
+ for (let i = 0; i < delta.indices.length; i += 1)
614
+ surfaces[delta.indices[i]] = delta.after[i] ?? null;
615
+ return { ...snapshot, surfaces };
616
+ }
617
+ /** Returns a new snapshot with a surface delta's `before` ids restored (copy-on-write undo). */
618
+ export function revertSurfaceDeltaFromSnapshot(snapshot, delta) {
619
+ const surfaces = snapshot.surfaces.slice();
620
+ for (let i = 0; i < delta.indices.length; i += 1)
621
+ surfaces[delta.indices[i]] = delta.before[i] ?? null;
622
+ return { ...snapshot, surfaces };
623
+ }
624
+ /** The distinct non-null surface ids painted in a snapshot, in first-seen order. */
625
+ function distinctSurfaces(snapshot) {
626
+ const seen = new Set();
627
+ const out = [];
628
+ for (const surface of snapshot.surfaces) {
629
+ if (surface !== null && !seen.has(surface)) {
630
+ seen.add(surface);
631
+ out.push(surface);
632
+ }
633
+ }
634
+ return out;
635
+ }
636
+ /**
637
+ * Upgrades a pre-2.0 snapshot in place-safe (copy-on-write) form: derives a {@link TerrainMaterialLayer}
638
+ * stack from the distinct painted surfaces (first-seen order, default params) when none exists.
639
+ * Leaves the lazy `weights` buffer absent — a single-layer terrain stays compact until blended.
640
+ * Idempotent: a snapshot that already carries `layers` is returned unchanged.
641
+ */
642
+ export function migrateTerrainSnapshot(snapshot) {
643
+ if (snapshot.layers !== undefined)
644
+ return snapshot;
645
+ const layers = distinctSurfaces(snapshot).map((surface) => ({
646
+ id: surface,
647
+ surface,
648
+ roughness: 0.9,
649
+ tiling: 8,
650
+ }));
651
+ return { ...snapshot, layers };
652
+ }
653
+ /** Materializes a snapshot's blend weights, seeding weight 1 on each cell's dominant surface. */
654
+ function materializeSnapshotWeights(snapshot) {
655
+ const layers = snapshot.layers ?? [];
656
+ const cells = snapshot.cols * snapshot.rows;
657
+ const buffer = new Array(cells * Math.max(1, layers.length)).fill(0);
658
+ if (snapshot.weights !== undefined) {
659
+ const copy = Math.min(buffer.length, snapshot.weights.length);
660
+ for (let i = 0; i < copy; i += 1)
661
+ buffer[i] = snapshot.weights[i];
662
+ return buffer;
663
+ }
664
+ if (layers.length > 0) {
665
+ for (let cell = 0; cell < cells; cell += 1) {
666
+ const surface = snapshot.surfaces[cell] ?? null;
667
+ const layer = surface === null ? -1 : layers.findIndex((entry) => entry.surface === surface);
668
+ if (layer >= 0)
669
+ buffer[cell * layers.length + layer] = 1;
670
+ }
671
+ }
672
+ return buffer;
673
+ }
674
+ /**
675
+ * Returns a new snapshot with a weight delta's `after` values applied (materializing weights first).
676
+ * @internal — undo/redo plumbing behind the `blendTerrain` command.
677
+ */
678
+ export function applyWeightDeltaToSnapshot(snapshot, delta) {
679
+ if (delta.layerCount !== (snapshot.layers?.length ?? 0))
680
+ return snapshot;
681
+ const weights = materializeSnapshotWeights(snapshot);
682
+ for (let i = 0; i < delta.indices.length; i += 1)
683
+ weights[delta.indices[i]] = delta.after[i];
684
+ return { ...snapshot, weights };
685
+ }
686
+ /**
687
+ * Returns a new snapshot with a weight delta's `before` values restored (copy-on-write undo).
688
+ * @internal — undo/redo plumbing behind the `blendTerrain` command.
689
+ */
690
+ export function revertWeightDeltaFromSnapshot(snapshot, delta) {
691
+ if (delta.layerCount !== (snapshot.layers?.length ?? 0))
692
+ return snapshot;
693
+ const weights = materializeSnapshotWeights(snapshot);
694
+ for (let i = 0; i < delta.indices.length; i += 1)
695
+ weights[delta.indices[i]] = delta.before[i];
696
+ return { ...snapshot, weights };
697
+ }
698
+ /**
699
+ * Opens a blend-stroke recorder over `terrain`; stamp blend edits into it, then read one net delta.
700
+ * @internal — the stroke recorder behind the blend brush and the `blendTerrain` command.
701
+ */
702
+ export function beginBlendStroke(terrain) {
703
+ const first = new Map();
704
+ const latest = new Map();
705
+ return {
706
+ stamp(edit) {
707
+ return terrain.blendRecording(edit, (index, before, after) => {
708
+ if (!first.has(index))
709
+ first.set(index, before);
710
+ latest.set(index, after);
711
+ });
712
+ },
713
+ delta() {
714
+ const indices = [];
715
+ const before = [];
716
+ const after = [];
717
+ for (const [index, next] of latest) {
718
+ const prev = first.get(index);
719
+ if (next === prev)
720
+ continue;
721
+ indices.push(index);
722
+ before.push(prev);
723
+ after.push(next);
724
+ }
725
+ return { layerCount: terrain.layers.length, indices, before, after };
726
+ },
727
+ isEmpty() {
728
+ return latest.size === 0;
729
+ },
730
+ };
731
+ }
732
+ /**
733
+ * The world-space {@link Aabb} spanning the vertices a stroke's `indices` touched (indices into the
734
+ * `(cols+1)×(rows+1)` offset grid), expanded by `margin` meters. Drives partial preview-mesh
735
+ * rebuilds — only the dirty submesh re-samples instead of the whole heightfield. Null if empty.
736
+ * @internal — the editor's preview-mesh rebuild uses this; not a game-facing primitive.
737
+ */
738
+ export function dirtyBoundsFromIndices(snapshot, indices, margin = 0) {
739
+ if (indices.length === 0)
740
+ return null;
741
+ const vertsX = snapshot.cols + 1;
742
+ const spanX = Math.max(snapshot.bounds.maxX - snapshot.bounds.minX, snapshot.cellSize);
743
+ const spanZ = Math.max(snapshot.bounds.maxZ - snapshot.bounds.minZ, snapshot.cellSize);
744
+ let minX = Infinity;
745
+ let minZ = Infinity;
746
+ let maxX = -Infinity;
747
+ let maxZ = -Infinity;
748
+ for (const index of indices) {
749
+ const gx = index % vertsX;
750
+ const gz = Math.floor(index / vertsX);
751
+ const wx = snapshot.bounds.minX + (gx / snapshot.cols) * spanX;
752
+ const wz = snapshot.bounds.minZ + (gz / snapshot.rows) * spanZ;
753
+ if (wx < minX)
754
+ minX = wx;
755
+ if (wx > maxX)
756
+ maxX = wx;
757
+ if (wz < minZ)
758
+ minZ = wz;
759
+ if (wz > maxZ)
760
+ maxZ = wz;
761
+ }
762
+ return { minX: minX - margin, minZ: minZ - margin, maxX: maxX + margin, maxZ: maxZ + margin };
763
+ }
764
+ /** A fresh, unedited terrain snapshot sized to `bounds`/`cellSize` — the seed for a new sculpt document. */
765
+ export function createTerrainSnapshot(config) {
766
+ return createEditableTerrain(config).snapshot();
767
+ }
768
+ /** Rebuilds a live {@link EditableTerrain} from a snapshot, layered over `base` ground. */
769
+ export function editableTerrainFromSnapshot(snapshot, base) {
770
+ const terrain = createEditableTerrain({
771
+ bounds: snapshot.bounds,
772
+ cellSize: snapshot.cellSize,
773
+ ...(base === undefined ? {} : { base }),
774
+ });
775
+ terrain.restore(snapshot);
776
+ return terrain;
777
+ }
778
+ /** Returns a new snapshot with a delta's `after` offsets applied (copy-on-write — inputs untouched). */
779
+ export function applyDeltaToSnapshot(snapshot, delta) {
780
+ const offsets = snapshot.offsets.slice();
781
+ for (let i = 0; i < delta.indices.length; i += 1)
782
+ offsets[delta.indices[i]] = delta.after[i];
783
+ return { ...snapshot, offsets };
784
+ }
785
+ /** Returns a new snapshot with a delta's `before` offsets restored (copy-on-write undo). */
786
+ export function revertDeltaFromSnapshot(snapshot, delta) {
787
+ const offsets = snapshot.offsets.slice();
788
+ for (let i = 0; i < delta.indices.length; i += 1)
789
+ offsets[delta.indices[i]] = delta.before[i];
790
+ return { ...snapshot, offsets };
791
+ }
157
792
  export function createTerraformBrush(terrain, config = {}) {
158
793
  let radius = config.radius ?? 3;
159
794
  let strength = config.strength ?? 0.5;