@thunderclap-engine/engine 0.0.0-stage → 1.1.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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,548 @@
1
+ /**
2
+ * Music on a beat grid: looping stems started sample-accurately on the next beat or bar from the
3
+ * context clock, grouped in layers, crossfaded by `fadeBeats` and by an intensity level. Sits on
4
+ * the mixer's buses; it owns its own `AudioBufferSourceNode`s so they can start at an offset
5
+ * that keeps every stem in phase with the grid. Each playing stem is registered with the mixer
6
+ * through a `Voice` adapter, so `audio.voices('music')`, `stopAll` and `pause` see it.
7
+ *
8
+ * A stem with a `url` can also be streamed (`play(name, { stream: true })`, ws/audio-4): a
9
+ * `MediaElementAudioSourceNode` over an `<audio>` element the page decodes as it goes, so a long
10
+ * track costs no decoded buffer. The grid then follows the element's `currentTime`.
11
+ */
12
+ import { rampParamTo, VoiceSends, } from './audio.js';
13
+ /** Gains of a sorted run of stems (by `lo`) at `level`, linear crossfades, summing to 1. */
14
+ export function intensityGains(ranges, level, out) {
15
+ const n = ranges.length;
16
+ out.length = n;
17
+ if (n === 0)
18
+ return out;
19
+ const x = Math.min(1, Math.max(0, level));
20
+ for (let i = 0; i < n; i++)
21
+ out[i] = 0;
22
+ if (x <= ranges[0][1] || n === 1) {
23
+ out[0] = 1;
24
+ if (n === 1)
25
+ return out;
26
+ }
27
+ for (let i = 0; i < n - 1; i++) {
28
+ const hi = ranges[i][1];
29
+ const nextLo = ranges[i + 1][0];
30
+ const a = Math.min(hi, nextLo);
31
+ const b = Math.max(hi, nextLo);
32
+ if (x < a)
33
+ break;
34
+ if (x >= b) {
35
+ out[i] = 0;
36
+ out[i + 1] = 1;
37
+ continue;
38
+ }
39
+ // In the transition between stem i and stem i+1.
40
+ const t = b > a ? (x - a) / (b - a) : 1;
41
+ out[i] = 1 - t;
42
+ out[i + 1] = t;
43
+ break;
44
+ }
45
+ return out;
46
+ }
47
+ export function createMusic(mixer, opts) {
48
+ const bpm = opts.bpm;
49
+ const beatsPerBar = Math.max(1, Math.round(opts.beatsPerBar ?? 4));
50
+ const bus = opts.bus ?? 'music';
51
+ const beatDuration = 60 / bpm;
52
+ const intensityFade = (opts.intensityFadeBeats ?? 1) * beatDuration;
53
+ const stems = new Map();
54
+ const active = new Map();
55
+ const pending = [];
56
+ const beatListeners = new Set();
57
+ const playingNames = [];
58
+ const scratchRanges = [];
59
+ const scratchGains = [];
60
+ const scratchStems = [];
61
+ const scratchLayers = [];
62
+ /** Streams fading out, paused once their `stopAt` passes. */
63
+ const stoppingStreams = [];
64
+ let origin = -1;
65
+ /** The streamed stem whose element clock the grid follows (the first one playing). */
66
+ let clockStream = null;
67
+ const makeElement = opts.createElement ??
68
+ (typeof document !== 'undefined'
69
+ ? () => document.createElement('audio')
70
+ : null);
71
+ let lastBeat = -1;
72
+ let intensity = 0;
73
+ let unsub = null;
74
+ let disposed = false;
75
+ const ctxTime = () => mixer.context?.currentTime ?? 0;
76
+ function ensureGrid() {
77
+ const ctx = mixer.context;
78
+ if (!ctx || mixer.state !== 'running' || !mixer.busNode(bus))
79
+ return false;
80
+ if (origin < 0) {
81
+ origin = ctx.currentTime;
82
+ lastBeat = -1;
83
+ }
84
+ return true;
85
+ }
86
+ function startTime(at) {
87
+ const now = ctxTime();
88
+ if (at === 'now')
89
+ return now;
90
+ const unit = at === 'beat' ? beatDuration : beatDuration * beatsPerBar;
91
+ const k = Math.ceil((now - origin) / unit - 1e-9);
92
+ return origin + Math.max(k, 0) * unit;
93
+ }
94
+ function layerOf(stem, o) {
95
+ return o.layer ?? stem.layer ?? stem.name;
96
+ }
97
+ /** Recompute the intensity gain of every playing stem in `layer` and ramp to it. */
98
+ function applyLayer(layer, fade, when) {
99
+ scratchStems.length = 0;
100
+ for (const p of active.values()) {
101
+ if (p.layer === layer && !p.stopping && p.stem.intensity)
102
+ scratchStems.push(p);
103
+ }
104
+ scratchStems.sort((a, b) => a.stem.intensity[0] - b.stem.intensity[0]);
105
+ scratchRanges.length = 0;
106
+ for (const p of scratchStems) {
107
+ const r = p.stem.intensity;
108
+ scratchRanges.push([r[0], r[1]]);
109
+ }
110
+ intensityGains(scratchRanges, intensity, scratchGains);
111
+ for (let i = 0; i < scratchStems.length; i++) {
112
+ const p = scratchStems[i];
113
+ ramp(p, scratchGains[i] * p.playGain, fade, when);
114
+ }
115
+ }
116
+ function ramp(p, target, fade, when) {
117
+ if (p.target === target && fade > 0)
118
+ return;
119
+ p.target = target;
120
+ const g = p.gainNode.gain;
121
+ g.cancelScheduledValues(when);
122
+ g.setValueAtTime(g.value, when);
123
+ if (fade > 0)
124
+ g.linearRampToValueAtTime(target, when + fade);
125
+ else
126
+ g.setValueAtTime(target, when);
127
+ }
128
+ function startStem(stem, o) {
129
+ const ctx = mixer.context;
130
+ const busNode = mixer.busNode(bus);
131
+ const layer = layerOf(stem, o);
132
+ const streamed = o.stream === true &&
133
+ stem.url !== undefined &&
134
+ makeElement !== null &&
135
+ typeof ctx.createMediaElementSource === 'function';
136
+ if (!streamed && !stem.buffer) {
137
+ console.warn(`[audio] stem "${stem.name}" has no buffer${stem.url ? ' and cannot stream here' : ''}`);
138
+ return;
139
+ }
140
+ // A stream cannot be scheduled ahead: it starts now, and so do the fades around it.
141
+ const when = streamed ? ctxTime() : startTime(o.at ?? 'bar');
142
+ const existing = active.get(stem.name);
143
+ if (existing)
144
+ stopOne(existing, (o.fadeBeats ?? 1) * beatDuration, when);
145
+ // A rangeless stem replaces the layer's other rangeless stems (a crossfade).
146
+ let replaced = false;
147
+ if (!stem.intensity) {
148
+ for (const p of active.values()) {
149
+ if (p.layer === layer && !p.stopping && !p.stem.intensity && p.stem.name !== stem.name) {
150
+ stopOne(p, (o.fadeBeats ?? 1) * beatDuration, when);
151
+ replaced = true;
152
+ }
153
+ }
154
+ }
155
+ const fade = (o.fadeBeats ?? (replaced ? 1 : 0)) * beatDuration;
156
+ const gainNode = ctx.createGain();
157
+ const playGain = o.gain ?? 1;
158
+ const p = {
159
+ stem,
160
+ layer,
161
+ src: null,
162
+ el: null,
163
+ elNode: null,
164
+ stopAt: 0,
165
+ gainNode,
166
+ filterNode: null,
167
+ playGain,
168
+ target: 0,
169
+ stopping: false,
170
+ voice: null,
171
+ unregister: null,
172
+ sends: null,
173
+ };
174
+ p.voice = stemVoice(p);
175
+ const target = stem.intensity ? 0 : playGain;
176
+ if (fade > 0) {
177
+ gainNode.gain.setValueAtTime(0, when);
178
+ gainNode.gain.linearRampToValueAtTime(target, when + fade);
179
+ }
180
+ else {
181
+ gainNode.gain.setValueAtTime(target, when);
182
+ }
183
+ p.target = target;
184
+ gainNode.connect(busNode);
185
+ if (streamed) {
186
+ const el = makeElement();
187
+ el.src = stem.url;
188
+ el.loop = true;
189
+ const node = ctx.createMediaElementSource(el);
190
+ node.connect(gainNode);
191
+ p.el = el;
192
+ p.elNode = node;
193
+ // In phase with the grid where the element's length is known; else it sets the grid.
194
+ const duration = el.duration;
195
+ if (Number.isFinite(duration) && duration > 0) {
196
+ el.currentTime = (((when - origin) % duration) + duration) % duration;
197
+ }
198
+ if (clockStream === null) {
199
+ clockStream = p;
200
+ if (active.size === 0) {
201
+ origin = when - (Number.isFinite(el.currentTime) ? el.currentTime : 0);
202
+ lastBeat = -1;
203
+ }
204
+ }
205
+ void Promise.resolve(el.play()).catch(() => { });
206
+ }
207
+ else {
208
+ const src = ctx.createBufferSource();
209
+ src.buffer = stem.buffer;
210
+ src.loop = true;
211
+ p.src = src;
212
+ src.connect(gainNode);
213
+ src.onended = () => endOne(p);
214
+ // Keep the loop in phase with the grid: offset by the grid time elapsed, modulo the loop.
215
+ const duration = stem.buffer.duration;
216
+ const offset = duration > 0 ? (((when - origin) % duration) + duration) % duration : 0;
217
+ src.start(when, offset);
218
+ }
219
+ active.set(stem.name, p);
220
+ p.unregister = mixer.register(p.voice, bus);
221
+ if (stem.intensity)
222
+ applyLayer(layer, fade, when);
223
+ }
224
+ /** The stem's source is done (buffer ended, or stream paused after its fade): tear it down. */
225
+ function endOne(p) {
226
+ p.sends?.dispose();
227
+ p.sends = null;
228
+ p.gainNode.disconnect();
229
+ if (p.filterNode) {
230
+ p.filterNode.disconnect();
231
+ mixer.releaseBiquad(p.filterNode);
232
+ p.filterNode = null;
233
+ }
234
+ if (p.el) {
235
+ p.el.pause();
236
+ p.elNode?.disconnect();
237
+ p.el.src = '';
238
+ if (clockStream === p)
239
+ clockStream = null;
240
+ }
241
+ p.unregister?.();
242
+ p.unregister = null;
243
+ if (active.get(p.stem.name) === p)
244
+ active.delete(p.stem.name);
245
+ }
246
+ /** Wire gain → [filter] → bus and move the sends to the tail. */
247
+ function relink(p) {
248
+ const busNode = mixer.busNode(bus);
249
+ const ctx = mixer.context;
250
+ if (!busNode || !ctx)
251
+ return;
252
+ p.gainNode.disconnect();
253
+ p.filterNode?.disconnect();
254
+ const tail = p.filterNode ? p.gainNode.connect(p.filterNode) : p.gainNode;
255
+ tail.connect(busNode);
256
+ if (p.sends) {
257
+ if (p.sends.attached)
258
+ p.sends.retarget(tail);
259
+ else
260
+ p.sends.attach(ctx, tail);
261
+ }
262
+ }
263
+ /** Streams cannot `stop(when)`: pause the ones whose fade has ended. */
264
+ function reapStreams(now) {
265
+ for (let i = stoppingStreams.length - 1; i >= 0; i--) {
266
+ const p = stoppingStreams[i];
267
+ if (now < p.stopAt)
268
+ continue;
269
+ stoppingStreams.splice(i, 1);
270
+ endOne(p);
271
+ }
272
+ }
273
+ /** Follow the clock stream's element: the grid origin is its `currentTime` ago, modulo its length. */
274
+ function followClock(now) {
275
+ const p = clockStream;
276
+ if (!p || p.stopping)
277
+ return;
278
+ const el = p.el;
279
+ const t = el.currentTime;
280
+ const duration = el.duration;
281
+ if (!Number.isFinite(t) || el.paused)
282
+ return;
283
+ let o = now - t;
284
+ if (Number.isFinite(duration) && duration > 0 && origin >= 0) {
285
+ // The element wrapped; keep the origin the candidate nearest the one we had.
286
+ o -= Math.round((o - origin) / duration) * duration;
287
+ }
288
+ origin = o;
289
+ }
290
+ /** The `Voice` the mixer sees for a stem: stop fades in seconds, gain and rate write its nodes. */
291
+ function stemVoice(p) {
292
+ const voice = {
293
+ rate: 1,
294
+ stop(fade = 0) {
295
+ stopOne(p, fade, ctxTime());
296
+ },
297
+ setGain(gain, r = 0.05) {
298
+ p.playGain = gain;
299
+ const when = ctxTime();
300
+ if (p.stopping)
301
+ return;
302
+ if (p.stem.intensity)
303
+ applyLayer(p.layer, r, when);
304
+ else
305
+ ramp(p, gain, r, when);
306
+ },
307
+ setRate(rate, r = 0.05) {
308
+ voice.rate = rate;
309
+ if (p.src) {
310
+ rampParamTo(p.src.playbackRate, rate, ctxTime(), r);
311
+ }
312
+ else if (p.el && !p.stopping) {
313
+ // An element cannot run at 0: pause it, and resume where it was.
314
+ if (rate <= 0) {
315
+ p.el.pause();
316
+ }
317
+ else {
318
+ p.el.playbackRate = rate;
319
+ if (p.el.paused)
320
+ void Promise.resolve(p.el.play()).catch(() => { });
321
+ }
322
+ }
323
+ },
324
+ setPosition() { },
325
+ setDirection() { },
326
+ follow() { },
327
+ sendTo(input, amount, r = 0.05) {
328
+ p.sends ??= new VoiceSends();
329
+ const sends = p.sends;
330
+ if (!sends.attached)
331
+ sends.attach(mixer.context, p.filterNode ?? p.gainNode);
332
+ sends.set(input, amount, r);
333
+ },
334
+ filter(kind, o = {}) {
335
+ if (p.stopping)
336
+ return;
337
+ const now = ctxTime();
338
+ const ramp = o.ramp ?? 0.05;
339
+ if (kind === null) {
340
+ const f = p.filterNode;
341
+ if (!f)
342
+ return;
343
+ p.filterNode = null;
344
+ relink(p);
345
+ mixer.releaseBiquad(f);
346
+ return;
347
+ }
348
+ let f = p.filterNode;
349
+ const fresh = f === null;
350
+ f ??= mixer.acquireBiquad();
351
+ if (!f)
352
+ return;
353
+ f.type = kind;
354
+ rampParamTo(f.frequency, o.cutoff ?? 1000, now, fresh ? 0 : ramp);
355
+ rampParamTo(f.Q, o.q ?? 1, now, fresh ? 0 : ramp);
356
+ if (fresh) {
357
+ p.filterNode = f;
358
+ relink(p);
359
+ }
360
+ },
361
+ position: new Float64Array(3),
362
+ get playing() {
363
+ return !p.stopping;
364
+ },
365
+ get sendCount() {
366
+ return p.sends?.count ?? 0;
367
+ },
368
+ occlusion: 0,
369
+ doppler: 1,
370
+ };
371
+ return voice;
372
+ }
373
+ function stopOne(p, fade, when) {
374
+ if (p.stopping)
375
+ return;
376
+ p.stopping = true;
377
+ active.delete(p.stem.name);
378
+ p.unregister?.();
379
+ p.unregister = null;
380
+ const g = p.gainNode.gain;
381
+ g.cancelScheduledValues(when);
382
+ g.setValueAtTime(g.value, when);
383
+ if (fade > 0) {
384
+ g.linearRampToValueAtTime(0, when + fade);
385
+ }
386
+ else {
387
+ g.setValueAtTime(0, when);
388
+ }
389
+ if (p.src) {
390
+ try {
391
+ p.src.stop(when + fade);
392
+ }
393
+ catch {
394
+ // Already stopped.
395
+ }
396
+ }
397
+ else if (p.el) {
398
+ if (clockStream === p) {
399
+ clockStream = null;
400
+ for (const q of active.values())
401
+ if (q.el && !q.stopping)
402
+ clockStream = q;
403
+ }
404
+ p.stopAt = when + fade;
405
+ if (fade > 0)
406
+ stoppingStreams.push(p);
407
+ else
408
+ endOne(p);
409
+ }
410
+ }
411
+ function flushPending() {
412
+ if (!ensureGrid())
413
+ return;
414
+ for (const q of pending)
415
+ startStem(q.stem, q.opts);
416
+ pending.length = 0;
417
+ }
418
+ function watchUnlock() {
419
+ if (unsub)
420
+ return;
421
+ unsub = mixer.onStateChange((s) => {
422
+ if (s === 'running')
423
+ flushPending();
424
+ });
425
+ }
426
+ const music = {
427
+ add(stem) {
428
+ stems.set(stem.name, stem);
429
+ },
430
+ play(stem, o = {}) {
431
+ if (disposed)
432
+ return;
433
+ const s = typeof stem === 'string' ? stems.get(stem) : stem;
434
+ if (!s) {
435
+ console.warn(`[audio] unknown stem "${String(stem)}"`);
436
+ return;
437
+ }
438
+ if (typeof stem !== 'string')
439
+ stems.set(s.name, s);
440
+ if (!ensureGrid()) {
441
+ pending.push({ stem: s, opts: o });
442
+ watchUnlock();
443
+ return;
444
+ }
445
+ startStem(s, o);
446
+ },
447
+ stop(layer, fadeBeats = 1) {
448
+ const fade = fadeBeats * beatDuration;
449
+ const when = ctxTime();
450
+ for (const p of [...active.values()]) {
451
+ if (layer === undefined || p.layer === layer)
452
+ stopOne(p, fade, when);
453
+ }
454
+ if (layer === undefined)
455
+ pending.length = 0;
456
+ else
457
+ for (let i = pending.length - 1; i >= 0; i--) {
458
+ if (layerOf(pending[i].stem, pending[i].opts) === layer)
459
+ pending.splice(i, 1);
460
+ }
461
+ },
462
+ setIntensity(level) {
463
+ intensity = Math.min(1, Math.max(0, Number.isFinite(level) ? level : 0));
464
+ if (origin < 0)
465
+ return;
466
+ const when = ctxTime();
467
+ // One pass per distinct layer that has ranged stems.
468
+ scratchLayers.length = 0;
469
+ for (const p of active.values()) {
470
+ if (!p.stem.intensity || p.stopping || scratchLayers.includes(p.layer))
471
+ continue;
472
+ scratchLayers.push(p.layer);
473
+ }
474
+ for (const layer of scratchLayers)
475
+ applyLayer(layer, intensityFade, when);
476
+ },
477
+ get intensity() {
478
+ return intensity;
479
+ },
480
+ get beat() {
481
+ return origin < 0
482
+ ? 0
483
+ : Math.max(0, Math.floor((ctxTime() - origin) / beatDuration)) % beatsPerBar;
484
+ },
485
+ get bar() {
486
+ return origin < 0
487
+ ? 0
488
+ : Math.floor(Math.max(0, Math.floor((ctxTime() - origin) / beatDuration)) / beatsPerBar);
489
+ },
490
+ get totalBeats() {
491
+ return origin < 0 ? 0 : Math.max(0, Math.floor((ctxTime() - origin) / beatDuration));
492
+ },
493
+ beatDuration,
494
+ nextBeatTime() {
495
+ return origin < 0 ? 0 : startTime('beat');
496
+ },
497
+ nextBarTime() {
498
+ return origin < 0 ? 0 : startTime('bar');
499
+ },
500
+ onBeat(cb) {
501
+ beatListeners.add(cb);
502
+ return () => beatListeners.delete(cb);
503
+ },
504
+ gain(name) {
505
+ return active.get(name)?.target ?? 0;
506
+ },
507
+ get playing() {
508
+ playingNames.length = 0;
509
+ for (const p of active.values())
510
+ playingNames.push(p.stem.name);
511
+ return playingNames;
512
+ },
513
+ update() {
514
+ if (disposed || !ensureGrid())
515
+ return;
516
+ const now = ctxTime();
517
+ if (stoppingStreams.length > 0)
518
+ reapStreams(now);
519
+ if (clockStream)
520
+ followClock(now);
521
+ const total = Math.max(0, Math.floor((now - origin) / beatDuration));
522
+ if (total === lastBeat)
523
+ return;
524
+ if (lastBeat < 0)
525
+ lastBeat = total - 1;
526
+ // Fire each beat crossed since the last update (a long frame skips none).
527
+ for (let b = lastBeat + 1; b <= total; b++) {
528
+ const beat = b % beatsPerBar;
529
+ const bar = Math.floor(b / beatsPerBar);
530
+ for (const fn of beatListeners)
531
+ fn(beat, bar);
532
+ }
533
+ lastBeat = total;
534
+ },
535
+ dispose() {
536
+ disposed = true;
537
+ music.stop(undefined, 0);
538
+ for (const p of stoppingStreams)
539
+ endOne(p);
540
+ stoppingStreams.length = 0;
541
+ beatListeners.clear();
542
+ unsub?.();
543
+ unsub = null;
544
+ origin = -1;
545
+ },
546
+ };
547
+ return music;
548
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Occlusion (ws/audio-4): a game answers one probe per spatial voice with how much of the world
3
+ * sits between the listener and the emitter (0 clear .. 1 occluded); the engine never casts a
4
+ * ray. Each probed voice owns a pooled lowpass whose cutoff and gain ramp with the answer. The
5
+ * probing is round-robin and budgeted, so a scene with many emitters costs a fixed number of
6
+ * probes per `audio.update`. This file holds the scheduler and the curves; the nodes live in
7
+ * `audio.ts` (`VoiceImpl.applyOcclusion`).
8
+ */
9
+ /** The probe's answer, written in place (no allocation per probe). */
10
+ export interface OcclusionResult {
11
+ /** 0 clear .. 1 fully occluded. Clamped. */
12
+ amount: number;
13
+ }
14
+ /** `audio.occlusion`: the game's probe and the filter's range. */
15
+ export interface OcclusionOptions {
16
+ /**
17
+ * Answer how occluded the sound at `emitter` is for the ears at `listener`, into `out.amount`.
18
+ * Called from `audio.update`, at most `budget` times per call, each voice at most once per
19
+ * `interval` seconds. Both positions are live views; copy them if kept.
20
+ */
21
+ probe: (listener: ArrayLike<number>, emitter: ArrayLike<number>, out: OcclusionResult) => void;
22
+ /** Seconds between two probes of one voice, and the ramp time of its filter. Default 0.1. */
23
+ interval?: number;
24
+ /** Lowpass cutoff (Hz) when clear. Default 20000. */
25
+ openCutoff?: number;
26
+ /** Lowpass cutoff (Hz) when fully occluded. Default 600. */
27
+ closedCutoff?: number;
28
+ /** Linear gain when fully occluded (1 when clear). Default 0.4. */
29
+ closedGain?: number;
30
+ /** Most probes per `audio.update`. Default 8. */
31
+ budget?: number;
32
+ }
33
+ export declare const DEFAULT_OCCLUSION: Readonly<Required<Omit<OcclusionOptions, 'probe'>>>;
34
+ /** What the scheduler needs from a voice (a `VoiceImpl` in `audio.ts`). */
35
+ export interface OccludableVoice {
36
+ readonly position: ArrayLike<number>;
37
+ /** Context time of the last probe (-Infinity before the first). */
38
+ occlusionProbedAt: number;
39
+ /** Ramp the voice's filter to `amount` over `ramp` seconds. */
40
+ applyOcclusion(amount: number, cutoff: number, gain: number, ramp: number): void;
41
+ }
42
+ /** Lowpass cutoff for `amount`: log-interpolated between open and closed, so the sweep sounds even. */
43
+ export declare function occlusionCutoff(amount: number, open: number, closed: number): number;
44
+ /** Linear gain for `amount`: 1 down to `closedGain`. */
45
+ export declare function occlusionGain(amount: number, closedGain: number): number;
46
+ /**
47
+ * Round-robin probe scheduler over the engine's occludable voices. `run` walks `voices` from
48
+ * where the last call stopped, probing those due (`interval` since their last probe) until the
49
+ * budget is spent; the rest wait for the next `update`. Allocates nothing per call.
50
+ */
51
+ export declare class OcclusionScheduler {
52
+ private cursor;
53
+ private readonly out;
54
+ run(opts: OcclusionOptions, listener: ArrayLike<number>, voices: readonly OccludableVoice[], now: number): number;
55
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Occlusion (ws/audio-4): a game answers one probe per spatial voice with how much of the world
3
+ * sits between the listener and the emitter (0 clear .. 1 occluded); the engine never casts a
4
+ * ray. Each probed voice owns a pooled lowpass whose cutoff and gain ramp with the answer. The
5
+ * probing is round-robin and budgeted, so a scene with many emitters costs a fixed number of
6
+ * probes per `audio.update`. This file holds the scheduler and the curves; the nodes live in
7
+ * `audio.ts` (`VoiceImpl.applyOcclusion`).
8
+ */
9
+ export const DEFAULT_OCCLUSION = {
10
+ interval: 0.1,
11
+ openCutoff: 20000,
12
+ closedCutoff: 600,
13
+ closedGain: 0.4,
14
+ budget: 8,
15
+ };
16
+ /** Lowpass cutoff for `amount`: log-interpolated between open and closed, so the sweep sounds even. */
17
+ export function occlusionCutoff(amount, open, closed) {
18
+ return open * (closed / open) ** amount;
19
+ }
20
+ /** Linear gain for `amount`: 1 down to `closedGain`. */
21
+ export function occlusionGain(amount, closedGain) {
22
+ return 1 - amount * (1 - closedGain);
23
+ }
24
+ /**
25
+ * Round-robin probe scheduler over the engine's occludable voices. `run` walks `voices` from
26
+ * where the last call stopped, probing those due (`interval` since their last probe) until the
27
+ * budget is spent; the rest wait for the next `update`. Allocates nothing per call.
28
+ */
29
+ export class OcclusionScheduler {
30
+ cursor = 0;
31
+ out = { amount: 0 };
32
+ run(opts, listener, voices, now) {
33
+ const n = voices.length;
34
+ if (n === 0)
35
+ return 0;
36
+ const interval = opts.interval ?? DEFAULT_OCCLUSION.interval;
37
+ const budget = Math.max(1, opts.budget ?? DEFAULT_OCCLUSION.budget);
38
+ const open = opts.openCutoff ?? DEFAULT_OCCLUSION.openCutoff;
39
+ const closed = opts.closedCutoff ?? DEFAULT_OCCLUSION.closedCutoff;
40
+ const closedGain = opts.closedGain ?? DEFAULT_OCCLUSION.closedGain;
41
+ let cursor = this.cursor < n ? this.cursor : 0;
42
+ let probed = 0;
43
+ let visited = 0;
44
+ while (visited < n && probed < budget) {
45
+ const v = voices[cursor];
46
+ cursor = (cursor + 1) % n;
47
+ visited++;
48
+ if (now - v.occlusionProbedAt < interval - 1e-9)
49
+ continue;
50
+ this.out.amount = 0;
51
+ opts.probe(listener, v.position, this.out);
52
+ const a = Math.min(1, Math.max(0, Number.isFinite(this.out.amount) ? this.out.amount : 0));
53
+ v.occlusionProbedAt = now;
54
+ v.applyOcclusion(a, occlusionCutoff(a, open, closed), occlusionGain(a, closedGain), interval);
55
+ probed++;
56
+ // The next call resumes after the last voice probed (skipped ones stay in line).
57
+ this.cursor = cursor;
58
+ }
59
+ return probed;
60
+ }
61
+ }
@@ -0,0 +1,23 @@
1
+ import type { Host } from '../host/types.js';
2
+ import { type SaveStore, type StorageLike } from '../save/index.js';
3
+ import { type AudioEngine, type MixerSettings } from './audio.js';
4
+ export interface PersistMixerOptions {
5
+ game: string;
6
+ /** Default `audio` (key `thunderclap:<game>:audio`), separate from progress saves. */
7
+ slot?: string;
8
+ /** Where the slot is stored (`host.storage`). Default: `defaultHost()`. */
9
+ host?: Pick<Host, 'storage'>;
10
+ /** A fixed storage instead of the host's (tests); `null` keeps it in memory. */
11
+ storage?: StorageLike | null;
12
+ debounceMs?: number;
13
+ autoFlush?: boolean;
14
+ }
15
+ /**
16
+ * Load mixer settings from `engine/save`, apply them to `audio`, and save every change.
17
+ * Returns the store (check `store.persistent` to warn about non-persistent storage) and
18
+ * an `unbind` function.
19
+ */
20
+ export declare function persistMixer(audio: AudioEngine, opts: PersistMixerOptions): {
21
+ store: SaveStore<MixerSettings>;
22
+ unbind: () => void;
23
+ };