1681-engine 0.5.8 → 0.5.10

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 (724) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +3 -0
  3. package/dist/1681-engine.js +526 -409
  4. package/dist/ambient/ColorGrade.js +0 -1
  5. package/dist/ambient/DayClock.js +0 -1
  6. package/dist/ambient/WeatherLayer.js +0 -1
  7. package/dist/ambient/grade.js +0 -1
  8. package/dist/ambient/life/Birds.js +73 -0
  9. package/dist/ambient/life/Butterflies.js +43 -0
  10. package/dist/ambient/life/ChimneySmoke.js +50 -0
  11. package/dist/ambient/life/Drift.js +75 -0
  12. package/dist/ambient/life/Fireflies.js +52 -0
  13. package/dist/ambient/life/lifeMotion.js +49 -0
  14. package/dist/ambient/weather.js +0 -1
  15. package/dist/anim/AnimStateMachine.js +0 -1
  16. package/dist/anim/Animator.js +0 -1
  17. package/dist/anim/animStateCompile.js +0 -1
  18. package/dist/anim/animStateSystem.js +0 -1
  19. package/dist/anim/animationSystem.js +0 -1
  20. package/dist/anim/asepriteSlices.js +0 -1
  21. package/dist/anim/asepriteTags.js +0 -1
  22. package/dist/anim/parseAseprite.js +0 -1
  23. package/dist/assets/AssetLoader.js +177 -34
  24. package/dist/assets/Prepainter.js +61 -0
  25. package/dist/assets/loadAseprite.js +0 -1
  26. package/dist/assets/takeCleanerWorker-DTcI_Jqr.js +0 -1
  27. package/dist/assets/textureAtlas.js +58 -0
  28. package/dist/assets/workerPainter.js +39 -0
  29. package/dist/audio/Footsteps.js +0 -1
  30. package/dist/audio/MusicPlayer.js +0 -1
  31. package/dist/audio/SoundEngine.js +13 -7
  32. package/dist/audio/cleanTake.js +0 -1
  33. package/dist/audio/music/ElementVoice.js +0 -1
  34. package/dist/audio/music/GraphVoice.js +0 -1
  35. package/dist/audio/music/MusicChannel.js +0 -1
  36. package/dist/audio/music/musicVoice.js +0 -1
  37. package/dist/audio/sampleBank.js +0 -1
  38. package/dist/audio/soundTable.js +0 -1
  39. package/dist/audio/synth.js +0 -1
  40. package/dist/cjs/electron/electronLike.d.ts +55 -0
  41. package/dist/cjs/electron/electronLike.js +7 -0
  42. package/dist/cjs/electron/index.d.ts +14 -0
  43. package/dist/cjs/electron/index.js +20 -0
  44. package/dist/cjs/electron/lockdown.d.ts +33 -0
  45. package/dist/cjs/electron/lockdown.js +69 -0
  46. package/dist/cjs/electron/lockdownRules.d.ts +28 -0
  47. package/dist/cjs/electron/lockdownRules.js +61 -0
  48. package/dist/cjs/electron/sessionStore.d.ts +40 -0
  49. package/dist/cjs/electron/sessionStore.js +77 -0
  50. package/dist/cjs/electron/shellId.d.ts +22 -0
  51. package/dist/cjs/electron/shellId.js +31 -0
  52. package/dist/cjs/electron/webFiles.d.ts +13 -0
  53. package/dist/cjs/electron/webFiles.js +52 -0
  54. package/dist/cjs/electron/webPatcher.d.ts +80 -0
  55. package/dist/cjs/electron/webPatcher.js +216 -0
  56. package/dist/cjs/electron/webPointers.d.ts +19 -0
  57. package/dist/cjs/electron/webPointers.js +35 -0
  58. package/dist/cjs/package.json +1 -0
  59. package/dist/cjs/updates/index.d.ts +8 -0
  60. package/dist/cjs/updates/index.js +21 -0
  61. package/dist/cjs/updates/manifest.d.ts +73 -0
  62. package/dist/cjs/updates/manifest.js +67 -0
  63. package/dist/cjs/updates/pickUpdate.d.ts +42 -0
  64. package/dist/cjs/updates/pickUpdate.js +52 -0
  65. package/dist/cjs/updates/webPlan.d.ts +20 -0
  66. package/dist/cjs/updates/webPlan.js +34 -0
  67. package/dist/core/BitmapLabel.js +0 -1
  68. package/dist/core/Camera.js +1 -2
  69. package/dist/core/Clock.js +31 -21
  70. package/dist/core/Graphics.js +0 -1
  71. package/dist/core/Group.js +0 -1
  72. package/dist/core/Image.js +0 -1
  73. package/dist/core/ObjectFactory.js +0 -1
  74. package/dist/core/Pointer.js +46 -24
  75. package/dist/core/Rectangle.js +0 -1
  76. package/dist/core/Stage.js +5 -6
  77. package/dist/core/Text.js +0 -1
  78. package/dist/core/TextureRegistry.js +54 -24
  79. package/dist/core/Timeline.js +0 -1
  80. package/dist/core/TimelineBuilder.js +0 -1
  81. package/dist/core/TimelineChild.js +0 -1
  82. package/dist/core/Tweener.js +0 -1
  83. package/dist/core/atlas.js +0 -1
  84. package/dist/core/atlasPlan.js +0 -1
  85. package/dist/core/blendLayer.js +11 -0
  86. package/dist/core/eases.js +0 -1
  87. package/dist/core/gameObject.js +22 -20
  88. package/dist/core/pixelZoom.js +43 -0
  89. package/dist/core/pixiPatches.js +0 -1
  90. package/dist/core/runDue.js +28 -0
  91. package/dist/core/silhouette.js +49 -0
  92. package/dist/core/timelinePlace.js +0 -1
  93. package/dist/core/tweenProps.js +0 -1
  94. package/dist/debug/DebugOverlay.js +0 -1
  95. package/dist/debug/DrawCallCounter.js +15 -12
  96. package/dist/debug/ErrorLog.js +0 -1
  97. package/dist/debug/FrameStats.js +0 -1
  98. package/dist/debug/inspectWorld.js +0 -1
  99. package/dist/diagnostics/ErrorDeduper.js +0 -1
  100. package/dist/diagnostics/FrameSampler.js +0 -1
  101. package/dist/diagnostics/bootWatch.js +0 -1
  102. package/dist/diagnostics/captureErrors.js +0 -1
  103. package/dist/diagnostics/contextLoss.js +0 -1
  104. package/dist/diagnostics/frameStats.js +0 -1
  105. package/dist/diagnostics/gpuInfo.js +0 -1
  106. package/dist/diagnostics/renderGuard.js +0 -1
  107. package/dist/ecs/SparseSet.js +0 -1
  108. package/dist/ecs/World.js +0 -1
  109. package/dist/ecs/fields.js +0 -1
  110. package/dist/electron/lockdown.js +31 -0
  111. package/dist/electron/lockdownRules.js +42 -0
  112. package/dist/electron/sessionStore.js +48 -0
  113. package/dist/electron/shellId.js +18 -0
  114. package/dist/electron/webFiles.js +36 -0
  115. package/dist/electron/webPatcher.js +147 -0
  116. package/dist/electron/webPointers.js +25 -0
  117. package/dist/electron.js +19 -0
  118. package/dist/engine/Engine.js +42 -18
  119. package/dist/engine/FixedLoop.js +0 -1
  120. package/dist/engine/HiddenPause.js +27 -0
  121. package/dist/engine/Scene.js +0 -1
  122. package/dist/engine/SceneManager.js +0 -1
  123. package/dist/events/EventBus.js +0 -1
  124. package/dist/fx/Afterimages.js +82 -0
  125. package/dist/fx/FloatingText.js +0 -1
  126. package/dist/fx/hitStop.js +0 -1
  127. package/dist/fx/viewCull.js +0 -1
  128. package/dist/i18n/Translator.js +0 -1
  129. package/dist/i18n/extractStrings.js +0 -1
  130. package/dist/i18n/formatMessage.js +0 -1
  131. package/dist/input/Actions.js +29 -21
  132. package/dist/input/AimGesture.js +0 -1
  133. package/dist/input/Keyboard.js +0 -1
  134. package/dist/input/Keymap.js +0 -1
  135. package/dist/input/TargetLock.js +0 -1
  136. package/dist/input/aimAxis.js +0 -1
  137. package/dist/input/haptics.js +50 -0
  138. package/dist/input/keyChord.js +0 -1
  139. package/dist/input/longPress.js +0 -1
  140. package/dist/input/rumble.js +42 -0
  141. package/dist/input/stickAxis.js +0 -1
  142. package/dist/input/touchDetect.js +0 -1
  143. package/dist/light/LightLayer.js +0 -1
  144. package/dist/nav/MinHeap.js +0 -1
  145. package/dist/nav/findPath.js +0 -1
  146. package/dist/nav/navGrid.js +0 -1
  147. package/dist/nav/nearestOpen.js +0 -1
  148. package/dist/nav/straightenPath.js +0 -1
  149. package/dist/net/ApiClient.js +96 -0
  150. package/dist/net/Connection.js +39 -16
  151. package/dist/net/InterpolationBuffer.js +0 -1
  152. package/dist/net/PredictedMover.js +0 -1
  153. package/dist/net/Room.js +0 -1
  154. package/dist/net/RoomHost.js +0 -1
  155. package/dist/net/cadence.js +0 -1
  156. package/dist/net/constants.js +0 -1
  157. package/dist/net/lobby/LobbyClient.js +0 -1
  158. package/dist/net/lobby/LobbyHost.js +0 -1
  159. package/dist/net/lobby/LobbyRooms.js +0 -1
  160. package/dist/net/lobby/Matchmaker.js +0 -1
  161. package/dist/net/movement.js +40 -39
  162. package/dist/net/protocol.js +0 -1
  163. package/dist/net/reconnect.js +10 -0
  164. package/dist/net/serverClock.js +0 -1
  165. package/dist/net/snapshotCodec.js +0 -1
  166. package/dist/parallax/Parallax.js +0 -1
  167. package/dist/parallax/ParallaxLayer.js +0 -1
  168. package/dist/parallax/parallaxMath.js +0 -1
  169. package/dist/particles/Emitter.js +0 -1
  170. package/dist/particles/ParticleBudget.js +0 -1
  171. package/dist/particles/ParticleSim.js +0 -1
  172. package/dist/particles/particleCurves.js +0 -1
  173. package/dist/particles/particleSampling.js +0 -1
  174. package/dist/physics/Body.js +0 -1
  175. package/dist/physics/BroadPhase.js +0 -1
  176. package/dist/physics/Contact.js +0 -1
  177. package/dist/physics/ContactSolver.js +0 -1
  178. package/dist/physics/PhysicsWorld.js +0 -1
  179. package/dist/physics/SpatialHash.js +0 -1
  180. package/dist/physics/collide.js +0 -1
  181. package/dist/physics/collidePolygon.js +0 -1
  182. package/dist/physics/contactReport.js +0 -1
  183. package/dist/physics/drawColliders.js +0 -1
  184. package/dist/physics/physicsSystem.js +0 -1
  185. package/dist/physics/polygon.js +0 -1
  186. package/dist/physics/raycast.js +0 -1
  187. package/dist/physics/syncToBody.js +0 -1
  188. package/dist/physics/tileBodies.js +0 -1
  189. package/dist/physics/types.js +0 -1
  190. package/dist/pixel/Darkness.js +50 -47
  191. package/dist/pixel/DitherVignette.js +0 -1
  192. package/dist/pixel/Evaporation.js +0 -1
  193. package/dist/pixel/NightShade.js +0 -1
  194. package/dist/pixel/PixelLayer.js +0 -1
  195. package/dist/pixel/PixelMarks.js +99 -0
  196. package/dist/pixel/SwayProp.js +0 -1
  197. package/dist/pixel/aimPixels.js +67 -0
  198. package/dist/pixel/lifeArt.js +83 -0
  199. package/dist/pixel/lights.js +0 -1
  200. package/dist/pixel/nineSlice.js +0 -1
  201. package/dist/pixel/paintLayer.js +0 -1
  202. package/dist/pixel/palette.js +0 -1
  203. package/dist/pixel/pixel.js +0 -1
  204. package/dist/pixel/pixelPaint.js +27 -24
  205. package/dist/pixel/pixelParticles.js +35 -0
  206. package/dist/pixel/pixelTone.js +0 -1
  207. package/dist/pixel/propKit.js +0 -1
  208. package/dist/pixel/ramp.js +0 -1
  209. package/dist/pixel/selout.js +0 -1
  210. package/dist/pixel/spritePixels.js +0 -1
  211. package/dist/pixel/swayRig.js +0 -1
  212. package/dist/pixel/vignetteMask.js +0 -1
  213. package/dist/pixel/weatherArt.js +0 -1
  214. package/dist/pixel.js +82 -71
  215. package/dist/platform/appResume.js +18 -0
  216. package/dist/platform/fullscreen.js +52 -0
  217. package/dist/platform/orientation.js +26 -0
  218. package/dist/platform/pointerLock.js +37 -0
  219. package/dist/platform/reducedMotion.js +23 -0
  220. package/dist/platform/timeoutSignal.js +14 -0
  221. package/dist/platform/touch.js +0 -1
  222. package/dist/postfx/BloomFilter.js +0 -1
  223. package/dist/postfx/ChromaticAberrationFilter.js +0 -1
  224. package/dist/postfx/ColorAdjustFilter.js +0 -1
  225. package/dist/postfx/CrtFilter.js +0 -1
  226. package/dist/postfx/PixelateFilter.js +0 -1
  227. package/dist/postfx/PostFx.js +0 -1
  228. package/dist/postfx/ShockwaveFilter.js +0 -1
  229. package/dist/postfx/VignetteFilter.js +0 -1
  230. package/dist/postfx/fxAnimator.js +0 -1
  231. package/dist/postfx/fxShader.js +0 -1
  232. package/dist/postfx.js +0 -1
  233. package/dist/react/ChoiceDialog.js +0 -1
  234. package/dist/react/ConfirmButton.js +24 -0
  235. package/dist/react/ConfirmDialog.js +0 -1
  236. package/dist/react/Glyph.js +0 -1
  237. package/dist/react/Modal.js +0 -1
  238. package/dist/react/Panel.js +0 -1
  239. package/dist/react/PixelImage.js +0 -1
  240. package/dist/react/Select.js +0 -1
  241. package/dist/react/Slider.js +0 -1
  242. package/dist/react/SplitPane.js +0 -1
  243. package/dist/react/TabStrip.js +0 -1
  244. package/dist/react/TitleTips.js +0 -1
  245. package/dist/react/Tooltip.js +0 -1
  246. package/dist/react/UiProvider.js +0 -1
  247. package/dist/react/baseCss.js +1 -1
  248. package/dist/react/choiceStore.js +0 -1
  249. package/dist/react/classNames.js +0 -1
  250. package/dist/react/confirmStore.js +0 -1
  251. package/dist/react/labels.js +7 -7
  252. package/dist/react/placement.js +0 -1
  253. package/dist/react/useFillSheet.js +0 -1
  254. package/dist/react/useFitToViewport.js +0 -1
  255. package/dist/react/useJsonSelector.js +13 -0
  256. package/dist/react/usePicked.js +0 -1
  257. package/dist/react/useStore.js +0 -1
  258. package/dist/react/useViewport.js +0 -1
  259. package/dist/react.js +73 -69
  260. package/dist/rig/PosePlayer.js +0 -1
  261. package/dist/rig/RigView.js +0 -1
  262. package/dist/rig/Skeleton.js +0 -1
  263. package/dist/rig/humanoid.js +0 -1
  264. package/dist/rig/humanoid.json.js +0 -1
  265. package/dist/rig/humanoidPoses.js +0 -1
  266. package/dist/rig/poseMath.js +0 -1
  267. package/dist/rig/poses.json.js +0 -1
  268. package/dist/save/Autosaver.js +0 -1
  269. package/dist/save/MemoryBackend.js +0 -1
  270. package/dist/save/SaveStore.js +0 -1
  271. package/dist/save/WebStorageBackend.js +0 -1
  272. package/dist/save/checksum.js +0 -1
  273. package/dist/save/migrateSave.js +0 -1
  274. package/dist/settings.js +0 -1
  275. package/dist/tilemap/TilemapView.js +57 -33
  276. package/dist/tilemap/autotile.js +103 -0
  277. package/dist/tilemap/collision.js +29 -19
  278. package/dist/tilemap/ldtk.js +21 -0
  279. package/dist/tilemap/ldtkParse.js +191 -0
  280. package/dist/tilemap/tiled.js +0 -1
  281. package/dist/transitions/CrossfadeTransition.js +0 -1
  282. package/dist/transitions/DitherWipeTransition.js +0 -1
  283. package/dist/transitions/FadeTransition.js +0 -1
  284. package/dist/transitions/Letterbox.js +0 -1
  285. package/dist/transitions/ditherMask.js +0 -1
  286. package/dist/transitions/transitionTo.js +0 -1
  287. package/dist/transitions/tweenDone.js +0 -1
  288. package/dist/types/ambient/life/Birds.d.ts +50 -0
  289. package/dist/types/ambient/life/Birds.d.ts.map +1 -0
  290. package/dist/types/ambient/life/Butterflies.d.ts +32 -0
  291. package/dist/types/ambient/life/Butterflies.d.ts.map +1 -0
  292. package/dist/types/ambient/life/ChimneySmoke.d.ts +37 -0
  293. package/dist/types/ambient/life/ChimneySmoke.d.ts.map +1 -0
  294. package/dist/types/ambient/life/Drift.d.ts +58 -0
  295. package/dist/types/ambient/life/Drift.d.ts.map +1 -0
  296. package/dist/types/ambient/life/Fireflies.d.ts +43 -0
  297. package/dist/types/ambient/life/Fireflies.d.ts.map +1 -0
  298. package/dist/types/ambient/life/lifeMotion.d.ts +63 -0
  299. package/dist/types/ambient/life/lifeMotion.d.ts.map +1 -0
  300. package/dist/types/assets/AssetLoader.d.ts +83 -8
  301. package/dist/types/assets/AssetLoader.d.ts.map +1 -1
  302. package/dist/types/assets/Prepainter.d.ts +64 -0
  303. package/dist/types/assets/Prepainter.d.ts.map +1 -0
  304. package/dist/types/assets/textureAtlas.d.ts +35 -0
  305. package/dist/types/assets/textureAtlas.d.ts.map +1 -0
  306. package/dist/types/assets/workerPainter.d.ts +21 -0
  307. package/dist/types/assets/workerPainter.d.ts.map +1 -0
  308. package/dist/types/audio/SoundEngine.d.ts +5 -0
  309. package/dist/types/audio/SoundEngine.d.ts.map +1 -1
  310. package/dist/types/core/Camera.d.ts +1 -1
  311. package/dist/types/core/Camera.d.ts.map +1 -1
  312. package/dist/types/core/Clock.d.ts +14 -2
  313. package/dist/types/core/Clock.d.ts.map +1 -1
  314. package/dist/types/core/Pointer.d.ts +18 -0
  315. package/dist/types/core/Pointer.d.ts.map +1 -1
  316. package/dist/types/core/Stage.d.ts.map +1 -1
  317. package/dist/types/core/TextureRegistry.d.ts +39 -0
  318. package/dist/types/core/TextureRegistry.d.ts.map +1 -1
  319. package/dist/types/core/blendLayer.d.ts +23 -0
  320. package/dist/types/core/blendLayer.d.ts.map +1 -0
  321. package/dist/types/core/gameObject.d.ts.map +1 -1
  322. package/dist/types/core/pixelZoom.d.ts +45 -0
  323. package/dist/types/core/pixelZoom.d.ts.map +1 -0
  324. package/dist/types/core/runDue.d.ts +15 -0
  325. package/dist/types/core/runDue.d.ts.map +1 -0
  326. package/dist/types/core/silhouette.d.ts +16 -0
  327. package/dist/types/core/silhouette.d.ts.map +1 -0
  328. package/dist/types/debug/DrawCallCounter.d.ts.map +1 -1
  329. package/dist/types/electron/electronLike.d.ts +56 -0
  330. package/dist/types/electron/electronLike.d.ts.map +1 -0
  331. package/dist/types/electron/index.d.ts +15 -0
  332. package/dist/types/electron/index.d.ts.map +1 -0
  333. package/dist/types/electron/lockdown.d.ts +34 -0
  334. package/dist/types/electron/lockdown.d.ts.map +1 -0
  335. package/dist/types/electron/lockdownRules.d.ts +29 -0
  336. package/dist/types/electron/lockdownRules.d.ts.map +1 -0
  337. package/dist/types/electron/sessionStore.d.ts +41 -0
  338. package/dist/types/electron/sessionStore.d.ts.map +1 -0
  339. package/dist/types/electron/shellId.d.ts +23 -0
  340. package/dist/types/electron/shellId.d.ts.map +1 -0
  341. package/dist/types/electron/webFiles.d.ts +14 -0
  342. package/dist/types/electron/webFiles.d.ts.map +1 -0
  343. package/dist/types/electron/webPatcher.d.ts +81 -0
  344. package/dist/types/electron/webPatcher.d.ts.map +1 -0
  345. package/dist/types/electron/webPointers.d.ts +20 -0
  346. package/dist/types/electron/webPointers.d.ts.map +1 -0
  347. package/dist/types/engine/Engine.d.ts +16 -0
  348. package/dist/types/engine/Engine.d.ts.map +1 -1
  349. package/dist/types/engine/HiddenPause.d.ts +22 -0
  350. package/dist/types/engine/HiddenPause.d.ts.map +1 -0
  351. package/dist/types/fx/Afterimages.d.ts +62 -0
  352. package/dist/types/fx/Afterimages.d.ts.map +1 -0
  353. package/dist/types/index.d.ts +32 -4
  354. package/dist/types/index.d.ts.map +1 -1
  355. package/dist/types/input/Actions.d.ts +7 -1
  356. package/dist/types/input/Actions.d.ts.map +1 -1
  357. package/dist/types/input/haptics.d.ts +42 -0
  358. package/dist/types/input/haptics.d.ts.map +1 -0
  359. package/dist/types/input/rumble.d.ts +41 -0
  360. package/dist/types/input/rumble.d.ts.map +1 -0
  361. package/dist/types/net/ApiClient.d.ts +74 -0
  362. package/dist/types/net/ApiClient.d.ts.map +1 -0
  363. package/dist/types/net/Connection.d.ts +22 -0
  364. package/dist/types/net/Connection.d.ts.map +1 -1
  365. package/dist/types/net/constants.d.ts +13 -4
  366. package/dist/types/net/constants.d.ts.map +1 -1
  367. package/dist/types/net/movement.d.ts.map +1 -1
  368. package/dist/types/net/reconnect.d.ts +14 -0
  369. package/dist/types/net/reconnect.d.ts.map +1 -0
  370. package/dist/types/pixel/Darkness.d.ts +17 -5
  371. package/dist/types/pixel/Darkness.d.ts.map +1 -1
  372. package/dist/types/pixel/PixelMarks.d.ts +68 -0
  373. package/dist/types/pixel/PixelMarks.d.ts.map +1 -0
  374. package/dist/types/pixel/aimPixels.d.ts +23 -0
  375. package/dist/types/pixel/aimPixels.d.ts.map +1 -0
  376. package/dist/types/pixel/index.d.ts +4 -1
  377. package/dist/types/pixel/index.d.ts.map +1 -1
  378. package/dist/types/pixel/lifeArt.d.ts +63 -0
  379. package/dist/types/pixel/lifeArt.d.ts.map +1 -0
  380. package/dist/types/pixel/pixelPaint.d.ts +1 -0
  381. package/dist/types/pixel/pixelPaint.d.ts.map +1 -1
  382. package/dist/types/pixel/pixelParticles.d.ts +21 -0
  383. package/dist/types/pixel/pixelParticles.d.ts.map +1 -0
  384. package/dist/types/platform/appResume.d.ts +25 -0
  385. package/dist/types/platform/appResume.d.ts.map +1 -0
  386. package/dist/types/platform/fullscreen.d.ts +37 -0
  387. package/dist/types/platform/fullscreen.d.ts.map +1 -0
  388. package/dist/types/platform/orientation.d.ts +19 -0
  389. package/dist/types/platform/orientation.d.ts.map +1 -0
  390. package/dist/types/platform/pointerLock.d.ts +26 -0
  391. package/dist/types/platform/pointerLock.d.ts.map +1 -0
  392. package/dist/types/platform/reducedMotion.d.ts +20 -0
  393. package/dist/types/platform/reducedMotion.d.ts.map +1 -0
  394. package/dist/types/platform/timeoutSignal.d.ts +6 -0
  395. package/dist/types/platform/timeoutSignal.d.ts.map +1 -0
  396. package/dist/types/react/ConfirmButton.d.ts +26 -0
  397. package/dist/types/react/ConfirmButton.d.ts.map +1 -0
  398. package/dist/types/react/baseCss.d.ts +1 -1
  399. package/dist/types/react/baseCss.d.ts.map +1 -1
  400. package/dist/types/react/index.d.ts +2 -0
  401. package/dist/types/react/index.d.ts.map +1 -1
  402. package/dist/types/react/labels.d.ts +2 -0
  403. package/dist/types/react/labels.d.ts.map +1 -1
  404. package/dist/types/react/useJsonSelector.d.ts +13 -0
  405. package/dist/types/react/useJsonSelector.d.ts.map +1 -0
  406. package/dist/types/tilemap/TilemapView.d.ts +17 -0
  407. package/dist/types/tilemap/TilemapView.d.ts.map +1 -1
  408. package/dist/types/tilemap/autotile.d.ts +95 -0
  409. package/dist/types/tilemap/autotile.d.ts.map +1 -0
  410. package/dist/types/tilemap/collision.d.ts +8 -0
  411. package/dist/types/tilemap/collision.d.ts.map +1 -1
  412. package/dist/types/tilemap/ldtk.d.ts +97 -0
  413. package/dist/types/tilemap/ldtk.d.ts.map +1 -0
  414. package/dist/types/tilemap/ldtkParse.d.ts +26 -0
  415. package/dist/types/tilemap/ldtkParse.d.ts.map +1 -0
  416. package/dist/types/ui/index.d.ts +4 -0
  417. package/dist/types/ui/index.d.ts.map +1 -1
  418. package/dist/types/ui/rich/RichText.d.ts +48 -0
  419. package/dist/types/ui/rich/RichText.d.ts.map +1 -0
  420. package/dist/types/ui/rich/richFonts.d.ts +70 -0
  421. package/dist/types/ui/rich/richFonts.d.ts.map +1 -0
  422. package/dist/types/ui/rich/richLayout.d.ts +76 -0
  423. package/dist/types/ui/rich/richLayout.d.ts.map +1 -0
  424. package/dist/types/ui/rich/richMarkup.d.ts +48 -0
  425. package/dist/types/ui/rich/richMarkup.d.ts.map +1 -0
  426. package/dist/types/updates/index.d.ts +9 -0
  427. package/dist/types/updates/index.d.ts.map +1 -0
  428. package/dist/types/updates/manifest.d.ts +74 -0
  429. package/dist/types/updates/manifest.d.ts.map +1 -0
  430. package/dist/types/updates/pickUpdate.d.ts +43 -0
  431. package/dist/types/updates/pickUpdate.d.ts.map +1 -0
  432. package/dist/types/updates/webPlan.d.ts +21 -0
  433. package/dist/types/updates/webPlan.d.ts.map +1 -0
  434. package/dist/ui/Bar.js +0 -1
  435. package/dist/ui/Button.js +0 -1
  436. package/dist/ui/Dialog.js +0 -1
  437. package/dist/ui/FocusManager.js +0 -1
  438. package/dist/ui/Label.js +0 -1
  439. package/dist/ui/List.js +0 -1
  440. package/dist/ui/Panel.js +0 -1
  441. package/dist/ui/ScrollView.js +0 -1
  442. package/dist/ui/Slider.js +0 -1
  443. package/dist/ui/Stack.js +0 -1
  444. package/dist/ui/Toggle.js +0 -1
  445. package/dist/ui/UiHost.js +0 -1
  446. package/dist/ui/UiPointerGate.js +0 -1
  447. package/dist/ui/UiRoot.js +0 -1
  448. package/dist/ui/Widget.js +0 -1
  449. package/dist/ui/dom/DomOverlay.js +0 -1
  450. package/dist/ui/dom/DomWorldAnchors.js +0 -1
  451. package/dist/ui/dom/InputShield.js +0 -1
  452. package/dist/ui/dom/createStore.js +0 -1
  453. package/dist/ui/dom/shieldDecision.js +0 -1
  454. package/dist/ui/dom/worldToScreen.js +0 -1
  455. package/dist/ui/layout/anchor.js +0 -1
  456. package/dist/ui/layout/barDrain.js +0 -1
  457. package/dist/ui/layout/fitToViewport.js +0 -1
  458. package/dist/ui/layout/focusNav.js +0 -1
  459. package/dist/ui/layout/geometry.js +0 -1
  460. package/dist/ui/layout/listWidth.js +0 -1
  461. package/dist/ui/layout/placeTooltip.js +0 -1
  462. package/dist/ui/layout/scroll.js +0 -1
  463. package/dist/ui/layout/slider.js +0 -1
  464. package/dist/ui/layout/stack.js +0 -1
  465. package/dist/ui/layout/wrapRow.js +0 -1
  466. package/dist/ui/rich/RichText.js +87 -0
  467. package/dist/ui/rich/richFonts.js +105 -0
  468. package/dist/ui/rich/richLayout.js +132 -0
  469. package/dist/ui/rich/richMarkup.js +67 -0
  470. package/dist/ui.js +75 -65
  471. package/dist/updates/manifest.js +35 -0
  472. package/dist/updates/pickUpdate.js +27 -0
  473. package/dist/updates/webPlan.js +14 -0
  474. package/dist/updates.js +15 -0
  475. package/package.json +31 -6
  476. package/dist/1681-engine.js.map +0 -1
  477. package/dist/ambient/ColorGrade.js.map +0 -1
  478. package/dist/ambient/DayClock.js.map +0 -1
  479. package/dist/ambient/WeatherLayer.js.map +0 -1
  480. package/dist/ambient/grade.js.map +0 -1
  481. package/dist/ambient/weather.js.map +0 -1
  482. package/dist/anim/AnimStateMachine.js.map +0 -1
  483. package/dist/anim/Animator.js.map +0 -1
  484. package/dist/anim/animStateCompile.js.map +0 -1
  485. package/dist/anim/animStateSystem.js.map +0 -1
  486. package/dist/anim/animationSystem.js.map +0 -1
  487. package/dist/anim/asepriteSlices.js.map +0 -1
  488. package/dist/anim/asepriteTags.js.map +0 -1
  489. package/dist/anim/parseAseprite.js.map +0 -1
  490. package/dist/assets/AssetLoader.js.map +0 -1
  491. package/dist/assets/loadAseprite.js.map +0 -1
  492. package/dist/assets/takeCleanerWorker-DTcI_Jqr.js.map +0 -1
  493. package/dist/audio/Footsteps.js.map +0 -1
  494. package/dist/audio/MusicPlayer.js.map +0 -1
  495. package/dist/audio/SoundEngine.js.map +0 -1
  496. package/dist/audio/cleanTake.js.map +0 -1
  497. package/dist/audio/music/ElementVoice.js.map +0 -1
  498. package/dist/audio/music/GraphVoice.js.map +0 -1
  499. package/dist/audio/music/MusicChannel.js.map +0 -1
  500. package/dist/audio/music/musicVoice.js.map +0 -1
  501. package/dist/audio/sampleBank.js.map +0 -1
  502. package/dist/audio/soundTable.js.map +0 -1
  503. package/dist/audio/synth.js.map +0 -1
  504. package/dist/core/BitmapLabel.js.map +0 -1
  505. package/dist/core/Camera.js.map +0 -1
  506. package/dist/core/Clock.js.map +0 -1
  507. package/dist/core/Graphics.js.map +0 -1
  508. package/dist/core/Group.js.map +0 -1
  509. package/dist/core/Image.js.map +0 -1
  510. package/dist/core/ObjectFactory.js.map +0 -1
  511. package/dist/core/Pointer.js.map +0 -1
  512. package/dist/core/Rectangle.js.map +0 -1
  513. package/dist/core/Stage.js.map +0 -1
  514. package/dist/core/Text.js.map +0 -1
  515. package/dist/core/TextureRegistry.js.map +0 -1
  516. package/dist/core/Timeline.js.map +0 -1
  517. package/dist/core/TimelineBuilder.js.map +0 -1
  518. package/dist/core/TimelineChild.js.map +0 -1
  519. package/dist/core/Tweener.js.map +0 -1
  520. package/dist/core/atlas.js.map +0 -1
  521. package/dist/core/atlasPlan.js.map +0 -1
  522. package/dist/core/eases.js.map +0 -1
  523. package/dist/core/gameObject.js.map +0 -1
  524. package/dist/core/pixiPatches.js.map +0 -1
  525. package/dist/core/timelinePlace.js.map +0 -1
  526. package/dist/core/tweenProps.js.map +0 -1
  527. package/dist/debug/DebugOverlay.js.map +0 -1
  528. package/dist/debug/DrawCallCounter.js.map +0 -1
  529. package/dist/debug/ErrorLog.js.map +0 -1
  530. package/dist/debug/FrameStats.js.map +0 -1
  531. package/dist/debug/inspectWorld.js.map +0 -1
  532. package/dist/diagnostics/ErrorDeduper.js.map +0 -1
  533. package/dist/diagnostics/FrameSampler.js.map +0 -1
  534. package/dist/diagnostics/bootWatch.js.map +0 -1
  535. package/dist/diagnostics/captureErrors.js.map +0 -1
  536. package/dist/diagnostics/contextLoss.js.map +0 -1
  537. package/dist/diagnostics/frameStats.js.map +0 -1
  538. package/dist/diagnostics/gpuInfo.js.map +0 -1
  539. package/dist/diagnostics/renderGuard.js.map +0 -1
  540. package/dist/ecs/SparseSet.js.map +0 -1
  541. package/dist/ecs/World.js.map +0 -1
  542. package/dist/ecs/fields.js.map +0 -1
  543. package/dist/engine/Engine.js.map +0 -1
  544. package/dist/engine/FixedLoop.js.map +0 -1
  545. package/dist/engine/Scene.js.map +0 -1
  546. package/dist/engine/SceneManager.js.map +0 -1
  547. package/dist/events/EventBus.js.map +0 -1
  548. package/dist/fx/FloatingText.js.map +0 -1
  549. package/dist/fx/hitStop.js.map +0 -1
  550. package/dist/fx/viewCull.js.map +0 -1
  551. package/dist/i18n/Translator.js.map +0 -1
  552. package/dist/i18n/extractStrings.js.map +0 -1
  553. package/dist/i18n/formatMessage.js.map +0 -1
  554. package/dist/input/Actions.js.map +0 -1
  555. package/dist/input/AimGesture.js.map +0 -1
  556. package/dist/input/Keyboard.js.map +0 -1
  557. package/dist/input/Keymap.js.map +0 -1
  558. package/dist/input/TargetLock.js.map +0 -1
  559. package/dist/input/aimAxis.js.map +0 -1
  560. package/dist/input/keyChord.js.map +0 -1
  561. package/dist/input/longPress.js.map +0 -1
  562. package/dist/input/stickAxis.js.map +0 -1
  563. package/dist/input/touchDetect.js.map +0 -1
  564. package/dist/light/LightLayer.js.map +0 -1
  565. package/dist/nav/MinHeap.js.map +0 -1
  566. package/dist/nav/findPath.js.map +0 -1
  567. package/dist/nav/navGrid.js.map +0 -1
  568. package/dist/nav/nearestOpen.js.map +0 -1
  569. package/dist/nav/straightenPath.js.map +0 -1
  570. package/dist/net/Connection.js.map +0 -1
  571. package/dist/net/InterpolationBuffer.js.map +0 -1
  572. package/dist/net/PredictedMover.js.map +0 -1
  573. package/dist/net/Room.js.map +0 -1
  574. package/dist/net/RoomHost.js.map +0 -1
  575. package/dist/net/cadence.js.map +0 -1
  576. package/dist/net/constants.js.map +0 -1
  577. package/dist/net/lobby/LobbyClient.js.map +0 -1
  578. package/dist/net/lobby/LobbyHost.js.map +0 -1
  579. package/dist/net/lobby/LobbyRooms.js.map +0 -1
  580. package/dist/net/lobby/Matchmaker.js.map +0 -1
  581. package/dist/net/movement.js.map +0 -1
  582. package/dist/net/protocol.js.map +0 -1
  583. package/dist/net/serverClock.js.map +0 -1
  584. package/dist/net/snapshotCodec.js.map +0 -1
  585. package/dist/parallax/Parallax.js.map +0 -1
  586. package/dist/parallax/ParallaxLayer.js.map +0 -1
  587. package/dist/parallax/parallaxMath.js.map +0 -1
  588. package/dist/particles/Emitter.js.map +0 -1
  589. package/dist/particles/ParticleBudget.js.map +0 -1
  590. package/dist/particles/ParticleSim.js.map +0 -1
  591. package/dist/particles/particleCurves.js.map +0 -1
  592. package/dist/particles/particleSampling.js.map +0 -1
  593. package/dist/physics/Body.js.map +0 -1
  594. package/dist/physics/BroadPhase.js.map +0 -1
  595. package/dist/physics/Contact.js.map +0 -1
  596. package/dist/physics/ContactSolver.js.map +0 -1
  597. package/dist/physics/PhysicsWorld.js.map +0 -1
  598. package/dist/physics/SpatialHash.js.map +0 -1
  599. package/dist/physics/collide.js.map +0 -1
  600. package/dist/physics/collidePolygon.js.map +0 -1
  601. package/dist/physics/contactReport.js.map +0 -1
  602. package/dist/physics/drawColliders.js.map +0 -1
  603. package/dist/physics/physicsSystem.js.map +0 -1
  604. package/dist/physics/polygon.js.map +0 -1
  605. package/dist/physics/raycast.js.map +0 -1
  606. package/dist/physics/syncToBody.js.map +0 -1
  607. package/dist/physics/tileBodies.js.map +0 -1
  608. package/dist/physics/types.js.map +0 -1
  609. package/dist/pixel/Darkness.js.map +0 -1
  610. package/dist/pixel/DitherVignette.js.map +0 -1
  611. package/dist/pixel/Evaporation.js.map +0 -1
  612. package/dist/pixel/NightShade.js.map +0 -1
  613. package/dist/pixel/PixelLayer.js.map +0 -1
  614. package/dist/pixel/SwayProp.js.map +0 -1
  615. package/dist/pixel/lights.js.map +0 -1
  616. package/dist/pixel/nineSlice.js.map +0 -1
  617. package/dist/pixel/paintLayer.js.map +0 -1
  618. package/dist/pixel/palette.js.map +0 -1
  619. package/dist/pixel/pixel.js.map +0 -1
  620. package/dist/pixel/pixelPaint.js.map +0 -1
  621. package/dist/pixel/pixelTone.js.map +0 -1
  622. package/dist/pixel/propKit.js.map +0 -1
  623. package/dist/pixel/ramp.js.map +0 -1
  624. package/dist/pixel/selout.js.map +0 -1
  625. package/dist/pixel/spritePixels.js.map +0 -1
  626. package/dist/pixel/swayRig.js.map +0 -1
  627. package/dist/pixel/vignetteMask.js.map +0 -1
  628. package/dist/pixel/weatherArt.js.map +0 -1
  629. package/dist/pixel.js.map +0 -1
  630. package/dist/platform/touch.js.map +0 -1
  631. package/dist/postfx/BloomFilter.js.map +0 -1
  632. package/dist/postfx/ChromaticAberrationFilter.js.map +0 -1
  633. package/dist/postfx/ColorAdjustFilter.js.map +0 -1
  634. package/dist/postfx/CrtFilter.js.map +0 -1
  635. package/dist/postfx/PixelateFilter.js.map +0 -1
  636. package/dist/postfx/PostFx.js.map +0 -1
  637. package/dist/postfx/ShockwaveFilter.js.map +0 -1
  638. package/dist/postfx/VignetteFilter.js.map +0 -1
  639. package/dist/postfx/fxAnimator.js.map +0 -1
  640. package/dist/postfx/fxShader.js.map +0 -1
  641. package/dist/postfx.js.map +0 -1
  642. package/dist/react/ChoiceDialog.js.map +0 -1
  643. package/dist/react/ConfirmDialog.js.map +0 -1
  644. package/dist/react/Glyph.js.map +0 -1
  645. package/dist/react/Modal.js.map +0 -1
  646. package/dist/react/Panel.js.map +0 -1
  647. package/dist/react/PixelImage.js.map +0 -1
  648. package/dist/react/Select.js.map +0 -1
  649. package/dist/react/Slider.js.map +0 -1
  650. package/dist/react/SplitPane.js.map +0 -1
  651. package/dist/react/TabStrip.js.map +0 -1
  652. package/dist/react/TitleTips.js.map +0 -1
  653. package/dist/react/Tooltip.js.map +0 -1
  654. package/dist/react/UiProvider.js.map +0 -1
  655. package/dist/react/baseCss.js.map +0 -1
  656. package/dist/react/choiceStore.js.map +0 -1
  657. package/dist/react/classNames.js.map +0 -1
  658. package/dist/react/confirmStore.js.map +0 -1
  659. package/dist/react/labels.js.map +0 -1
  660. package/dist/react/placement.js.map +0 -1
  661. package/dist/react/useFillSheet.js.map +0 -1
  662. package/dist/react/useFitToViewport.js.map +0 -1
  663. package/dist/react/usePicked.js.map +0 -1
  664. package/dist/react/useStore.js.map +0 -1
  665. package/dist/react/useViewport.js.map +0 -1
  666. package/dist/react.js.map +0 -1
  667. package/dist/rig/PosePlayer.js.map +0 -1
  668. package/dist/rig/RigView.js.map +0 -1
  669. package/dist/rig/Skeleton.js.map +0 -1
  670. package/dist/rig/humanoid.js.map +0 -1
  671. package/dist/rig/humanoid.json.js.map +0 -1
  672. package/dist/rig/humanoidPoses.js.map +0 -1
  673. package/dist/rig/poseMath.js.map +0 -1
  674. package/dist/rig/poses.json.js.map +0 -1
  675. package/dist/save/Autosaver.js.map +0 -1
  676. package/dist/save/MemoryBackend.js.map +0 -1
  677. package/dist/save/SaveStore.js.map +0 -1
  678. package/dist/save/WebStorageBackend.js.map +0 -1
  679. package/dist/save/checksum.js.map +0 -1
  680. package/dist/save/migrateSave.js.map +0 -1
  681. package/dist/settings.js.map +0 -1
  682. package/dist/tilemap/TilemapView.js.map +0 -1
  683. package/dist/tilemap/collision.js.map +0 -1
  684. package/dist/tilemap/tiled.js.map +0 -1
  685. package/dist/transitions/CrossfadeTransition.js.map +0 -1
  686. package/dist/transitions/DitherWipeTransition.js.map +0 -1
  687. package/dist/transitions/FadeTransition.js.map +0 -1
  688. package/dist/transitions/Letterbox.js.map +0 -1
  689. package/dist/transitions/ditherMask.js.map +0 -1
  690. package/dist/transitions/transitionTo.js.map +0 -1
  691. package/dist/transitions/tweenDone.js.map +0 -1
  692. package/dist/ui/Bar.js.map +0 -1
  693. package/dist/ui/Button.js.map +0 -1
  694. package/dist/ui/Dialog.js.map +0 -1
  695. package/dist/ui/FocusManager.js.map +0 -1
  696. package/dist/ui/Label.js.map +0 -1
  697. package/dist/ui/List.js.map +0 -1
  698. package/dist/ui/Panel.js.map +0 -1
  699. package/dist/ui/ScrollView.js.map +0 -1
  700. package/dist/ui/Slider.js.map +0 -1
  701. package/dist/ui/Stack.js.map +0 -1
  702. package/dist/ui/Toggle.js.map +0 -1
  703. package/dist/ui/UiHost.js.map +0 -1
  704. package/dist/ui/UiPointerGate.js.map +0 -1
  705. package/dist/ui/UiRoot.js.map +0 -1
  706. package/dist/ui/Widget.js.map +0 -1
  707. package/dist/ui/dom/DomOverlay.js.map +0 -1
  708. package/dist/ui/dom/DomWorldAnchors.js.map +0 -1
  709. package/dist/ui/dom/InputShield.js.map +0 -1
  710. package/dist/ui/dom/createStore.js.map +0 -1
  711. package/dist/ui/dom/shieldDecision.js.map +0 -1
  712. package/dist/ui/dom/worldToScreen.js.map +0 -1
  713. package/dist/ui/layout/anchor.js.map +0 -1
  714. package/dist/ui/layout/barDrain.js.map +0 -1
  715. package/dist/ui/layout/fitToViewport.js.map +0 -1
  716. package/dist/ui/layout/focusNav.js.map +0 -1
  717. package/dist/ui/layout/geometry.js.map +0 -1
  718. package/dist/ui/layout/listWidth.js.map +0 -1
  719. package/dist/ui/layout/placeTooltip.js.map +0 -1
  720. package/dist/ui/layout/scroll.js.map +0 -1
  721. package/dist/ui/layout/slider.js.map +0 -1
  722. package/dist/ui/layout/stack.js.map +0 -1
  723. package/dist/ui/layout/wrapRow.js.map +0 -1
  724. package/dist/ui.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"Image.js","sources":["../../src/core/Image.ts"],"sourcesContent":["import { Sprite } from 'pixi.js';\nimport { GameObject } from './gameObject';\nimport type { TextureRegistry } from './TextureRegistry';\n\n/** A textured sprite, centred by default; frames are looked up by name in the texture registry. */\nexport class Image extends GameObject(Sprite) {\n textureKey: string;\n frameName: string;\n\n constructor(\n private readonly registry: TextureRegistry,\n x: number,\n y: number,\n key: string,\n frame?: string | number,\n ) {\n super(registry.get(key, frame));\n this.textureKey = key;\n this.frameName = frame === undefined ? '' : String(frame);\n this.anchor.set(0.5);\n this.position.set(x, y);\n }\n\n get originX(): number {\n return this.anchor.x;\n }\n\n get originY(): number {\n return this.anchor.y;\n }\n\n /** Width of the frame itself, unscaled. */\n get frameWidth(): number {\n return this.texture.orig.width;\n }\n\n get frameHeight(): number {\n return this.texture.orig.height;\n }\n\n get displayWidth(): number {\n return Math.abs(this.frameWidth * this.scaleX);\n }\n\n set displayWidth(w: number) {\n this.scaleX = w / this.frameWidth;\n }\n\n get displayHeight(): number {\n return Math.abs(this.frameHeight * this.scaleY);\n }\n\n set displayHeight(h: number) {\n this.scaleY = h / this.frameHeight;\n }\n\n setOrigin(x = 0.5, y = x): this {\n this.anchor.set(x, y);\n return this;\n }\n\n setDisplaySize(w: number, h: number): this {\n return this.setScale(w / this.frameWidth, h / this.frameHeight);\n }\n\n setFrame(frame: string | number): this {\n this.frameName = String(frame);\n this.texture = this.registry.get(this.textureKey, frame);\n return this;\n }\n\n setTexture(key: string, frame?: string | number): this {\n this.textureKey = key;\n this.frameName = frame === undefined ? '' : String(frame);\n this.texture = this.registry.get(key, frame);\n return this;\n }\n}\n"],"names":["Image","GameObject","Sprite","registry","x","y","key","frame","w","h"],"mappings":";;AAKO,MAAMA,UAAcC,EAAWC,CAAM,EAAE;AAAA,EAI5C,YACmBC,GACjBC,GACAC,GACAC,GACAC,GACA;AACA,UAAMJ,EAAS,IAAIG,GAAKC,CAAK,CAAC,GANb,KAAA,WAAAJ,GAOjB,KAAK,aAAaG,GAClB,KAAK,YAAYC,MAAU,SAAY,KAAK,OAAOA,CAAK,GACxD,KAAK,OAAO,IAAI,GAAG,GACnB,KAAK,SAAS,IAAIH,GAAGC,CAAC;AAAA,EACxB;AAAA,EAXmB;AAAA,EAJnB;AAAA,EACA;AAAA,EAgBA,IAAI,UAAkB;AACpB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA,EAGA,IAAI,aAAqB;AACvB,WAAO,KAAK,QAAQ,KAAK;AAAA,EAC3B;AAAA,EAEA,IAAI,cAAsB;AACxB,WAAO,KAAK,QAAQ,KAAK;AAAA,EAC3B;AAAA,EAEA,IAAI,eAAuB;AACzB,WAAO,KAAK,IAAI,KAAK,aAAa,KAAK,MAAM;AAAA,EAC/C;AAAA,EAEA,IAAI,aAAaG,GAAW;AAC1B,SAAK,SAASA,IAAI,KAAK;AAAA,EACzB;AAAA,EAEA,IAAI,gBAAwB;AAC1B,WAAO,KAAK,IAAI,KAAK,cAAc,KAAK,MAAM;AAAA,EAChD;AAAA,EAEA,IAAI,cAAcC,GAAW;AAC3B,SAAK,SAASA,IAAI,KAAK;AAAA,EACzB;AAAA,EAEA,UAAUL,IAAI,KAAKC,IAAID,GAAS;AAC9B,gBAAK,OAAO,IAAIA,GAAGC,CAAC,GACb;AAAA,EACT;AAAA,EAEA,eAAeG,GAAWC,GAAiB;AACzC,WAAO,KAAK,SAASD,IAAI,KAAK,YAAYC,IAAI,KAAK,WAAW;AAAA,EAChE;AAAA,EAEA,SAASF,GAA8B;AACrC,gBAAK,YAAY,OAAOA,CAAK,GAC7B,KAAK,UAAU,KAAK,SAAS,IAAI,KAAK,YAAYA,CAAK,GAChD;AAAA,EACT;AAAA,EAEA,WAAWD,GAAaC,GAA+B;AACrD,gBAAK,aAAaD,GAClB,KAAK,YAAYC,MAAU,SAAY,KAAK,OAAOA,CAAK,GACxD,KAAK,UAAU,KAAK,SAAS,IAAID,GAAKC,CAAK,GACpC;AAAA,EACT;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectFactory.js","sources":["../../src/core/ObjectFactory.ts"],"sourcesContent":["import type { Container } from 'pixi.js';\nimport { BitmapLabel } from './BitmapLabel';\nimport { Graphics } from './Graphics';\nimport { Group } from './Group';\nimport { Image } from './Image';\nimport { Rectangle } from './Rectangle';\nimport type { Stage } from './Stage';\nimport { Text, type TextStyle } from './Text';\n\n/** `scene.add.*`: creates an object and puts it on the world layer, where depth sorts it. */\nexport class ObjectFactory {\n constructor(private readonly stage: Stage) {}\n\n image(x: number, y: number, key: string, frame?: string | number): Image {\n return this.attach(new Image(this.stage.textures, x, y, key, frame));\n }\n\n /**\n * A copy of `src` as it is drawn right now (afterimages, clones). A live texture, repainted every frame, is\n * snapshotted first, and the snapshot is freed with the copy.\n */\n still(x: number, y: number, src: Image): Image {\n const textures = this.stage.textures;\n if (!textures.isLive(src.textureKey)) return this.image(x, y, src.textureKey, src.frameName);\n const key = textures.snapshot(src.textureKey, src.frameName);\n const img = this.image(x, y, key, '0');\n img.once('destroyed', () => textures.remove(key));\n return img;\n }\n\n graphics(opts?: { x?: number; y?: number }): Graphics {\n const g = this.attach(new Graphics());\n g.position.set(opts?.x ?? 0, opts?.y ?? 0);\n return g;\n }\n\n container(x = 0, y = 0, children: Container[] = []): Group {\n const c = this.attach(new Group());\n c.position.set(x, y);\n c.add(children);\n return c;\n }\n\n rectangle(x: number, y: number, w: number, h: number, color: number, alpha = 1): Rectangle {\n const r = this.attach(new Rectangle(w, h, color, alpha));\n r.position.set(x, y);\n return r;\n }\n\n text(x: number, y: number, text: string, style: TextStyle): Text {\n return this.attach(new Text(x, y, text, style));\n }\n\n /** A bitmap-font label (`BitmapLabel`): cheap to change, for text rewritten many times a second. */\n label(x: number, y: number, text: string, fontFamily: string, size: number, color: string, stroke: string, strokeWidth: number, resolution: number, glyphs = ''): BitmapLabel {\n return this.attach(new BitmapLabel(x, y, text, fontFamily, size, color, stroke, strokeWidth, resolution, glyphs));\n }\n\n /** Puts an object made elsewhere on the world layer, as the factory's own are (a `PixelLayer`, a custom class). */\n existing<T extends Container & { scene: Stage }>(obj: T): T {\n return this.attach(obj);\n }\n\n private attach<T extends Container & { scene: Stage }>(obj: T): T {\n obj.scene = this.stage;\n this.stage.world.addChild(obj);\n return obj;\n }\n}\n"],"names":["ObjectFactory","stage","x","y","key","frame","Image","src","textures","img","opts","g","Graphics","children","c","Group","w","h","color","alpha","r","Rectangle","text","style","Text","fontFamily","size","stroke","strokeWidth","resolution","glyphs","BitmapLabel","obj"],"mappings":";;;;;;AAUO,MAAMA,EAAc;AAAA,EACzB,YAA6BC,GAAc;AAAd,SAAA,QAAAA;AAAA,EAAe;AAAA,EAAf;AAAA,EAE7B,MAAMC,GAAWC,GAAWC,GAAaC,GAAgC;AACvE,WAAO,KAAK,OAAO,IAAIC,EAAM,KAAK,MAAM,UAAUJ,GAAGC,GAAGC,GAAKC,CAAK,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAMH,GAAWC,GAAWI,GAAmB;AAC7C,UAAMC,IAAW,KAAK,MAAM;AAC5B,QAAI,CAACA,EAAS,OAAOD,EAAI,UAAU,EAAG,QAAO,KAAK,MAAML,GAAGC,GAAGI,EAAI,YAAYA,EAAI,SAAS;AAC3F,UAAMH,IAAMI,EAAS,SAASD,EAAI,YAAYA,EAAI,SAAS,GACrDE,IAAM,KAAK,MAAMP,GAAGC,GAAGC,GAAK,GAAG;AACrC,WAAAK,EAAI,KAAK,aAAa,MAAMD,EAAS,OAAOJ,CAAG,CAAC,GACzCK;AAAA,EACT;AAAA,EAEA,SAASC,GAA6C;AACpD,UAAMC,IAAI,KAAK,OAAO,IAAIC,GAAU;AACpC,WAAAD,EAAE,SAAS,IAAID,GAAM,KAAK,GAAGA,GAAM,KAAK,CAAC,GAClCC;AAAA,EACT;AAAA,EAEA,UAAUT,IAAI,GAAGC,IAAI,GAAGU,IAAwB,IAAW;AACzD,UAAMC,IAAI,KAAK,OAAO,IAAIC,GAAO;AACjC,WAAAD,EAAE,SAAS,IAAIZ,GAAGC,CAAC,GACnBW,EAAE,IAAID,CAAQ,GACPC;AAAA,EACT;AAAA,EAEA,UAAUZ,GAAWC,GAAWa,GAAWC,GAAWC,GAAeC,IAAQ,GAAc;AACzF,UAAMC,IAAI,KAAK,OAAO,IAAIC,EAAUL,GAAGC,GAAGC,GAAOC,CAAK,CAAC;AACvD,WAAAC,EAAE,SAAS,IAAIlB,GAAGC,CAAC,GACZiB;AAAA,EACT;AAAA,EAEA,KAAKlB,GAAWC,GAAWmB,GAAcC,GAAwB;AAC/D,WAAO,KAAK,OAAO,IAAIC,EAAKtB,GAAGC,GAAGmB,GAAMC,CAAK,CAAC;AAAA,EAChD;AAAA;AAAA,EAGA,MAAMrB,GAAWC,GAAWmB,GAAcG,GAAoBC,GAAcR,GAAeS,GAAgBC,GAAqBC,GAAoBC,IAAS,IAAiB;AAC5K,WAAO,KAAK,OAAO,IAAIC,EAAY7B,GAAGC,GAAGmB,GAAMG,GAAYC,GAAMR,GAAOS,GAAQC,GAAaC,GAAYC,CAAM,CAAC;AAAA,EAClH;AAAA;AAAA,EAGA,SAAiDE,GAAW;AAC1D,WAAO,KAAK,OAAOA,CAAG;AAAA,EACxB;AAAA,EAEQ,OAA+CA,GAAW;AAChE,WAAAA,EAAI,QAAQ,KAAK,OACjB,KAAK,MAAM,MAAM,SAASA,CAAG,GACtBA;AAAA,EACT;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Pointer.js","sources":["../../src/core/Pointer.ts"],"sourcesContent":["import type { Camera } from './Camera';\nimport { isTouchUi } from '../platform/touch';\n\n/** MouseEvent.button (0 left, 1 middle, 2 right) as a MouseEvent.buttons bit. */\nfunction buttonBit(button: number): number {\n return button === 1 ? 4 : button === 2 ? 2 : button === 0 ? 1 : 0;\n}\n\n/**\n * The mouse over the game canvas. Presses count only when they start on the canvas (not on the React UI above it);\n * moves are followed everywhere so aiming keeps working at the edge of a panel.\n */\nexport class Pointer {\n /**\n * A press this returns true for is not counted as a held button (e.g. Shift + right-click opening a context menu\n * instead of firing what the right button is bound to). Unset, every press counts.\n */\n static ignorePress: ((e: PointerEvent) => boolean) | null = null;\n\n /** Screen position relative to the canvas, in CSS pixels. */\n x = 0;\n y = 0;\n worldX = 0;\n worldY = 0;\n private buttons = 0;\n private readonly cleanup: (() => void)[] = [];\n\n constructor(private readonly canvas: HTMLCanvasElement) {\n // with the on-screen controls a finger on the world is not a mouse: it would swing at every tap and drag the\n // aim after the joystick thumb (touch controls aim on their own). A real mouse still works.\n const fingers = isTouchUi();\n const on = <K extends keyof WindowEventMap>(target: Window | HTMLElement, type: K, fn: (e: WindowEventMap[K]) => void) => {\n const handler = (e: WindowEventMap[K]) => {\n if (fingers && e instanceof PointerEvent && e.pointerType === 'touch') return;\n fn(e);\n };\n target.addEventListener(type, handler as EventListener);\n this.cleanup.push(() => target.removeEventListener(type, handler as EventListener));\n };\n // Pointer events, since Pixi's event system cancels the mouse events that follow them. Read `button` (the one\n // that changed), not `buttons`: synthetic input (CDP) leaves `buttons` at 0.\n on(canvas, 'pointerdown', (e) => {\n this.track(e);\n if (Pointer.ignorePress?.(e)) return;\n this.buttons |= buttonBit(e.button);\n });\n on(window, 'pointermove', (e) => {\n this.track(e);\n // a second button pressed or released while one is held arrives as a move\n if (e.button >= 0) this.buttons = (e.buttons & buttonBit(e.button)) !== 0 ? this.buttons | buttonBit(e.button) : this.buttons & ~buttonBit(e.button);\n });\n on(window, 'pointerup', (e) => {\n this.buttons &= ~buttonBit(e.button);\n });\n on(window, 'blur', () => {\n this.buttons = 0;\n });\n on(canvas, 'contextmenu', (e) => e.preventDefault());\n }\n\n leftButtonDown(): boolean {\n return (this.buttons & 1) !== 0;\n }\n\n rightButtonDown(): boolean {\n return (this.buttons & 2) !== 0;\n }\n\n updateWorldPoint(cam: Camera): void {\n this.worldX = cam.scrollX + this.x / cam.zoom;\n this.worldY = cam.scrollY + this.y / cam.zoom;\n }\n\n destroy(): void {\n for (const fn of this.cleanup) fn();\n this.cleanup.length = 0;\n }\n\n private track(e: MouseEvent): void {\n const r = this.canvas.getBoundingClientRect();\n this.x = e.clientX - r.left;\n this.y = e.clientY - r.top;\n }\n}\n"],"names":["buttonBit","button","Pointer","canvas","fingers","isTouchUi","on","target","type","fn","handler","e","cam","r"],"mappings":";AAIA,SAASA,EAAUC,GAAwB;AACzC,SAAOA,MAAW,IAAI,IAAIA,MAAW,IAAI,IAAIA,MAAW,IAAI,IAAI;AAClE;AAMO,MAAMC,EAAQ;AAAA,EAenB,YAA6BC,GAA2B;AAA3B,SAAA,SAAAA;AAG3B,UAAMC,IAAUC,EAAA,GACVC,IAAK,CAAiCC,GAA8BC,GAASC,MAAuC;AACxH,YAAMC,IAAU,CAACC,MAAyB;AACxC,QAAIP,KAAWO,aAAa,gBAAgBA,EAAE,gBAAgB,WAC9DF,EAAGE,CAAC;AAAA,MACN;AACA,MAAAJ,EAAO,iBAAiBC,GAAME,CAAwB,GACtD,KAAK,QAAQ,KAAK,MAAMH,EAAO,oBAAoBC,GAAME,CAAwB,CAAC;AAAA,IACpF;AAGA,IAAAJ,EAAGH,GAAQ,eAAe,CAACQ,MAAM;AAE/B,MADA,KAAK,MAAMA,CAAC,GACR,CAAAT,EAAQ,cAAcS,CAAC,MAC3B,KAAK,WAAWX,EAAUW,EAAE,MAAM;AAAA,IACpC,CAAC,GACDL,EAAG,QAAQ,eAAe,CAACK,MAAM;AAC/B,WAAK,MAAMA,CAAC,GAERA,EAAE,UAAU,MAAG,KAAK,WAAWA,EAAE,UAAUX,EAAUW,EAAE,MAAM,OAAO,IAAI,KAAK,UAAUX,EAAUW,EAAE,MAAM,IAAI,KAAK,UAAU,CAACX,EAAUW,EAAE,MAAM;AAAA,IACrJ,CAAC,GACDL,EAAG,QAAQ,aAAa,CAACK,MAAM;AAC7B,WAAK,WAAW,CAACX,EAAUW,EAAE,MAAM;AAAA,IACrC,CAAC,GACDL,EAAG,QAAQ,QAAQ,MAAM;AACvB,WAAK,UAAU;AAAA,IACjB,CAAC,GACDA,EAAGH,GAAQ,eAAe,CAACQ,MAAMA,EAAE,gBAAgB;AAAA,EACrD;AAAA,EA/B6B;AAAA;AAAA;AAAA;AAAA;AAAA,EAV7B,OAAO,cAAqD;AAAA;AAAA,EAG5D,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,SAAS;AAAA,EACD,UAAU;AAAA,EACD,UAA0B,CAAA;AAAA,EAmC3C,iBAA0B;AACxB,YAAQ,KAAK,UAAU,OAAO;AAAA,EAChC;AAAA,EAEA,kBAA2B;AACzB,YAAQ,KAAK,UAAU,OAAO;AAAA,EAChC;AAAA,EAEA,iBAAiBC,GAAmB;AAClC,SAAK,SAASA,EAAI,UAAU,KAAK,IAAIA,EAAI,MACzC,KAAK,SAASA,EAAI,UAAU,KAAK,IAAIA,EAAI;AAAA,EAC3C;AAAA,EAEA,UAAgB;AACd,eAAWH,KAAM,KAAK,QAAS,CAAAA,EAAA;AAC/B,SAAK,QAAQ,SAAS;AAAA,EACxB;AAAA,EAEQ,MAAME,GAAqB;AACjC,UAAME,IAAI,KAAK,OAAO,sBAAA;AACtB,SAAK,IAAIF,EAAE,UAAUE,EAAE,MACvB,KAAK,IAAIF,EAAE,UAAUE,EAAE;AAAA,EACzB;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Rectangle.js","sources":["../../src/core/Rectangle.ts"],"sourcesContent":["import { Container, Graphics as PixiGraphics } from 'pixi.js';\nimport { GameObject } from './gameObject';\n\n/** A filled rectangle placed by its origin (centred by default). */\nexport class Rectangle extends GameObject(Container) {\n private readonly g = new PixiGraphics();\n private ox = 0.5;\n private oy = 0.5;\n\n constructor(\n private w: number,\n private h: number,\n private color: number,\n private fillAlpha = 1,\n ) {\n super();\n this.addChild(this.g);\n this.redraw();\n }\n\n get originX(): number {\n return this.ox;\n }\n\n get originY(): number {\n return this.oy;\n }\n\n get rectWidth(): number {\n return this.w;\n }\n\n get rectHeight(): number {\n return this.h;\n }\n\n get fillColor(): number {\n return this.color;\n }\n\n get fillOpacity(): number {\n return this.fillAlpha;\n }\n\n setOrigin(x = 0.5, y = x): this {\n this.ox = x;\n this.oy = y;\n return this.redraw();\n }\n\n setFillStyle(color: number, alpha = 1): this {\n this.color = color;\n this.fillAlpha = alpha;\n return this.redraw();\n }\n\n setDisplaySize(w: number, h: number): this {\n return this.setScale(w / this.w, h / this.h);\n }\n\n resize(w: number, h: number): this {\n this.w = w;\n this.h = h;\n return this.redraw();\n }\n\n private redraw(): this {\n this.g.clear().rect(-this.w * this.ox, -this.h * this.oy, this.w, this.h).fill({ color: this.color, alpha: this.fillAlpha });\n return this;\n }\n}\n"],"names":["Rectangle","GameObject","Container","w","h","color","fillAlpha","PixiGraphics","x","y","alpha"],"mappings":";;AAIO,MAAMA,UAAkBC,EAAWC,CAAS,EAAE;AAAA,EAKnD,YACUC,GACAC,GACAC,GACAC,IAAY,GACpB;AACA,UAAA,GALQ,KAAA,IAAAH,GACA,KAAA,IAAAC,GACA,KAAA,QAAAC,GACA,KAAA,YAAAC,GAGR,KAAK,SAAS,KAAK,CAAC,GACpB,KAAK,OAAA;AAAA,EACP;AAAA,EARU;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EARO,IAAI,IAAIC,EAAA;AAAA,EACjB,KAAK;AAAA,EACL,KAAK;AAAA,EAab,IAAI,UAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,cAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAUC,IAAI,KAAKC,IAAID,GAAS;AAC9B,gBAAK,KAAKA,GACV,KAAK,KAAKC,GACH,KAAK,OAAA;AAAA,EACd;AAAA,EAEA,aAAaJ,GAAeK,IAAQ,GAAS;AAC3C,gBAAK,QAAQL,GACb,KAAK,YAAYK,GACV,KAAK,OAAA;AAAA,EACd;AAAA,EAEA,eAAeP,GAAWC,GAAiB;AACzC,WAAO,KAAK,SAASD,IAAI,KAAK,GAAGC,IAAI,KAAK,CAAC;AAAA,EAC7C;AAAA,EAEA,OAAOD,GAAWC,GAAiB;AACjC,gBAAK,IAAID,GACT,KAAK,IAAIC,GACF,KAAK,OAAA;AAAA,EACd;AAAA,EAEQ,SAAe;AACrB,gBAAK,EAAE,MAAA,EAAQ,KAAK,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,KAAK,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,OAAO,KAAK,UAAA,CAAW,GACpH;AAAA,EACT;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Stage.js","sources":["../../src/core/Stage.ts"],"sourcesContent":["import { Container, type Application } from 'pixi.js';\nimport { Camera } from './Camera';\nimport { Clock } from './Clock';\nimport { ObjectFactory } from './ObjectFactory';\nimport { Pointer } from './Pointer';\nimport type { TextureRegistry } from './TextureRegistry';\nimport { Tweener } from './Tweener';\n\n/**\n * What a world object is created in: a world layer the camera moves (children sorted by depth), a screen layer\n * above it, and the per-frame services (tweens, timers, camera, pointer). Subclasses implement `update`.\n */\nexport abstract class Stage {\n /** Told of every fault a guarded step caught (error telemetry, if the game sets one). */\n static onCaught: ((where: string, err: unknown) => void) | null = null;\n\n readonly world = new Container({ sortableChildren: true });\n readonly screen = new Container();\n readonly add = new ObjectFactory(this);\n readonly tweens = new Tweener();\n readonly time = new Clock();\n readonly cameras: { main: Camera };\n readonly input: { activePointer: Pointer };\n private readonly resizeListeners: (() => void)[] = [];\n private lastWidth = 0;\n private lastHeight = 0;\n\n constructor(\n readonly app: Application,\n readonly textures: TextureRegistry,\n ) {\n app.stage.addChild(this.world, this.screen);\n this.cameras = { main: new Camera(this.world, this.screen) };\n this.input = { activePointer: new Pointer(app.canvas) };\n this.syncSize();\n }\n\n get width(): number {\n return this.app.screen.width;\n }\n\n get height(): number {\n return this.app.screen.height;\n }\n\n get fps(): number {\n return this.app.ticker.FPS;\n }\n\n /** Calls `fn` whenever the window changes size; the returned function stops it (a per-map listener must). */\n onResize(fn: () => void): () => void {\n this.resizeListeners.push(fn);\n return () => {\n const i = this.resizeListeners.indexOf(fn);\n if (i >= 0) this.resizeListeners.splice(i, 1);\n };\n }\n\n abstract update(now: number, delta: number): void;\n\n /**\n * One frame: game logic, then tweens and timers, then the camera. Pixi renders after. Each step is guarded: a\n * ticker listener that throws never asks for the next animation frame, so one bad frame used to stop the world for\n * good — the canvas frozen and no input reaching the server (seen on Android).\n */\n frame(delta: number): void {\n const now = performance.now();\n this.guard('resize', () => this.syncSize());\n this.guard('update', () => this.update(now, delta));\n this.guard('tweens', () => this.tweens.update(delta));\n this.guard('timers', () => this.time.update(now));\n this.guard('camera', () => this.cameras.main.apply(now));\n }\n\n private readonly reported = new Set<string>();\n\n /**\n * Runs one step of a frame and swallows what it throws, so the steps after it still run. Subclasses guard the parts\n * of their own `update` too: a fault before the camera follows the player used to leave the view standing on one\n * spot while the world went on.\n */\n protected guard(step: string, fn: () => void): void {\n this.guarded(step, fn, undefined);\n }\n\n /** `guard` for a step that answers something: `fallback` when it threw. */\n protected guarded<T>(step: string, fn: () => T, fallback: T): T {\n try {\n return fn();\n } catch (err) {\n this.caught(step, err);\n return fallback;\n }\n }\n\n /** What a guarded step threw: logged once per distinct error, or a fault repeating every frame floods the log at 60 a second. */\n protected caught(step: string, err: unknown): void {\n const key = `${step}|${err instanceof Error ? err.message : String(err)}`;\n if (this.reported.has(key)) return;\n this.reported.add(key);\n console.error(`[frame] ${step} failed`, err);\n // caught here, so the window's error listener never hears of it: tell the telemetry ourselves\n Stage.onCaught?.(`[frame] ${step}`, err);\n }\n\n destroy(): void {\n this.tweens.clear();\n this.time.clear();\n this.input.activePointer.destroy();\n this.world.destroy({ children: true, context: true });\n this.screen.destroy({ children: true, context: true });\n }\n\n private syncSize(): void {\n const { width, height } = this.app.screen;\n const cam = this.cameras.main;\n cam.width = width;\n cam.height = height;\n if (width === this.lastWidth && height === this.lastHeight) return;\n this.lastWidth = width;\n this.lastHeight = height;\n // a copy: a listener may unsubscribe itself (or another) while they run\n for (const fn of [...this.resizeListeners]) fn();\n }\n}\n"],"names":["Stage","app","textures","Camera","Pointer","Container","ObjectFactory","Tweener","Clock","fn","i","delta","now","step","fallback","err","key","width","height","cam"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Text.js","sources":["../../src/core/Text.ts"],"sourcesContent":["import { Text as PixiText, type TextStyleOptions } from 'pixi.js';\nimport { GameObject } from './gameObject';\n\nexport interface TextStyle {\n fontFamily?: string;\n fontSize: string | number;\n color?: string;\n stroke?: string;\n strokeThickness?: number;\n /** Texture pixels per world pixel: world text is zoomed by the camera, so it is rasterised finer. */\n resolution?: number;\n align?: 'left' | 'center' | 'right';\n lineSpacing?: number;\n wordWrapWidth?: number;\n}\n\n/** Canvas text in world or screen space. */\nexport class Text extends GameObject(PixiText) {\n constructor(x: number, y: number, text: string, style: TextStyle) {\n // only keys that are set: an explicit undefined overrides Pixi's default (leading: undefined = NaN height)\n const pixiStyle: TextStyleOptions = {\n fontFamily: style.fontFamily ?? 'monospace',\n fontSize: typeof style.fontSize === 'string' ? parseFloat(style.fontSize) : style.fontSize,\n fill: style.color ?? '#ffffff',\n };\n if (style.stroke) pixiStyle.stroke = { color: style.stroke, width: style.strokeThickness ?? 1, join: 'round' };\n if (style.align) pixiStyle.align = style.align;\n if (style.lineSpacing !== undefined) pixiStyle.leading = style.lineSpacing;\n if (style.wordWrapWidth !== undefined) {\n pixiStyle.wordWrap = true;\n pixiStyle.wordWrapWidth = style.wordWrapWidth;\n }\n super({ text, resolution: style.resolution ?? 1, style: pixiStyle });\n this.position.set(x, y);\n }\n\n setOrigin(x = 0.5, y = x): this {\n this.anchor.set(x, y);\n return this;\n }\n\n setText(text: string): this {\n this.text = text;\n return this;\n }\n}\n"],"names":["Text","GameObject","PixiText","x","y","text","style","pixiStyle"],"mappings":";;AAiBO,MAAMA,UAAaC,EAAWC,CAAQ,EAAE;AAAA,EAC7C,YAAYC,GAAWC,GAAWC,GAAcC,GAAkB;AAEhE,UAAMC,IAA8B;AAAA,MAClC,YAAYD,EAAM,cAAc;AAAA,MAChC,UAAU,OAAOA,EAAM,YAAa,WAAW,WAAWA,EAAM,QAAQ,IAAIA,EAAM;AAAA,MAClF,MAAMA,EAAM,SAAS;AAAA,IAAA;AAEvB,IAAIA,EAAM,WAAQC,EAAU,SAAS,EAAE,OAAOD,EAAM,QAAQ,OAAOA,EAAM,mBAAmB,GAAG,MAAM,QAAA,IACjGA,EAAM,UAAOC,EAAU,QAAQD,EAAM,QACrCA,EAAM,gBAAgB,WAAWC,EAAU,UAAUD,EAAM,cAC3DA,EAAM,kBAAkB,WAC1BC,EAAU,WAAW,IACrBA,EAAU,gBAAgBD,EAAM,gBAElC,MAAM,EAAE,MAAAD,GAAM,YAAYC,EAAM,cAAc,GAAG,OAAOC,GAAW,GACnE,KAAK,SAAS,IAAIJ,GAAGC,CAAC;AAAA,EACxB;AAAA,EAEA,UAAUD,IAAI,KAAKC,IAAID,GAAS;AAC9B,gBAAK,OAAO,IAAIA,GAAGC,CAAC,GACb;AAAA,EACT;AAAA,EAEA,QAAQC,GAAoB;AAC1B,gBAAK,OAAOA,GACL;AAAA,EACT;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"TextureRegistry.js","sources":["../../src/core/TextureRegistry.ts"],"sourcesContent":["import { CanvasSource, Rectangle, Texture } from 'pixi.js';\n\n/** A canvas-backed texture with named frames; repaint the canvas, then `refresh()` to upload it. */\nexport class CanvasTexture {\n readonly frames = new Map<string, Texture>();\n readonly source: CanvasSource;\n readonly whole: Texture;\n\n constructor(\n readonly key: string,\n readonly canvas: HTMLCanvasElement,\n ) {\n this.source = new CanvasSource({ resource: canvas, scaleMode: 'nearest' });\n this.whole = new Texture({ source: this.source });\n }\n\n get width(): number {\n return this.canvas.width;\n }\n\n get height(): number {\n return this.canvas.height;\n }\n\n get context(): CanvasRenderingContext2D {\n return this.canvas.getContext('2d')!;\n }\n\n /** Adds a frame cut from the canvas (`_source` kept for the old call shape). */\n add(name: string, _source: number, x: number, y: number, w: number, h: number): void {\n this.frames.get(name)?.destroy();\n this.frames.set(name, new Texture({ source: this.source, frame: new Rectangle(x, y, w, h) }));\n }\n\n /**\n * Registers under this texture a frame that lives on another one (an atlas page after the first), so the frame is\n * found by this texture's key. A texture of its own over the other page's source: each side frees its own.\n */\n adopt(name: string, from: CanvasTexture): void {\n const src = from.frames.get(name);\n if (!src) return;\n this.frames.get(name)?.destroy();\n this.frames.set(name, new Texture({ source: from.source, frame: src.frame.clone() }));\n }\n\n refresh(): void {\n this.source.update();\n }\n\n destroy(): void {\n for (const t of this.frames.values()) t.destroy();\n this.frames.clear();\n this.whole.destroy();\n this.source.destroy();\n }\n}\n\n/** Every texture of the world by key: the sprite atlas and per-player repainted sheets. */\nexport class TextureRegistry {\n private readonly textures = new Map<string, CanvasTexture>();\n /** Textures repainted or freed while in use (a rig body, its hand): a copy of one must snapshot it first. */\n private readonly live = new Set<string>();\n private missing = new Set<string>();\n private snapshots = 0;\n\n markLive(key: string): void {\n this.live.add(key);\n }\n\n isLive(key: string): boolean {\n return this.live.has(key);\n }\n\n /** A new texture holding frame `frame` of `key` as it is painted right now; returns its key (one frame, '0'). */\n snapshot(key: string, frame?: string | number): string {\n const src = this.get(key, frame);\n const { x, y, width, height } = src.frame;\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const from = this.textures.get(key)?.canvas;\n if (from) canvas.getContext('2d')!.drawImage(from, x, y, width, height, 0, 0, width, height);\n const copy = `${key}#still${++this.snapshots}`;\n this.addCanvas(copy, canvas).add('0', 0, 0, 0, width, height);\n return copy;\n }\n\n /**\n * `snapshot` into a texture kept for reuse (`into`): its canvas is repainted in place and uploaded again, and only\n * made anew when the frame's size changes. Frame '0' is the copy.\n */\n snapshotInto(key: string, frame: string | number | undefined, into: string): void {\n const src = this.get(key, frame);\n const { x, y, width, height } = src.frame;\n let tex = this.textures.get(into);\n if (!tex || tex.width !== width || tex.height !== height) {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n tex = this.addCanvas(into, canvas);\n tex.add('0', 0, 0, 0, width, height);\n }\n const ctx = tex.context;\n ctx.clearRect(0, 0, width, height);\n const from = this.textures.get(key)?.canvas;\n if (from) ctx.drawImage(from, x, y, width, height, 0, 0, width, height);\n tex.refresh();\n }\n\n exists(key: string): boolean {\n return this.textures.has(key);\n }\n\n addCanvas(key: string, canvas: HTMLCanvasElement): CanvasTexture {\n this.remove(key);\n const tex = new CanvasTexture(key, canvas);\n this.textures.set(key, tex);\n return tex;\n }\n\n /** A canvas cut into equal frames named 0, 1, 2... left to right, then row by row. */\n addSpriteSheet(key: string, canvas: HTMLCanvasElement, size: { frameWidth: number; frameHeight: number }): CanvasTexture {\n const tex = this.addCanvas(key, canvas);\n const cols = Math.floor(canvas.width / size.frameWidth);\n const rows = Math.floor(canvas.height / size.frameHeight);\n for (let i = 0; i < cols * rows; i++) {\n tex.add(String(i), 0, (i % cols) * size.frameWidth, Math.floor(i / cols) * size.frameHeight, size.frameWidth, size.frameHeight);\n }\n return tex;\n }\n\n createCanvas(key: string, width: number, height: number): CanvasTexture {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return this.addCanvas(key, canvas);\n }\n\n remove(key: string): void {\n this.textures.get(key)?.destroy();\n this.textures.delete(key);\n this.live.delete(key);\n }\n\n get(key: string, frame?: string | number): Texture {\n const tex = this.textures.get(key);\n if (!tex) return this.warn(key);\n if (frame === undefined || frame === '') return tex.whole;\n return tex.frames.get(String(frame)) ?? this.warn(`${key}/${frame}`);\n }\n\n private warn(what: string): Texture {\n if (!this.missing.has(what)) {\n this.missing.add(what);\n console.warn(`[textures] missing ${what}`);\n }\n return Texture.EMPTY;\n }\n}\n"],"names":["CanvasTexture","key","canvas","CanvasSource","Texture","name","_source","x","y","w","Rectangle","from","src","TextureRegistry","frame","width","height","copy","into","tex","ctx","size","cols","rows","i","what"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Timeline.js","sources":["../../src/core/Timeline.ts"],"sourcesContent":["import { TimelineBuilder } from './TimelineBuilder';\nimport type { NestedPlay } from './TimelineChild';\nimport type { Steppable, Tweener, TweenTarget } from './Tweener';\n\nexport interface TimelineOptions {\n /**\n * The manager that steps it: `stage.tweens` makes it follow the stage's `timeScale` and stop with the stage. Leave\n * it out to step the timeline yourself with `update(dt)` (a fixed step, a test, a timeline played by another).\n */\n tweens?: Tweener;\n /** Built but not playing until `play()` / `resume()`. */\n paused?: boolean;\n /** Extra plays after the first (-1 = forever). */\n repeat?: number;\n /** Every second play runs backwards. */\n yoyo?: boolean;\n /** Ms of stillness between plays. */\n repeatDelay?: number;\n /** Its own speed factor, on top of its manager's. */\n timeScale?: number;\n onUpdate?: (timeline: Timeline) => void;\n onRepeat?: (timeline: Timeline) => void;\n /** When the playhead reaches the end it is playing toward: the end, or the start when reversed. */\n onComplete?: (timeline: Timeline) => void;\n}\n\n/**\n * Tweens and calls laid out in time and played as one: in sequence by default, overlapping by position (`'<'`,\n * `'-=100'`, a label), side by side in `parallel` groups, nested (`add`). It can repeat, yoyo, run at its own speed,\n * pause, seek and play backwards, and `finished` resolves when it completes.\n *\n * Built in one go, then it plays: a timeline stepped while empty has nothing left to do and completes, so build it in\n * the frame it is made (or make it `paused` and `play()` it later). Children are kept sorted by start, which is the\n * order they are played in going forward (and the reverse going back), so a later tween on the same property wins.\n */\nexport class Timeline extends TimelineBuilder implements Steppable, NestedPlay {\n timeScale: number;\n readonly repeat: number;\n readonly yoyo: boolean;\n readonly repeatDelay: number;\n /** A timeline holds no targets of its own for `Tweener.killTweensOf`: stop it with `kill()`. */\n readonly targets: readonly object[] = [];\n private readonly options: TimelineOptions;\n private time = 0;\n private paused: boolean;\n private reversed = false;\n private done = false;\n private killed = false;\n /** In its manager's list: set again by anything that brings a finished timeline back to life. */\n private listed = false;\n /** The next render starts the run: a call standing at its first point fires. */\n private fresh = true;\n private nestedIn: Timeline | null = null;\n private resolveFinished: () => void = () => undefined;\n private finishedPromise: Promise<void>;\n private settled = false;\n /** Scratch for `locate`, so moving the playhead allocates nothing. */\n private locCycle = 0;\n private locLocal = 0;\n\n constructor(options: TimelineOptions = {}) {\n super();\n this.options = options;\n this.repeat = options.repeat ?? 0;\n this.yoyo = options.yoyo ?? false;\n this.repeatDelay = Math.max(0, options.repeatDelay ?? 0);\n this.timeScale = options.timeScale ?? 1;\n this.paused = options.paused ?? false;\n this.finishedPromise = this.newPromise();\n this.relist();\n }\n\n protected adopt(child: TimelineBuilder): NestedPlay {\n if (!(child instanceof Timeline) || child === this || child.nestedIn) throw new Error('Timeline.add: a timeline is played by one parent at most, never itself');\n if (!Number.isFinite(child.totalDuration)) throw new Error('Timeline.add: a timeline that repeats forever cannot be nested');\n child.nestedIn = this;\n return child;\n }\n\n // ── playing ───────────────────────────────────────────────────────────────────────────────────────────────────\n\n /** Every play and the delays between them, ms; Infinity when it repeats forever. */\n get totalDuration(): number {\n if (this.repeat === -1) return Infinity;\n return this.duration * (this.repeat + 1) + this.repeatDelay * this.repeat;\n }\n\n /** Where the playhead stands, ms from the start of the first play. */\n get currentTime(): number {\n return this.time;\n }\n\n /** 0–1 over the whole run (over the current play when it repeats forever). */\n get progress(): number {\n const total = this.totalDuration;\n if (Number.isFinite(total)) return total > 0 ? this.time / total : 1;\n const d = this.duration;\n if (d <= 0) return 1;\n this.locate(this.time, total);\n return this.locLocal / d;\n }\n\n get isPaused(): boolean {\n return this.paused;\n }\n\n get isReversed(): boolean {\n return this.reversed;\n }\n\n /** Still has somewhere to go: not completed, not killed. */\n get isActive(): boolean {\n return !this.done && !this.killed;\n }\n\n /** Resolves when the timeline completes (or is killed, so nothing awaits it forever). A restart makes a new one. */\n get finished(): Promise<void> {\n return this.finishedPromise;\n }\n\n /** Advances by `dt` ms (× `timeScale`): how a timeline with no manager is played. */\n update(dt: number): void {\n this.tick(dt);\n }\n\n /** @internal The manager's step: false once there is nothing left to play. */\n step(dt: number): boolean {\n // dropped by the manager if anything below throws: only a clean return keeps it listed\n this.listed = false;\n const alive = this.tick(dt);\n this.listed = alive;\n return alive;\n }\n\n pause(): this {\n this.paused = true;\n return this;\n }\n\n /** Carries on the way it was going. */\n resume(): this {\n this.paused = false;\n this.wake();\n return this;\n }\n\n /** Plays forward from where it stands. */\n play(): this {\n this.reversed = false;\n return this.resume();\n }\n\n /** Turns round and plays toward the start (or forward again, if it was reversed). */\n reverse(): this {\n this.reversed = !this.reversed;\n return this.resume();\n }\n\n /**\n * Jumps to `to` (ms or a label) and shows it there. Calls on the way are skipped unless `fireCalls`; the playing\n * state is kept, so a paused timeline stays paused.\n */\n seek(to: number | string, fireCalls = false): this {\n const total = this.totalDuration;\n const at = typeof to === 'number' ? to : this.labels.get(to);\n if (at === undefined) throw new Error(`Timeline.seek: no label \"${to}\"`);\n const target = Math.min(total, Math.max(0, at));\n this.render(this.time, target, fireCalls, this.fresh && fireCalls);\n this.time = target;\n this.fresh = false;\n this.wake();\n return this;\n }\n\n /** Back to the start, playing forward, with a new `finished` if the old one was settled. */\n restart(): this {\n this.render(this.time, 0, false, false);\n this.time = 0;\n this.reversed = false;\n this.paused = false;\n this.fresh = true;\n if (this.settled) this.finishedPromise = this.newPromise();\n this.wake();\n return this;\n }\n\n /** Stops for good where it stands (nothing is put back) and settles `finished`. */\n kill(): void {\n this.killed = true;\n this.settle();\n }\n\n /** @internal The manager's name for `kill` (`Tweener.clear`, a stage destroyed). */\n remove(): void {\n this.kill();\n }\n\n /** @internal Played by a parent: its own time and calls, as one span. */\n playNested(from: number, to: number, fire: boolean, includeStart: boolean): void {\n this.render(from, to, fire, includeStart);\n this.time = to;\n this.options.onUpdate?.(this);\n if (to >= this.totalDuration && from < to) this.options.onComplete?.(this);\n }\n\n // ── inside ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n private tick(dt: number): boolean {\n if (this.killed || this.nestedIn) return false;\n if (this.done) return false;\n if (this.paused) return true;\n const total = this.totalDuration;\n const from = this.time;\n const delta = dt * this.timeScale;\n let to = this.reversed ? from - delta : from + delta;\n if (to > total) to = total;\n if (to < 0) to = 0;\n if (to !== from || this.fresh) {\n this.render(from, to, true, this.fresh);\n this.time = to;\n this.fresh = false;\n this.options.onUpdate?.(this);\n }\n if (this.killed) return false;\n if (this.reversed ? to <= 0 : to >= total) {\n this.done = true;\n this.options.onComplete?.(this);\n this.settle();\n return false;\n }\n return true;\n }\n\n /** Moves the playhead from `from` to `to` (total time), play by play. */\n private render(from: number, to: number, fire: boolean, includeStart: boolean): void {\n const d = this.duration;\n const total = this.totalDuration;\n const period = d + this.repeatDelay;\n if (this.repeat === 0 || period <= 0) {\n this.playCycle(0, Math.min(d, Math.max(0, from)), Math.min(d, Math.max(0, to)), fire, includeStart);\n return;\n }\n this.locate(from, total);\n const ca = this.locCycle;\n const la = this.locLocal;\n this.locate(to, total);\n const cb = this.locCycle;\n const lb = this.locLocal;\n if (ca === cb) {\n this.playCycle(ca, la, lb, fire, includeStart);\n return;\n }\n // a yoyo's turn has just played its end point, so the next play leaves it out; a plain repeat starts afresh\n const restart = !this.yoyo;\n if (cb > ca) {\n this.playCycle(ca, la, d, fire, includeStart);\n // a frame that crosses many plays (a very short timeline) shows only the last two\n for (let c = Math.max(ca + 1, cb - 1); c <= cb; c++) {\n if (this.nextPlay()) return;\n this.playCycle(c, 0, c === cb ? lb : d, fire, restart);\n }\n } else {\n this.playCycle(ca, la, 0, fire, includeStart);\n for (let c = Math.min(ca - 1, cb + 1); c >= cb; c--) {\n if (this.nextPlay()) return;\n this.playCycle(c, d, c === cb ? lb : 0, fire, restart);\n }\n }\n }\n\n /** A play boundary: `onRepeat`, and a forever timeline whose every target is gone ends here. Answers \"stop\". */\n private nextPlay(): boolean {\n if (this.repeat === -1 && this.orphaned()) {\n this.kill();\n return true;\n }\n this.options.onRepeat?.(this);\n return this.killed;\n }\n\n private playCycle(cycle: number, a: number, b: number, fire: boolean, includeStart: boolean): void {\n if (this.yoyo && cycle % 2 === 1) {\n const d = this.duration;\n a = d - a;\n b = d - b;\n }\n const list = this.children;\n if (b >= a) for (let i = 0; i < list.length; i++) list[i]!.play(a, b, fire, includeStart);\n else for (let i = list.length - 1; i >= 0; i--) list[i]!.play(a, b, fire, includeStart);\n }\n\n /** Total time → play and time within it, into `locCycle` / `locLocal`. */\n private locate(t: number, total: number): void {\n const d = this.duration;\n if (t >= total) {\n this.locCycle = this.repeat;\n this.locLocal = d;\n return;\n }\n const period = d + this.repeatDelay;\n const c = Math.floor(t / period);\n this.locCycle = c;\n this.locLocal = Math.min(d, t - c * period);\n }\n\n /**\n * Every tween's every target is a destroyed display object. Checked only when a forever timeline turns round, as\n * `Tween` drops a forever tween whose sprites were freed: otherwise it would hold them for good.\n */\n private orphaned(): boolean {\n let any = false;\n for (const c of this.children) {\n if (c.kind !== 'tween') continue;\n for (const t of c.targets) {\n if ((t as TweenTarget).destroyed !== true) return false;\n any = true;\n }\n }\n return any;\n }\n\n /** Brought back to life (reversed, sought, restarted, resumed): its manager steps it again. */\n private wake(): void {\n if (this.killed) return;\n // the next step finds out whether there is anywhere left to go, and completes it if not\n this.done = false;\n this.relist();\n }\n\n private relist(): void {\n const tweens = this.options.tweens;\n if (!tweens || this.listed || this.killed || this.nestedIn) return;\n this.listed = true;\n tweens.run(this);\n }\n\n private settle(): void {\n if (this.settled) return;\n this.settled = true;\n this.resolveFinished();\n }\n\n private newPromise(): Promise<void> {\n this.settled = false;\n return new Promise<void>((resolve) => {\n this.resolveFinished = resolve;\n });\n }\n}\n"],"names":["Timeline","TimelineBuilder","options","child","total","d","dt","alive","to","fireCalls","at","target","from","fire","includeStart","delta","period","ca","la","cb","lb","restart","c","cycle","a","b","list","i","any","t","tweens","resolve"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"TimelineBuilder.js","sources":["../../src/core/TimelineBuilder.ts"],"sourcesContent":["import { easeByName, type EaseFn } from './eases';\nimport { placeAt, type PlaceContext, type TimelinePosition } from './timelinePlace';\nimport { TimelineChild, type NestedPlay } from './TimelineChild';\nimport type { TweenTarget } from './Tweener';\n\n/** A number to tween to, or `{ from, to }` to start from a given value instead of the target's own. */\nexport type TimelineProps = Record<string, number | { from?: number; to: number }>;\n\nexport interface TimelineTweenOptions {\n /** Ms (default 1000, as `Tweener`'s). */\n duration?: number;\n /** A name `easeByName` knows ('Quad.Out' when left out, as `Tweener`'s) or an ease function. */\n ease?: string | EaseFn;\n at?: TimelinePosition;\n}\n\n/**\n * The building half of `Timeline`: where each tween, call, gap and nested timeline goes. Kept apart from the playing\n * half so each stays readable; nothing here runs per frame.\n */\nexport abstract class TimelineBuilder {\n /** Sorted by start; equal starts keep the order they were added in. */\n protected readonly children: TimelineChild[] = [];\n protected readonly labels = new Map<string, number>();\n /** The longest child's end, kept as children are added rather than summed every frame. */\n private length = 0;\n private lastStart = 0;\n private lastEnd = 0;\n private group: { start: number; end: number } | null = null;\n\n /** Checks `child` may be played by this timeline and takes it over (it stops being stepped alone). */\n protected abstract adopt(child: TimelineBuilder): NestedPlay;\n\n /** One play, ms (the longest child's end). */\n get duration(): number {\n return this.length;\n }\n\n /** Tweens the numeric `props` of `targets` (one object or several), after what came before unless `at` says. */\n to(targets: object | readonly object[], props: TimelineProps, options: TimelineTweenOptions = {}): this {\n const list = (Array.isArray(targets) ? [...targets] : [targets]) as TweenTarget[];\n const names: string[] = [];\n const ends: number[] = [];\n const froms: (number | undefined)[] = [];\n for (const [name, v] of Object.entries(props)) {\n names.push(name);\n if (typeof v === 'number') {\n ends.push(v);\n froms.push(undefined);\n } else {\n ends.push(v.to);\n froms.push(v.from);\n }\n }\n const ease = typeof options.ease === 'function' ? options.ease : easeByName(options.ease);\n const duration = Math.max(0, options.duration ?? 1000);\n const start = this.place(options.at);\n return this.insert(new TimelineChild('tween', start, duration, list, names, ends, froms, ease));\n }\n\n /** Calls `fn` when the playhead passes `at` (default: the end so far), going either way. */\n call(fn: () => void, at?: TimelinePosition): this {\n return this.insert(new TimelineChild('call', this.place(at), 0, [], [], [], [], null, fn));\n }\n\n /** `ms` of nothing: the next child starts that much later, and a wait at the end makes the timeline longer. */\n wait(ms: number): this {\n return this.insert(new TimelineChild('gap', this.place(undefined), Math.max(0, ms)));\n }\n\n /** Names a point (default: the end so far), for positions and `seek`. */\n label(name: string, at?: TimelinePosition): this {\n this.labels.set(name, this.place(at));\n return this;\n }\n\n /** Where a label stands, if it is set. */\n labelTime(name: string): number | undefined {\n return this.labels.get(name);\n }\n\n /**\n * Everything `build` adds starts together, at `at` (default: the end so far); a position inside is measured from\n * the group's start. What follows the group starts at its longest child's end.\n */\n parallel(build: (timeline: this) => void, at?: TimelinePosition): this {\n const outer = this.group;\n const start = this.place(at);\n const group = { start, end: start };\n this.group = group;\n try {\n build(this);\n } finally {\n this.group = outer;\n if (outer) outer.end = Math.max(outer.end, group.end);\n this.lastStart = start;\n this.lastEnd = group.end;\n }\n return this;\n }\n\n /** Plays another timeline (built, not repeating forever) as one child of this one; it is no longer stepped alone. */\n add(child: TimelineBuilder, at?: TimelinePosition): this {\n const nested = this.adopt(child);\n const start = this.place(at);\n return this.insert(new TimelineChild('nested', start, nested.totalDuration, [], [], [], [], null, null, nested));\n }\n\n private place(at: TimelinePosition | undefined): number {\n const end = this.length;\n const ctx: PlaceContext = { fallback: this.group ? this.group.start : end, end, lastStart: this.lastStart, lastEnd: this.lastEnd, labels: this.labels };\n return placeAt(at, ctx);\n }\n\n private insert(child: TimelineChild): this {\n let i = this.children.length;\n while (i > 0 && this.children[i - 1]!.start > child.start) i--;\n this.children.splice(i, 0, child);\n this.length = Math.max(this.length, child.end);\n this.lastStart = child.start;\n this.lastEnd = child.end;\n if (this.group) this.group.end = Math.max(this.group.end, child.end);\n return this;\n }\n}\n"],"names":["TimelineBuilder","targets","props","options","list","names","ends","froms","name","v","ease","easeByName","duration","start","TimelineChild","fn","at","ms","build","outer","group","child","nested","end","ctx","placeAt","i"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"TimelineChild.js","sources":["../../src/core/TimelineChild.ts"],"sourcesContent":["import type { EaseFn } from './eases';\nimport type { TweenTarget } from './Tweener';\nimport { readProp, writeProp } from './tweenProps';\n\n/** What a nested timeline offers the timeline playing it. */\nexport interface NestedPlay {\n readonly totalDuration: number;\n /** Plays from `from` to `to` (its own time, ms); `includeStart` fires a call standing exactly at `from`. */\n playNested(from: number, to: number, fire: boolean, includeStart: boolean): void;\n}\n\nexport type ChildKind = 'tween' | 'call' | 'gap' | 'nested';\n\n/**\n * One thing on a timeline: a tween, a call, an empty gap (`wait`) or a nested timeline, at `start` for `duration` ms.\n * A tween reads its start values the first time the playhead reaches it — not when it is added — so a tween that\n * follows another on the same property starts where that one ended, and keeps those values for seeking and reversing.\n */\nexport class TimelineChild {\n /** Every target × prop start value, captured once (`inited`). */\n private readonly froms: number[] = [];\n private inited = false;\n /** Progress (tween) or own time (nested) last written; NaN before the first, so the first always writes. */\n private last = Number.NaN;\n\n constructor(\n readonly kind: ChildKind,\n readonly start: number,\n readonly duration: number,\n readonly targets: readonly TweenTarget[] = [],\n private readonly props: readonly string[] = [],\n private readonly ends: readonly number[] = [],\n /** A start value given as `{ from, to }`, per prop; undefined reads the target's own. */\n private readonly givenFrom: readonly (number | undefined)[] = [],\n private readonly ease: EaseFn | null = null,\n private readonly fn: (() => void) | null = null,\n private readonly nested: NestedPlay | null = null,\n ) {}\n\n get end(): number {\n return this.start + this.duration;\n }\n\n /**\n * The playhead moved from `a` to `b` (the parent's own time, either way). Calls fire when `fire` is set and their\n * time lies in the span with its start left out and its end kept — so a call met at the end of one frame does not\n * fire again at the start of the next — unless `includeStart` keeps the start too (a run or a repeat beginning).\n */\n play(a: number, b: number, fire: boolean, includeStart: boolean): void {\n switch (this.kind) {\n case 'tween':\n this.playTween(b);\n break;\n case 'call':\n if (fire && this.fn && crossed(a, b, this.start, includeStart)) this.fn();\n break;\n case 'nested':\n this.playNested(a, b, fire, includeStart);\n break;\n case 'gap':\n break;\n }\n }\n\n private playTween(b: number): void {\n if (!this.inited) {\n if (b < this.start) return;\n this.capture();\n }\n let p: number;\n if (this.duration > 0) p = Math.min(1, Math.max(0, (b - this.start) / this.duration));\n else p = b >= this.start ? 1 : 0;\n if (p === this.last) return;\n this.last = p;\n const k = this.ease ? this.ease(p) : p;\n const props = this.props;\n const np = props.length;\n const targets = this.targets;\n for (let t = 0; t < targets.length; t++) {\n const target = targets[t]!;\n // a destroyed display object is skipped, as Tweener does: its owner freed it, nothing is left to move\n if (target.destroyed === true) continue;\n for (let i = 0; i < np; i++) {\n const from = this.froms[t * np + i]!;\n writeProp(target, props[i]!, from + (this.ends[i]! - from) * k);\n }\n }\n }\n\n private capture(): void {\n this.inited = true;\n const props = this.props;\n for (const target of this.targets) {\n for (let i = 0; i < props.length; i++) this.froms.push(this.givenFrom[i] ?? readProp(target, props[i]!));\n }\n }\n\n private playNested(a: number, b: number, fire: boolean, includeStart: boolean): void {\n const sub = this.nested;\n if (!sub) return;\n if (!this.inited) {\n if (b < this.start) return;\n this.inited = true;\n }\n const total = sub.totalDuration;\n const la = Math.min(total, Math.max(0, a - this.start));\n const lb = Math.min(total, Math.max(0, b - this.start));\n // the nested timeline's own start point is \"crossed\" when the parent came from outside its span\n const forward = b >= a;\n const subInclude = forward ? a < this.start || (a === this.start && includeStart) : a > this.end || (a === this.end && includeStart);\n if (la === lb && lb === this.last && !subInclude) return;\n this.last = lb;\n sub.playNested(la, lb, fire, subInclude);\n }\n}\n\n/**\n * Whether a call at `at` lies in the span the playhead swept from `a` to `b`: the end kept, the start only when\n * `includeStart` says so.\n */\nexport function crossed(a: number, b: number, at: number, includeStart: boolean): boolean {\n if (at === a) return includeStart;\n return b >= a ? at > a && at <= b : at < a && at >= b;\n}\n"],"names":["TimelineChild","kind","start","duration","targets","props","ends","givenFrom","ease","fn","nested","a","b","fire","includeStart","crossed","p","k","np","t","target","i","from","writeProp","readProp","sub","total","la","lb","subInclude","at"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Tweener.js","sources":["../../src/core/Tweener.ts"],"sourcesContent":["import { easeByName } from './eases';\nimport { Timeline, type TimelineOptions } from './Timeline';\nimport { readProp as read, writeProp as write } from './tweenProps';\n\nexport type TweenTarget = Record<string, unknown> & { destroyed?: boolean };\n\n/** Tween config in the shape the renderer has always used: numeric props at the top level or under `props`. */\nexport interface TweenConfig {\n targets: object | object[];\n duration?: number;\n delay?: number;\n /** Ms to stay at the end values before a yoyo plays back. */\n hold?: number;\n yoyo?: boolean;\n /** Extra plays after the first (-1 = forever). */\n repeat?: number;\n ease?: string;\n props?: Record<string, number>;\n onStart?: () => void;\n onUpdate?: () => void;\n onComplete?: () => void;\n [prop: string]: unknown;\n}\n\nconst RESERVED = new Set([\n 'targets', 'duration', 'delay', 'hold', 'yoyo', 'repeat', 'ease', 'props', 'onStart', 'onUpdate', 'onComplete',\n]);\n\ninterface Track {\n target: TweenTarget;\n prop: string;\n from: number;\n to: number;\n}\n\n/** One running tween; `remove()` stops it without calling onComplete. */\nexport class Tween {\n private tracks: Track[] | null = null;\n private elapsed = 0;\n private plays = 0;\n removed = false;\n readonly targets: TweenTarget[];\n\n constructor(\n private readonly cfg: TweenConfig,\n private readonly ends: Record<string, number>,\n ) {\n const t = cfg.targets;\n this.targets = (Array.isArray(t) ? t : [t]) as TweenTarget[];\n }\n\n /**\n * Every target is a destroyed display object: nothing is left to move. A forever tween (`repeat: -1`) on a sprite\n * its owner freed used to stay in the list for good, holding the sprite, and the list grew with every map loaded.\n * A plain object (a clock a callback reads) has no `destroyed` and is never taken for one. Only a forever tween is\n * dropped this way: a finite one still runs out and calls its `onComplete`, which may clean up more than the sprite.\n */\n private orphaned(): boolean {\n if (this.cfg.repeat !== -1) return false;\n const ts = this.targets;\n if (ts.length === 0) return false;\n for (const t of ts) if (t.destroyed !== true) return false;\n return true;\n }\n\n remove(): void {\n this.removed = true;\n }\n\n stop(): void {\n this.removed = true;\n }\n\n /** Advances by `dt` ms; returns false once the tween is finished. */\n step(dt: number): boolean {\n if (this.removed) return false;\n if (this.orphaned()) {\n this.removed = true;\n return false;\n }\n const cfg = this.cfg;\n const delay = cfg.delay ?? 0;\n this.elapsed += dt;\n if (this.elapsed < delay) return true;\n if (!this.tracks) {\n this.tracks = [];\n for (const target of this.targets) {\n for (const [prop, to] of Object.entries(this.ends)) this.tracks.push({ target, prop, from: read(target, prop), to });\n }\n cfg.onStart?.();\n }\n const duration = Math.max(1, cfg.duration ?? 1000);\n const hold = cfg.yoyo ? cfg.hold ?? 0 : 0;\n const cycle = cfg.yoyo ? duration * 2 + hold : duration;\n let local = this.elapsed - delay - this.plays * cycle;\n const repeat = cfg.repeat ?? 0;\n let finished = false;\n if (local >= cycle) {\n if (repeat === -1 || this.plays < repeat) {\n this.plays++;\n local -= cycle;\n } else {\n local = cycle;\n finished = true;\n }\n }\n let p: number;\n if (!cfg.yoyo) p = local / duration;\n else if (local <= duration) p = local / duration;\n else if (local <= duration + hold) p = 1;\n else p = 1 - (local - duration - hold) / duration;\n const k = easeByName(cfg.ease)(Math.min(1, Math.max(0, p)));\n for (const t of this.tracks) if (!t.target.destroyed) write(t.target, t.prop, t.from + (t.to - t.from) * k);\n cfg.onUpdate?.();\n if (finished) {\n this.removed = true;\n cfg.onComplete?.();\n return false;\n }\n return true;\n }\n}\n\n/**\n * Anything the manager can step each frame: a `Tween`, or a `Timeline` it drives. `step` answers false once finished\n * (the manager then drops it); `remove` stops it for good; `targets` is what `killTweensOf` matches against.\n */\nexport interface Steppable {\n step(dt: number): boolean;\n remove(): void;\n readonly targets: readonly object[];\n}\n\n/** Frame-driven tween manager (the renderer's replacement for Phaser's TweenManager). */\nexport class Tweener {\n /** Global speed factor; hit-stop slows every tween at once. */\n timeScale = 1;\n private tweens: Steppable[] = [];\n\n add(cfg: TweenConfig): Tween {\n const ends: Record<string, number> = { ...(cfg.props ?? {}) };\n const starts: Record<string, number> = {};\n for (const [k, v] of Object.entries(cfg)) {\n if (RESERVED.has(k)) continue;\n if (typeof v === 'number') ends[k] = v;\n else if (v && typeof v === 'object' && 'to' in v) {\n const range = v as { from?: number; to: number };\n ends[k] = range.to;\n if (range.from !== undefined) starts[k] = range.from;\n }\n }\n // `{ from, to }` jumps to `from` right away, before any delay\n const targets = (Array.isArray(cfg.targets) ? cfg.targets : [cfg.targets]) as Record<string, unknown>[];\n for (const t of targets) for (const [k, v] of Object.entries(starts)) t[k] = v;\n const tween = new Tween(cfg, ends);\n this.tweens.push(tween);\n return tween;\n }\n\n /**\n * Steps `item` every frame from the next one on, at this manager's `timeScale`, until its `step` answers false or\n * the manager is cleared (a stage's manager is cleared when the stage is destroyed).\n */\n run<T extends Steppable>(item: T): T {\n this.tweens.push(item);\n return item;\n }\n\n /** A `Timeline` driven by this manager: it follows `timeScale` and stops with `clear`. */\n timeline(options: Omit<TimelineOptions, 'tweens'> = {}): Timeline {\n return new Timeline({ ...options, tweens: this });\n }\n\n killTweensOf(target: object | object[]): void {\n const set = new Set(Array.isArray(target) ? target : [target]);\n for (const t of this.tweens) if (t.targets.some((x) => set.has(x))) t.remove();\n }\n\n update(dt: number): void {\n const scaled = dt * this.timeScale;\n // tweens started from a callback run from the next frame on, like Phaser's\n // compacted in place: the ones added meanwhile sit past `n` and keep their order after the survivors\n const list = this.tweens;\n const n = list.length;\n let kept = 0;\n for (let i = 0; i < n; i++) {\n const t = list[i]!;\n if (stepSafely(t, scaled)) list[kept++] = t;\n }\n // a callback that cleared the manager dropped this list, and every tween on it was removed\n if (this.tweens !== list) return;\n for (let i = n; i < list.length; i++) list[kept++] = list[i]!;\n list.length = kept;\n }\n\n clear(): void {\n for (const t of this.tweens) t.remove();\n this.tweens = [];\n }\n}\n\n/** A tween whose step or callback throws is dropped, so it neither throws every frame nor leaves the list half compacted. */\nfunction stepSafely(t: { step(dt: number): boolean }, dt: number): boolean {\n try {\n return t.step(dt);\n } catch (err) {\n console.error('[tween] dropped after an error', err);\n return false;\n }\n}\n"],"names":["RESERVED","Tween","cfg","ends","t","ts","dt","delay","target","prop","to","read","duration","hold","cycle","local","repeat","finished","p","k","easeByName","write","Tweener","starts","v","range","targets","tween","item","options","Timeline","set","x","scaled","list","kept","i","stepSafely","err"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"atlas.js","sources":["../../src/core/atlas.ts"],"sourcesContent":["import type { TextureRegistry } from './TextureRegistry';\nimport { ATLAS_PAGE_FULL, planAtlas } from './atlasPlan';\n\ninterface AtlasItem {\n name: string;\n canvas: HTMLCanvasElement;\n frameWidth?: number;\n frameHeight?: number;\n}\n\n/**\n * Packs many small procedurally painted canvases into as few textures as the GPU allows, so Pixi can batch every\n * sprite of the world into very few draw calls. One page on every desktop (up to 8192 square); a device whose GPU\n * takes less gets pages of `limit` (`atlasPageLimit`, `planAtlas`). Every frame is registered under the one `key`\n * whichever page holds it — the pages after the first are textures of their own (`key~1`, `key~2`…) whose frames\n * the first page's texture adopts — so the world keeps asking for `atlas` and a frame name, and never knows.\n * Frame names: `name` for single images, `name#i` for sheet frames.\n */\nexport class AtlasBuilder {\n private items: AtlasItem[] = [];\n\n add(name: string, canvas: HTMLCanvasElement): void {\n this.items.push({ name, canvas });\n }\n\n addSheet(name: string, canvas: HTMLCanvasElement, frameWidth: number, frameHeight: number): void {\n this.items.push({ name, canvas, frameWidth, frameHeight });\n }\n\n build(textures: TextureRegistry, key: string, limit = ATLAS_PAGE_FULL): void {\n const items = this.items;\n const plan = planAtlas(items.map((it) => ({ w: it.canvas.width, h: it.canvas.height })), limit);\n const pages = plan.pages.map((p, i) => {\n const canvas = document.createElement('canvas');\n canvas.width = p.width;\n canvas.height = p.height;\n // kept on the CPU (`willReadFrequently`): the atlas is drawn once, then read many times — the sway rigs' trunks,\n // a death's evaporating pixels, a hit's white silhouette — and on a GPU canvas the first read pulled the whole\n // page back, 300 ms on entering a new map (profiled)\n return { canvas, ctx: canvas.getContext('2d', { willReadFrequently: true })!, key: i === 0 ? key : `${key}~${i}` };\n });\n items.forEach((it, i) => {\n const at = plan.places[i]!;\n pages[at.page]!.ctx.drawImage(it.canvas, at.x, at.y);\n });\n const first = textures.addCanvas(key, pages[0]!.canvas);\n const texOf = pages.map((p, i) => (i === 0 ? first : textures.addCanvas(p.key, p.canvas)));\n items.forEach((it, i) => {\n const at = plan.places[i]!;\n const page = texOf[at.page]!;\n // a frame on a later page is also registered on the first, so `atlas` + its name finds it\n const add = (name: string, x: number, y: number, w: number, h: number) => {\n page.add(name, 0, x, y, w, h);\n if (at.page > 0) first.adopt(name, page);\n };\n if (it.frameWidth && it.frameHeight) {\n // a sheet may wrap into rows (`makeSheet`'s `maxWidth`, a very large creature's); frames are read row by row\n const cols = Math.floor(it.canvas.width / it.frameWidth);\n const rows = Math.max(1, Math.floor(it.canvas.height / it.frameHeight));\n for (let f = 0; f < cols * rows; f++)\n add(`${it.name}#${f}`, at.x + (f % cols) * it.frameWidth, at.y + Math.floor(f / cols) * it.frameHeight, it.frameWidth, it.frameHeight);\n } else {\n add(it.name, at.x, at.y, it.canvas.width, it.canvas.height);\n }\n });\n const sizes = plan.pages.map((p) => `${p.width}x${p.height}`).join(' + ');\n console.info(`[atlas] ${key}: ${items.length} images packed into ${sizes}${plan.pages.length > 1 ? ` (pages of ${limit})` : ''}`);\n }\n}\n"],"names":["AtlasBuilder","name","canvas","frameWidth","frameHeight","textures","key","limit","ATLAS_PAGE_FULL","items","plan","planAtlas","it","pages","p","i","at","first","texOf","page","add","x","y","w","h","cols","rows","f","sizes"],"mappings":";AAkBO,MAAMA,EAAa;AAAA,EAChB,QAAqB,CAAA;AAAA,EAE7B,IAAIC,GAAcC,GAAiC;AACjD,SAAK,MAAM,KAAK,EAAE,MAAAD,GAAM,QAAAC,GAAQ;AAAA,EAClC;AAAA,EAEA,SAASD,GAAcC,GAA2BC,GAAoBC,GAA2B;AAC/F,SAAK,MAAM,KAAK,EAAE,MAAAH,GAAM,QAAAC,GAAQ,YAAAC,GAAY,aAAAC,GAAa;AAAA,EAC3D;AAAA,EAEA,MAAMC,GAA2BC,GAAaC,IAAQC,GAAuB;AAC3E,UAAMC,IAAQ,KAAK,OACbC,IAAOC,EAAUF,EAAM,IAAI,CAACG,OAAQ,EAAE,GAAGA,EAAG,OAAO,OAAO,GAAGA,EAAG,OAAO,OAAA,EAAS,GAAGL,CAAK,GACxFM,IAAQH,EAAK,MAAM,IAAI,CAACI,GAAGC,MAAM;AACrC,YAAMb,IAAS,SAAS,cAAc,QAAQ;AAC9C,aAAAA,EAAO,QAAQY,EAAE,OACjBZ,EAAO,SAASY,EAAE,QAIX,EAAE,QAAAZ,GAAQ,KAAKA,EAAO,WAAW,MAAM,EAAE,oBAAoB,GAAA,CAAM,GAAI,KAAKa,MAAM,IAAIT,IAAM,GAAGA,CAAG,IAAIS,CAAC,GAAA;AAAA,IAChH,CAAC;AACD,IAAAN,EAAM,QAAQ,CAACG,GAAIG,MAAM;AACvB,YAAMC,IAAKN,EAAK,OAAOK,CAAC;AACxB,MAAAF,EAAMG,EAAG,IAAI,EAAG,IAAI,UAAUJ,EAAG,QAAQI,EAAG,GAAGA,EAAG,CAAC;AAAA,IACrD,CAAC;AACD,UAAMC,IAAQZ,EAAS,UAAUC,GAAKO,EAAM,CAAC,EAAG,MAAM,GAChDK,IAAQL,EAAM,IAAI,CAACC,GAAGC,MAAOA,MAAM,IAAIE,IAAQZ,EAAS,UAAUS,EAAE,KAAKA,EAAE,MAAM,CAAE;AACzF,IAAAL,EAAM,QAAQ,CAACG,GAAIG,MAAM;AACvB,YAAMC,IAAKN,EAAK,OAAOK,CAAC,GAClBI,IAAOD,EAAMF,EAAG,IAAI,GAEpBI,IAAM,CAACnB,GAAcoB,GAAWC,GAAWC,GAAWC,MAAc;AACxE,QAAAL,EAAK,IAAIlB,GAAM,GAAGoB,GAAGC,GAAGC,GAAGC,CAAC,GACxBR,EAAG,OAAO,KAAGC,EAAM,MAAMhB,GAAMkB,CAAI;AAAA,MACzC;AACA,UAAIP,EAAG,cAAcA,EAAG,aAAa;AAEnC,cAAMa,IAAO,KAAK,MAAMb,EAAG,OAAO,QAAQA,EAAG,UAAU,GACjDc,IAAO,KAAK,IAAI,GAAG,KAAK,MAAMd,EAAG,OAAO,SAASA,EAAG,WAAW,CAAC;AACtE,iBAASe,IAAI,GAAGA,IAAIF,IAAOC,GAAMC;AAC/B,UAAAP,EAAI,GAAGR,EAAG,IAAI,IAAIe,CAAC,IAAIX,EAAG,IAAKW,IAAIF,IAAQb,EAAG,YAAYI,EAAG,IAAI,KAAK,MAAMW,IAAIF,CAAI,IAAIb,EAAG,aAAaA,EAAG,YAAYA,EAAG,WAAW;AAAA,MACzI;AACE,QAAAQ,EAAIR,EAAG,MAAMI,EAAG,GAAGA,EAAG,GAAGJ,EAAG,OAAO,OAAOA,EAAG,OAAO,MAAM;AAAA,IAE9D,CAAC;AACD,UAAMgB,IAAQlB,EAAK,MAAM,IAAI,CAACI,MAAM,GAAGA,EAAE,KAAK,IAAIA,EAAE,MAAM,EAAE,EAAE,KAAK,KAAK;AACxE,YAAQ,KAAK,WAAWR,CAAG,KAAKG,EAAM,MAAM,uBAAuBmB,CAAK,GAAGlB,EAAK,MAAM,SAAS,IAAI,cAAcH,CAAK,MAAM,EAAE,EAAE;AAAA,EAClI;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"atlasPlan.js","sources":["../../src/core/atlasPlan.ts"],"sourcesContent":["/*\n * Where every image goes in the world atlas, kept pure (no canvas) so the packing decision is unit-tested. The\n * builder (`atlas.ts`) paints what this plans.\n */\n\nexport const ATLAS_PAD = 2;\nconst MIN_ATLAS_WIDTH = 1024;\n/** The page side every desktop GPU takes, and the one the atlas was built to before pages existed. */\nexport const ATLAS_PAGE_FULL = 8192;\n/** The page side on a GPU that reports less than `ATLAS_PAGE_FULL` (older iPhones and iPads: 4096). */\nconst ATLAS_PAGE_SMALL = 4096;\n\n/**\n * How big an atlas page may be on this device: 8192 wherever the GPU takes it (every desktop — unchanged), else\n * split at 4096, or at the largest power of two the GPU does take if that is smaller still. Unknown: 8192, as before.\n */\nexport function atlasPageLimit(maxTextureSize: number | null): number {\n if (maxTextureSize === null || maxTextureSize >= ATLAS_PAGE_FULL) return ATLAS_PAGE_FULL;\n return Math.min(ATLAS_PAGE_SMALL, 2 ** Math.floor(Math.log2(Math.max(1, maxTextureSize))));\n}\n\nexport interface AtlasSize {\n w: number;\n h: number;\n}\n\nexport interface AtlasPage {\n width: number;\n height: number;\n}\n\nexport interface AtlasPlan {\n pages: AtlasPage[];\n /** One per input, in input order: the page and the top-left corner. */\n places: { page: number; x: number; y: number }[];\n}\n\n/**\n * Shelf-packs the images, tallest first, into pages no larger than `limit` on either side. The page width is the\n * power of two that fits the widest image and the square root of the total area (at least 1024), capped at `limit`;\n * a shelf that would run past `limit` starts a new page. With everything fitting under `limit` this is exactly the\n * single page the atlas always was. An image larger than `limit` itself is given a page of its own (the GPU will\n * refuse it, but nothing else is lost with it).\n */\nexport function planAtlas(sizes: readonly AtlasSize[], limit: number): AtlasPlan {\n const order = sizes.map((_, i) => i).sort((a, b) => sizes[b]!.h - sizes[a]!.h);\n const area = sizes.reduce((sum, s) => sum + (s.w + ATLAS_PAD) * (s.h + ATLAS_PAD), 0);\n const widest = Math.max(1, ...sizes.map((s) => s.w + ATLAS_PAD));\n const width = Math.min(limit, Math.max(MIN_ATLAS_WIDTH, 2 ** Math.ceil(Math.log2(Math.max(widest, Math.sqrt(area))))));\n const places: AtlasPlan['places'] = new Array(sizes.length);\n const pages: AtlasPage[] = [];\n let page = 0;\n let x = 0;\n let y = 0;\n let shelf = 0;\n let pageWidth = width;\n const close = () => pages.push({ width: pageWidth, height: y + shelf });\n for (const i of order) {\n const w = sizes[i]!.w + ATLAS_PAD;\n const h = sizes[i]!.h + ATLAS_PAD;\n if (x > 0 && x + w > pageWidth) {\n x = 0;\n y += shelf;\n shelf = 0;\n }\n if (y > 0 && y + h > limit) {\n close();\n page++;\n x = 0;\n y = 0;\n shelf = 0;\n pageWidth = width;\n }\n // an image wider than the page (only past `limit`) widens its own page rather than overlapping the edge\n if (w > pageWidth) pageWidth = w;\n places[i] = { page, x, y };\n x += w;\n shelf = Math.max(shelf, h);\n }\n if (sizes.length > 0) close();\n return { pages, places };\n}\n"],"names":["ATLAS_PAD","ATLAS_PAGE_FULL","atlasPageLimit","maxTextureSize","planAtlas","sizes","limit","order","_","i","b","area","sum","s","widest","width","places","pages","page","x","y","shelf","pageWidth","close","w","h"],"mappings":"AAKO,MAAMA,IAAY;AAGlB,MAAMC,IAAkB;AAQxB,SAASC,EAAeC,GAAuC;AACpE,SAAIA,MAAmB,QAAQA,KAAkB,OAAwB,OAClE,KAAK,IAAI,MAAkB,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,IAAI,GAAGA,CAAc,CAAC,CAAC,CAAC;AAC3F;AAyBO,SAASC,EAAUC,GAA6BC,GAA0B;AAC/E,QAAMC,IAAQF,EAAM,IAAI,CAACG,GAAGC,MAAMA,CAAC,EAAE,KAAK,CAAC,GAAGC,MAAML,EAAMK,CAAC,EAAG,IAAIL,EAAM,CAAC,EAAG,CAAC,GACvEM,IAAON,EAAM,OAAO,CAACO,GAAKC,MAAMD,KAAOC,EAAE,IAAI,MAAcA,EAAE,IAAI,IAAY,CAAC,GAC9EC,IAAS,KAAK,IAAI,GAAG,GAAGT,EAAM,IAAI,CAACQ,MAAMA,EAAE,IAAI,CAAS,CAAC,GACzDE,IAAQ,KAAK,IAAIT,GAAO,KAAK,IAAI,MAAiB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,IAAIQ,GAAQ,KAAK,KAAKH,CAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAC/GK,IAA8B,IAAI,MAAMX,EAAM,MAAM,GACpDY,IAAqB,CAAA;AAC3B,MAAIC,IAAO,GACPC,IAAI,GACJC,IAAI,GACJC,IAAQ,GACRC,IAAYP;AAChB,QAAMQ,IAAQ,MAAMN,EAAM,KAAK,EAAE,OAAOK,GAAW,QAAQF,IAAIC,GAAO;AACtE,aAAWZ,KAAKF,GAAO;AACrB,UAAMiB,IAAInB,EAAMI,CAAC,EAAG,IAAI,GAClBgB,IAAIpB,EAAMI,CAAC,EAAG,IAAI;AACxB,IAAIU,IAAI,KAAKA,IAAIK,IAAIF,MACnBH,IAAI,GACJC,KAAKC,GACLA,IAAQ,IAEND,IAAI,KAAKA,IAAIK,IAAInB,MACnBiB,EAAA,GACAL,KACAC,IAAI,GACJC,IAAI,GACJC,IAAQ,GACRC,IAAYP,IAGVS,IAAIF,MAAWA,IAAYE,IAC/BR,EAAOP,CAAC,IAAI,EAAE,MAAAS,GAAM,GAAAC,GAAG,GAAAC,EAAA,GACvBD,KAAKK,GACLH,IAAQ,KAAK,IAAIA,GAAOI,CAAC;AAAA,EAC3B;AACA,SAAIpB,EAAM,SAAS,KAAGkB,EAAA,GACf,EAAE,OAAAN,GAAO,QAAAD,EAAA;AAClB;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"eases.js","sources":["../../src/core/eases.ts"],"sourcesContent":["export type EaseFn = (t: number) => number;\n\nconst linear: EaseFn = (t) => t;\n\n/** Easing functions by the names tween configs use ('Cubic.Out', 'Back.Out', ...). */\nconst EASES: Record<string, EaseFn> = {\n Linear: linear,\n 'Quad.In': (t) => t * t,\n 'Quad.Out': (t) => t * (2 - t),\n 'Cubic.Out': (t) => 1 - Math.pow(1 - t, 3),\n 'Sine.In': (t) => 1 - Math.cos((t * Math.PI) / 2),\n 'Sine.InOut': (t) => -(Math.cos(Math.PI * t) - 1) / 2,\n 'Back.Out': (t) => {\n const s = 1.70158;\n const u = t - 1;\n return u * u * ((s + 1) * u + s) + 1;\n },\n};\n\nexport function easeByName(name: string | undefined): EaseFn {\n if (!name) return EASES['Quad.Out']!; // Phaser's default tween ease is Power1 = Quad.Out\n const fn = EASES[name];\n if (!fn) console.warn(`[tween] unknown ease ${name}`);\n return fn ?? linear;\n}\n"],"names":["linear","t","EASES","easeByName","name","fn"],"mappings":"AAEA,MAAMA,IAAiB,CAACC,MAAMA,GAGxBC,IAAgC;AAAA,EACpC,QAAQF;AAAA,EACR,WAAW,CAACC,MAAMA,IAAIA;AAAA,EACtB,YAAY,CAACA,MAAMA,KAAK,IAAIA;AAAA,EAC5B,aAAa,CAACA,MAAM,IAAI,KAAK,IAAI,IAAIA,GAAG,CAAC;AAAA,EACzC,WAAW,CAACA,MAAM,IAAI,KAAK,IAAKA,IAAI,KAAK,KAAM,CAAC;AAAA,EAChD,cAAc,CAACA,MAAM,EAAE,KAAK,IAAI,KAAK,KAAKA,CAAC,IAAI,KAAK;AAAA,EACpD,YAAY,CAACA,MAAM;AAEjB,UAAM,IAAIA,IAAI;AACd,WAAO,IAAI,KAAM,UAAS,IAAI,WAAK;AAAA,EACrC;AACF;AAEO,SAASE,EAAWC,GAAkC;AAC3D,MAAI,CAACA,EAAM,QAAOF,EAAM,UAAU;AAClC,QAAMG,IAAKH,EAAME,CAAI;AACrB,SAAKC,KAAI,QAAQ,KAAK,wBAAwBD,CAAI,EAAE,GAC7CC,KAAML;AACf;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"gameObject.js","sources":["../../src/core/gameObject.ts"],"sourcesContent":["import { ColorMatrixFilter, type BLEND_MODES, type Container } from 'pixi.js';\nimport type { Stage } from './Stage';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Ctor<T> = new (...args: any[]) => T;\n\n/**\n * One solid-fill filter list per colour, shared by every object filled with it (a hit flash, a silhouette): a filter\n * made and destroyed on every hit of every body was garbage and shader state a blow. Kept for good —\n * a handful of colours are ever used.\n */\nconst FILLS = new Map<number, ColorMatrixFilter[]>();\n\nfunction fillFilters(color: number): ColorMatrixFilter[] {\n let list = FILLS.get(color);\n if (list) return list;\n const f = new ColorMatrixFilter();\n f.antialias = 'off';\n const r = ((color >> 16) & 0xff) / 255;\n const g = ((color >> 8) & 0xff) / 255;\n const b = (color & 0xff) / 255;\n f.matrix = [0, 0, 0, 0, r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0, 0, 0, 1, 0];\n FILLS.set(color, (list = [f]));\n return list;\n}\n\n/** What `GameObject` adds to a Pixi display object. */\nexport interface GameObjectMembers {\n scene: Stage;\n /** False once destroyed. */\n readonly active: boolean;\n /** The draw order in the world layer (`zIndex`): bigger is in front. */\n depth: number;\n /** Scale without the flip: stays positive on a mirrored sprite. */\n scaleX: number;\n scaleY: number;\n flipX: boolean;\n flipY: boolean;\n setScale(x: number, y?: number): this;\n setFlipX(v: boolean): this;\n setFlipY(v: boolean): this;\n setFlip(x: boolean, y: boolean): this;\n setDepth(d: number): this;\n setAlpha(a?: number): this;\n setVisible(v: boolean): this;\n setPosition(x?: number, y?: number): this;\n setRotation(r?: number): this;\n setAngle(a?: number): this;\n setBlendMode(mode: BLEND_MODES): this;\n setTint(color?: number): this;\n /** Paints every opaque pixel in one solid colour (hit flash, silhouettes). */\n setTintFill(color: number): this;\n clearTint(): this;\n /** 0 pins the object to the screen instead of the world. */\n setScrollFactor(f: number): this;\n destroy(): void;\n}\n\n/**\n * The chainable setters the world code was written against (setDepth, setAlpha, setTintFill...), added onto a Pixi\n * display object. Scale is kept apart from flipping, so `scaleX` stays positive on a mirrored sprite as it did\n * before, and a tween on scale cannot undo a flip.\n */\nexport function GameObject<TBase extends Ctor<Container>>(Base: TBase): TBase & Ctor<GameObjectMembers> {\n return class extends Base {\n scene!: Stage;\n private sx = 1;\n private sy = 1;\n private fx = false;\n private fy = false;\n /** The colour this object is filled with (`setTintFill`), or null. */\n private solidFillColor: number | null = null;\n\n /** False once destroyed. */\n get active(): boolean {\n return !this.destroyed;\n }\n\n get depth(): number {\n return this.zIndex;\n }\n\n set depth(d: number) {\n this.zIndex = d;\n }\n\n get scaleX(): number {\n return this.sx;\n }\n\n set scaleX(v: number) {\n this.sx = v;\n this.applyScale();\n }\n\n get scaleY(): number {\n return this.sy;\n }\n\n set scaleY(v: number) {\n this.sy = v;\n this.applyScale();\n }\n\n get flipX(): boolean {\n return this.fx;\n }\n\n set flipX(v: boolean) {\n this.fx = v;\n this.applyScale();\n }\n\n get flipY(): boolean {\n return this.fy;\n }\n\n set flipY(v: boolean) {\n this.fy = v;\n this.applyScale();\n }\n\n private applyScale(): void {\n if (!this.destroyed) this.scale.set(this.sx * (this.fx ? -1 : 1), this.sy * (this.fy ? -1 : 1));\n }\n\n setScale(x: number, y = x): this {\n this.sx = x;\n this.sy = y;\n this.applyScale();\n return this;\n }\n\n setFlipX(v: boolean): this {\n this.flipX = v;\n return this;\n }\n\n setFlipY(v: boolean): this {\n this.flipY = v;\n return this;\n }\n\n setFlip(x: boolean, y: boolean): this {\n this.fx = x;\n this.fy = y;\n this.applyScale();\n return this;\n }\n\n setDepth(d: number): this {\n this.zIndex = d;\n return this;\n }\n\n setAlpha(a = 1): this {\n this.alpha = a;\n return this;\n }\n\n setVisible(v: boolean): this {\n this.visible = v;\n return this;\n }\n\n setPosition(x = 0, y = x): this {\n this.position.set(x, y);\n return this;\n }\n\n setRotation(r = 0): this {\n this.rotation = r;\n return this;\n }\n\n setAngle(a = 0): this {\n this.angle = a;\n return this;\n }\n\n setBlendMode(mode: BLEND_MODES): this {\n this.blendMode = mode;\n return this;\n }\n\n setTint(color = 0xffffff): this {\n this.clearFill();\n this.tint = color;\n return this;\n }\n\n /** Paints every opaque pixel in one solid colour (hit flash, silhouettes). */\n setTintFill(color: number): this {\n this.tint = 0xffffff;\n if (this.solidFillColor === color) return this;\n this.solidFillColor = color;\n this.filters = fillFilters(color);\n return this;\n }\n\n clearTint(): this {\n this.clearFill();\n this.tint = 0xffffff;\n return this;\n }\n\n private clearFill(): void {\n if (this.solidFillColor === null) return;\n this.filters = null;\n this.solidFillColor = null;\n }\n\n /** 0 pins the object to the screen instead of the world (full-screen flashes). */\n setScrollFactor(f: number): this {\n if (f === 0) this.scene.screen.addChild(this);\n return this;\n }\n\n override destroy(): void {\n if (this.destroyed) return;\n // the fill filters are shared (`FILLS`): let go of them, never destroy them\n if (this.solidFillColor !== null) this.filters = null;\n this.solidFillColor = null;\n // `context`: a Pixi Graphics destroyed with options keeps its GraphicsContext — and that context's GPU buffers —\n // until Pixi's own GC sweeps it a minute or more later. Every effect layer is a Graphics, so a crowded fight left\n // hundreds a second on the GPU, and a phone that ran out dropped the WebGL context (the world rebuilt, seconds\n // frozen, the socket timed out).\n super.destroy({ children: true, context: true });\n }\n };\n}\n"],"names":["FILLS","fillFilters","color","list","f","ColorMatrixFilter","r","g","b","GameObject","Base","d","v","x","y","a","mode"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pixiPatches.js","sources":["../../src/core/pixiPatches.ts"],"sourcesContent":["import { BatchTextureArray } from 'pixi.js';\n\nlet patched = false;\n\n/**\n * Pixi 8's `BatchTextureArray.clear` sets `ids[source.uid] = null` instead of deleting the key, and its batches are\n * pooled, so every pooled batch keeps one key for every texture source ever drawn through it — about 70% of what a\n * fight's heap grew by in an hour (Pixi 8.21.0). The replacement deletes the key, which reads the\n * same to Pixi (`checkAndUpdateTexture` treats null and a missing key alike). Applied once, and only while `clear`\n * still has the shape it was written against, so a Pixi that fixed or changed it is left alone.\n */\nexport function patchPixi(): boolean {\n if (patched) return true;\n const proto = BatchTextureArray.prototype as unknown as { clear: () => void };\n const probe = new BatchTextureArray() as unknown as { ids: unknown; textures: unknown; count: unknown };\n const shaped = typeof proto.clear === 'function' && typeof probe.ids === 'object' && Array.isArray(probe.textures) && typeof probe.count === 'number';\n if (!shaped || !/ids\\[[^\\]]+\\]\\s*=\\s*null/.test(String(proto.clear))) return false;\n proto.clear = function clear(this: { ids: Record<number, number | null>; textures: ({ uid: number } | null)[]; count: number }): void {\n const ids = this.ids;\n const textures = this.textures;\n for (let i = 0; i < this.count; i++) {\n const t = textures[i];\n textures[i] = null;\n if (t) delete ids[t.uid];\n }\n this.count = 0;\n };\n patched = true;\n return true;\n}\n"],"names":["patched","patchPixi","proto","BatchTextureArray","probe","ids","textures","i","t"],"mappings":";AAEA,IAAIA,IAAU;AASP,SAASC,IAAqB;AACnC,MAAID,EAAS,QAAO;AACpB,QAAME,IAAQC,EAAkB,WAC1BC,IAAQ,IAAID,EAAA;AAElB,SAAI,EADW,OAAOD,EAAM,SAAU,cAAc,OAAOE,EAAM,OAAQ,YAAY,MAAM,QAAQA,EAAM,QAAQ,KAAK,OAAOA,EAAM,SAAU,aAC9H,CAAC,2BAA2B,KAAK,OAAOF,EAAM,KAAK,CAAC,IAAU,MAC7EA,EAAM,QAAQ,WAAwH;AACpI,UAAMG,IAAM,KAAK,KACXC,IAAW,KAAK;AACtB,aAASC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,YAAMC,IAAIF,EAASC,CAAC;AACpB,MAAAD,EAASC,CAAC,IAAI,MACVC,KAAG,OAAOH,EAAIG,EAAE,GAAG;AAAA,IACzB;AACA,SAAK,QAAQ;AAAA,EACf,GACAR,IAAU,IACH;AACT;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"timelinePlace.js","sources":["../../src/core/timelinePlace.ts"],"sourcesContent":["/**\n * Where a child goes on a timeline: a time in ms from its start, or a string —\n * `'<'` the start of the child added last, `'>'` its end, `'+=200'` / `'-=100'` 200 ms after / 100 ms before the\n * default place (the timeline's end, or a `parallel` group's start), `'intro'` a label, and any of `'<'`, `'>'` or a\n * label followed by `+=n` / `-=n`. A string of digits is a time.\n */\nexport type TimelinePosition = number | string;\n\n/** What a position is measured against; `labels` is written to when a position names a label not yet set. */\nexport interface PlaceContext {\n /** Where a child goes when no position is given: the timeline's end, or the open `parallel` group's start. */\n fallback: number;\n /** The end of the timeline as built so far: where a label first named in a position is set. */\n end: number;\n lastStart: number;\n lastEnd: number;\n labels: Map<string, number>;\n}\n\nconst SHAPE = /^(.*?)(?:([+-])=(\\d+(?:\\.\\d+)?))?$/;\nconst DIGITS = /^\\d+(?:\\.\\d+)?$/;\n\n/**\n * The start time (ms, never below 0) `at` names. A label not set yet is set at the timeline's end, the way GSAP does,\n * so `to(a, …, { at: 'hit' })` both places the child and names the spot for the children after it.\n */\nexport function placeAt(at: TimelinePosition | undefined, ctx: PlaceContext): number {\n if (at === undefined) return ctx.fallback;\n if (typeof at === 'number') return Math.max(0, at);\n const text = at.trim();\n if (DIGITS.test(text)) return Number(text);\n const m = SHAPE.exec(text);\n // the pattern matches any string (both parts may be empty), so this is only for the type checker\n if (!m) return ctx.fallback;\n const anchor = m[1] ?? '';\n let base: number;\n if (anchor === '') base = ctx.fallback;\n else if (anchor === '<') base = ctx.lastStart;\n else if (anchor === '>') base = ctx.lastEnd;\n else {\n const label = ctx.labels.get(anchor);\n if (label === undefined) {\n ctx.labels.set(anchor, ctx.end);\n base = ctx.end;\n } else base = label;\n }\n const amount = m[3] === undefined ? 0 : Number(m[3]) * (m[2] === '-' ? -1 : 1);\n return Math.max(0, base + amount);\n}\n"],"names":["SHAPE","DIGITS","placeAt","at","ctx","text","m","anchor","base","label","amount"],"mappings":"AAmBA,MAAMA,IAAQ,sCACRC,IAAS;AAMR,SAASC,EAAQC,GAAkCC,GAA2B;AACnF,MAAID,MAAO,OAAW,QAAOC,EAAI;AACjC,MAAI,OAAOD,KAAO,iBAAiB,KAAK,IAAI,GAAGA,CAAE;AACjD,QAAME,IAAOF,EAAG,KAAA;AAChB,MAAIF,EAAO,KAAKI,CAAI,EAAG,QAAO,OAAOA,CAAI;AACzC,QAAMC,IAAIN,EAAM,KAAKK,CAAI;AAEzB,MAAI,CAACC,EAAG,QAAOF,EAAI;AACnB,QAAMG,IAASD,EAAE,CAAC,KAAK;AACvB,MAAIE;AACJ,MAAID,MAAW,GAAI,CAAAC,IAAOJ,EAAI;AAAA,WACrBG,MAAW,IAAK,CAAAC,IAAOJ,EAAI;AAAA,WAC3BG,MAAW,IAAK,CAAAC,IAAOJ,EAAI;AAAA,OAC/B;AACH,UAAMK,IAAQL,EAAI,OAAO,IAAIG,CAAM;AACnC,IAAIE,MAAU,UACZL,EAAI,OAAO,IAAIG,GAAQH,EAAI,GAAG,GAC9BI,IAAOJ,EAAI,OACNI,IAAOC;AAAA,EAChB;AACA,QAAMC,IAASJ,EAAE,CAAC,MAAM,SAAY,IAAI,OAAOA,EAAE,CAAC,CAAC,KAAKA,EAAE,CAAC,MAAM,MAAM,KAAK;AAC5E,SAAO,KAAK,IAAI,GAAGE,IAAOE,CAAM;AAClC;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tweenProps.js","sources":["../../src/core/tweenProps.ts"],"sourcesContent":["import type { TweenTarget } from './Tweener';\n\n/**\n * How a tween reads and writes one property, shared by `Tweener` and `Timeline` so both treat `scale` the same way:\n * one number written to `scaleX` and `scaleY`, the shape every display object here exposes.\n */\nexport function readProp(target: TweenTarget, prop: string): number {\n const v = prop === 'scale' ? target['scaleX'] : target[prop];\n return typeof v === 'number' ? v : 0;\n}\n\nexport function writeProp(target: TweenTarget, prop: string, value: number): void {\n if (prop === 'scale') {\n target['scaleX'] = value;\n target['scaleY'] = value;\n } else target[prop] = value;\n}\n"],"names":["readProp","target","prop","v","writeProp","value"],"mappings":"AAMO,SAASA,EAASC,GAAqBC,GAAsB;AAClE,QAAMC,IAAID,MAAS,UAAUD,EAAO,SAAYA,EAAOC,CAAI;AAC3D,SAAO,OAAOC,KAAM,WAAWA,IAAI;AACrC;AAEO,SAASC,EAAUH,GAAqBC,GAAcG,GAAqB;AAChF,EAAIH,MAAS,WACXD,EAAO,SAAYI,GACnBJ,EAAO,SAAYI,KACdJ,EAAOC,CAAI,IAAIG;AACxB;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"DebugOverlay.js","sources":["../../src/debug/DebugOverlay.ts"],"sourcesContent":["import { Container, Graphics, Sprite, Text, Texture } from 'pixi.js';\nimport type { Renderer } from 'pixi.js';\nimport type { ComponentType, World } from '../ecs/World';\nimport type { Engine } from '../engine/Engine';\nimport { DrawCallCounter } from './DrawCallCounter';\nimport { ErrorLog } from './ErrorLog';\nimport { FrameStats } from './FrameStats';\nimport { inspectWorld } from './inspectWorld';\n\n/** Draws one box in world coordinates (a hurtbox, a wall, a trigger), in `color` (default green). */\nexport type BoxDrawer = (x: number, y: number, width: number, height: number, color?: number) => void;\n\n/** Draws one line in world coordinates (a turned collider's edge, a ray), in `color` (default green). */\nexport type LineDrawer = (x0: number, y0: number, x1: number, y1: number, color?: number) => void;\n\nexport interface DebugOverlayOptions {\n /**\n * `KeyboardEvent.code` that shows and hides it (default 'Backquote'). The key's own browser action is prevented\n * (F3 would open the find bar, F12 the developer tools).\n */\n key?: string;\n /** The world to count and inspect (default the current scene's). */\n world?: () => World | undefined;\n /** Component types the inspector lists; none, and it lists every entity with all it holds. */\n components?: readonly ComponentType<unknown>[];\n /** Entities listed at most (default 12). */\n inspectLimit?: number;\n /** Called every shown frame to draw collision boxes (and lines: turned shapes, rays). */\n colliders?: (draw: BoxDrawer, line: LineDrawer) => void;\n /** Errors kept (default 8). */\n errors?: number;\n /** Where the key is listened for (default `window`). */\n target?: Window | HTMLElement;\n}\n\nconst GRAPH_W = 180;\nconst GRAPH_H = 40;\n/** Text is rebuilt this often: a Pixi `Text` re-rasterises on every change, which would cost more than it measures. */\nconst TEXT_MS = 250;\n\n/**\n * A developer overlay toggled by a key: FPS with a frame-time graph, draw calls and textures, the entity count, a\n * list of entities and their components, collision boxes, and the last errors the frame guard caught.\n *\n * Hidden, it costs nothing per frame: nothing is drawn or listened to but the key and the engine's `error` event\n * (so faults from before it was opened are still listed). Shown, it rides the engine's `frame` event.\n */\nexport class DebugOverlay {\n readonly errors: ErrorLog;\n private readonly stats = new FrameStats(GRAPH_W / 2);\n private readonly panel = new Container();\n private readonly back = new Sprite(Texture.WHITE);\n private readonly graph = new Graphics();\n private readonly text = new Text({ text: '', style: { fontFamily: 'monospace', fontSize: 11, fill: 0xe8e8e8, lineHeight: 13 } });\n private readonly boxes = new Graphics();\n private readonly boxLayer = new Container();\n private readonly draws: DrawCallCounter;\n private readonly cleanup: (() => void)[] = [];\n private offFrame: (() => void) | null = null;\n private lastText = -Infinity;\n private drawCalls = 0;\n private shown = false;\n private readonly drawBox: BoxDrawer = (x, y, w, h, color = 0x40ff60) => {\n this.boxes.rect(x, y, w, h).stroke({ width: 1, color, alpha: 0.9, pixelLine: true });\n };\n\n private readonly drawLine: LineDrawer = (x0, y0, x1, y1, color = 0x40ff60) => {\n this.boxes.moveTo(x0, y0).lineTo(x1, y1).stroke({ width: 1, color, alpha: 0.9, pixelLine: true });\n };\n\n constructor(\n private readonly engine: Engine,\n private readonly options: DebugOverlayOptions = {},\n ) {\n this.errors = new ErrorLog(options.errors ?? 8);\n this.draws = new DrawCallCounter(engine.app.renderer);\n this.back.tint = 0x000000;\n this.back.alpha = 0.7;\n this.graph.position.set(6, 6);\n this.text.position.set(6, GRAPH_H + 12);\n this.panel.addChild(this.back, this.graph, this.text);\n this.boxLayer.addChild(this.boxes);\n this.cleanup.push(engine.events.on('error', ({ where, error }) => this.errors.add(where, error)));\n const target = options.target ?? window;\n const key = options.key ?? 'Backquote';\n const onKey = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.code !== key) return;\n // the overlay's key, not the browser's: F3 opened find-in-page over the game\n k.preventDefault();\n if (!k.repeat) this.toggle();\n };\n target.addEventListener('keydown', onKey);\n this.cleanup.push(() => target.removeEventListener('keydown', onKey));\n }\n\n get visible(): boolean {\n return this.shown;\n }\n\n toggle(): void {\n if (this.shown) this.hide();\n else this.show();\n }\n\n show(): void {\n if (this.shown) return;\n this.shown = true;\n const stage = this.engine.stage;\n stage.screen.addChild(this.boxLayer, this.panel);\n stage.cameras.main.follow(this.boxLayer);\n this.draws.install();\n this.draws.take();\n this.stats.clear();\n this.lastText = -Infinity;\n this.offFrame = this.engine.events.on('frame', ({ now, dt }) => this.frame(now, dt));\n }\n\n hide(): void {\n if (!this.shown) return;\n this.shown = false;\n this.offFrame?.();\n this.offFrame = null;\n this.draws.uninstall();\n this.engine.stage.cameras.main.unfollow(this.boxLayer);\n this.panel.removeFromParent();\n this.boxLayer.removeFromParent();\n this.boxes.clear();\n }\n\n destroy(): void {\n this.hide();\n for (const fn of this.cleanup.splice(0)) fn();\n this.panel.destroy({ children: true });\n this.boxLayer.destroy({ children: true });\n }\n\n private frame(now: number, dt: number): void {\n this.stats.push(dt * 1000);\n // counted while the previous frame rendered: the 'frame' event comes before this frame's render\n this.drawCalls = this.draws.take();\n this.paintGraph();\n this.boxes.clear();\n if (this.options.colliders) this.options.colliders(this.drawBox, this.drawLine);\n if (now - this.lastText >= TEXT_MS) {\n this.lastText = now;\n this.paintText();\n }\n }\n\n private paintGraph(): void {\n const g = this.graph.clear();\n g.rect(0, 0, GRAPH_W, GRAPH_H).fill({ color: 0x202020 });\n // 16.7 ms (60 fps) and 33.3 ms (30 fps) guides; the graph tops out at 50 ms\n for (const ms of [1000 / 60, 1000 / 30]) g.rect(0, GRAPH_H - (ms / 50) * GRAPH_H, GRAPH_W, 1).fill({ color: 0x505050 });\n const n = this.stats.count;\n for (let i = 0; i < n; i++) {\n const ms = this.stats.at(i);\n const h = Math.max(1, Math.min(GRAPH_H, Math.round((ms / 50) * GRAPH_H)));\n const color = ms > 1000 / 30 ? 0xff5050 : ms > 1000 / 55 ? 0xffc040 : 0x60e070;\n g.rect(i * 2, GRAPH_H - h, 2, h).fill({ color });\n }\n }\n\n private paintText(): void {\n const renderer = this.engine.app.renderer;\n const world = this.options.world ? this.options.world() : this.engine.scene?.world;\n const lines: string[] = [\n `${this.stats.fps.toFixed(0)} fps avg ${this.stats.average.toFixed(1)} ms max ${this.stats.max.toFixed(1)} ms`,\n `draw calls ${this.draws.supported ? this.drawCalls : 'n/a'} textures ${textureCount(renderer)}`,\n `entities ${world ? world.size : '-'} scene ${this.engine.scene?.constructor.name ?? '-'}`,\n ];\n if (world) {\n for (const row of inspectWorld(world, this.options.components, this.options.inspectLimit ?? 12)) lines.push(`#${row.entity} ${row.components.join(' | ')}`);\n }\n if (this.errors.entries.length) {\n lines.push('errors:');\n for (const e of this.errors.entries) lines.push(`${e.count > 1 ? `${e.count}x ` : ''}${e.where}: ${e.message}`.slice(0, 120));\n }\n this.text.text = lines.join('\\n');\n this.back.width = Math.max(GRAPH_W + 12, Math.ceil(this.text.width) + 12);\n this.back.height = Math.ceil(this.text.y + this.text.height + 6);\n }\n}\n\n/** Textures the renderer holds on the GPU; the WebGL and WebGPU systems expose it, the canvas fallback does not. */\nfunction textureCount(renderer: Renderer): number | string {\n const managed = (renderer.texture as { managedTextures?: readonly unknown[] }).managedTextures;\n return managed ? managed.length : 'n/a';\n}\n"],"names":["GRAPH_W","GRAPH_H","TEXT_MS","DebugOverlay","engine","options","ErrorLog","DrawCallCounter","where","error","target","key","onKey","e","k","FrameStats","Container","Sprite","Texture","Graphics","Text","x","y","w","h","color","x0","y0","x1","y1","stage","now","dt","fn","g","ms","n","i","renderer","world","lines","textureCount","row","inspectWorld","managed"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"DrawCallCounter.js","sources":["../../src/debug/DrawCallCounter.ts"],"sourcesContent":["import type { Renderer } from 'pixi.js';\n\nconst DRAW_CALLS = ['drawElements', 'drawArrays', 'drawElementsInstanced', 'drawArraysInstanced'] as const;\n\n/**\n * Counts WebGL draw calls by wrapping the context's draw functions while it is installed, since Pixi 8 keeps no\n * count of its own. Installed only while the debug overlay is open, so a closed overlay costs nothing; on WebGPU\n * (no `gl`) it counts nothing and says so.\n */\nexport class DrawCallCounter {\n /** Draw calls since the last `take`. */\n private calls = 0;\n private restore: (() => void) | null = null;\n\n constructor(private readonly renderer: Renderer) {}\n\n /** Whether this renderer can be counted (WebGL). */\n get supported(): boolean {\n return glOf(this.renderer) !== null;\n }\n\n install(): void {\n const gl = glOf(this.renderer);\n if (!gl || this.restore) return;\n const target = gl as unknown as Record<string, unknown>;\n const saved: [string, unknown][] = [];\n for (const name of DRAW_CALLS) {\n const fn = target[name];\n if (typeof fn !== 'function') continue;\n saved.push([name, fn]);\n target[name] = (...args: unknown[]) => {\n this.calls++;\n return (fn as (...a: unknown[]) => unknown).apply(gl, args);\n };\n }\n this.restore = () => {\n for (const [name, fn] of saved) target[name] = fn;\n };\n }\n\n uninstall(): void {\n this.restore?.();\n this.restore = null;\n }\n\n /** Draw calls since the last call, and starts counting again. */\n take(): number {\n const n = this.calls;\n this.calls = 0;\n return n;\n }\n}\n\nfunction glOf(renderer: Renderer): WebGL2RenderingContext | WebGLRenderingContext | null {\n const gl = (renderer as unknown as { gl?: WebGL2RenderingContext | WebGLRenderingContext }).gl;\n return gl ?? null;\n}\n"],"names":["DRAW_CALLS","DrawCallCounter","renderer","glOf","gl","target","saved","name","fn","args","n"],"mappings":"AAEA,MAAMA,IAAa,CAAC,gBAAgB,cAAc,yBAAyB,qBAAqB;AAOzF,MAAMC,EAAgB;AAAA,EAK3B,YAA6BC,GAAoB;AAApB,SAAA,WAAAA;AAAA,EAAqB;AAAA,EAArB;AAAA;AAAA,EAHrB,QAAQ;AAAA,EACR,UAA+B;AAAA;AAAA,EAKvC,IAAI,YAAqB;AACvB,WAAOC,EAAK,KAAK,QAAQ,MAAM;AAAA,EACjC;AAAA,EAEA,UAAgB;AACd,UAAMC,IAAKD,EAAK,KAAK,QAAQ;AAC7B,QAAI,CAACC,KAAM,KAAK,QAAS;AACzB,UAAMC,IAASD,GACTE,IAA6B,CAAA;AACnC,eAAWC,KAAQP,GAAY;AAC7B,YAAMQ,IAAKH,EAAOE,CAAI;AACtB,MAAI,OAAOC,KAAO,eAClBF,EAAM,KAAK,CAACC,GAAMC,CAAE,CAAC,GACrBH,EAAOE,CAAI,IAAI,IAAIE,OACjB,KAAK,SACGD,EAAoC,MAAMJ,GAAIK,CAAI;AAAA,IAE9D;AACA,SAAK,UAAU,MAAM;AACnB,iBAAW,CAACF,GAAMC,CAAE,KAAKF,EAAO,CAAAD,EAAOE,CAAI,IAAIC;AAAA,IACjD;AAAA,EACF;AAAA,EAEA,YAAkB;AAChB,SAAK,UAAA,GACL,KAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,OAAe;AACb,UAAME,IAAI,KAAK;AACf,gBAAK,QAAQ,GACNA;AAAA,EACT;AACF;AAEA,SAASP,EAAKD,GAA2E;AAEvF,SADYA,EAAgF,MAC/E;AACf;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ErrorLog.js","sources":["../../src/debug/ErrorLog.ts"],"sourcesContent":["/** One fault the frame guard (or a system) caught. */\nexport interface LoggedError {\n where: string;\n message: string;\n /** How many times in a row the same fault came in. */\n count: number;\n at: number;\n}\n\n/**\n * The last `limit` distinct faults, newest last. A fault repeating every frame is one entry with a count, not\n * sixty lines a second pushing everything else out.\n */\nexport class ErrorLog {\n readonly entries: LoggedError[] = [];\n\n constructor(readonly limit = 10) {}\n\n add(where: string, error: unknown, at = Date.now()): void {\n const message = error instanceof Error ? error.message : String(error);\n const last = this.entries[this.entries.length - 1];\n if (last && last.where === where && last.message === message) {\n last.count++;\n last.at = at;\n return;\n }\n this.entries.push({ where, message, count: 1, at });\n if (this.entries.length > this.limit) this.entries.shift();\n }\n\n clear(): void {\n this.entries.length = 0;\n }\n}\n"],"names":["ErrorLog","limit","where","error","at","message","last"],"mappings":"AAaO,MAAMA,EAAS;AAAA,EAGpB,YAAqBC,IAAQ,IAAI;AAAZ,SAAA,QAAAA;AAAA,EAAa;AAAA,EAAb;AAAA,EAFZ,UAAyB,CAAA;AAAA,EAIlC,IAAIC,GAAeC,GAAgBC,IAAK,KAAK,OAAa;AACxD,UAAMC,IAAUF,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK,GAC/DG,IAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC;AACjD,QAAIA,KAAQA,EAAK,UAAUJ,KAASI,EAAK,YAAYD,GAAS;AAC5D,MAAAC,EAAK,SACLA,EAAK,KAAKF;AACV;AAAA,IACF;AACA,SAAK,QAAQ,KAAK,EAAE,OAAAF,GAAO,SAAAG,GAAS,OAAO,GAAG,IAAAD,GAAI,GAC9C,KAAK,QAAQ,SAAS,KAAK,SAAO,KAAK,QAAQ,MAAA;AAAA,EACrD;AAAA,EAEA,QAAc;AACZ,SAAK,QAAQ,SAAS;AAAA,EACxB;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"FrameStats.js","sources":["../../src/debug/FrameStats.ts"],"sourcesContent":["/**\n * The last `size` frame times in a ring of numbers: pure, allocation-free, read by the debug overlay's graph and\n * its FPS line. Averages are over the ring, so one long frame shows in `max` without wrecking the FPS shown.\n */\nexport class FrameStats {\n readonly times: Float32Array;\n private head = 0;\n private filled = 0;\n\n constructor(readonly size = 120) {\n this.times = new Float32Array(size);\n }\n\n /** Records one frame's length in ms. */\n push(ms: number): void {\n this.times[this.head] = ms;\n this.head = (this.head + 1) % this.size;\n if (this.filled < this.size) this.filled++;\n }\n\n get count(): number {\n return this.filled;\n }\n\n /** The `i`-th frame, oldest first. */\n at(i: number): number {\n return this.times[(this.head - this.filled + i + this.size) % this.size]!;\n }\n\n get average(): number {\n if (this.filled === 0) return 0;\n let sum = 0;\n for (let i = 0; i < this.filled; i++) sum += this.times[i]!;\n return sum / this.filled;\n }\n\n get max(): number {\n let m = 0;\n for (let i = 0; i < this.filled; i++) if (this.times[i]! > m) m = this.times[i]!;\n return m;\n }\n\n get fps(): number {\n const avg = this.average;\n return avg > 0 ? 1000 / avg : 0;\n }\n\n clear(): void {\n this.head = 0;\n this.filled = 0;\n }\n}\n"],"names":["FrameStats","size","ms","i","sum","m","avg"],"mappings":"AAIO,MAAMA,EAAW;AAAA,EAKtB,YAAqBC,IAAO,KAAK;AAAZ,SAAA,OAAAA,GACnB,KAAK,QAAQ,IAAI,aAAaA,CAAI;AAAA,EACpC;AAAA,EAFqB;AAAA,EAJZ;AAAA,EACD,OAAO;AAAA,EACP,SAAS;AAAA;AAAA,EAOjB,KAAKC,GAAkB;AACrB,SAAK,MAAM,KAAK,IAAI,IAAIA,GACxB,KAAK,QAAQ,KAAK,OAAO,KAAK,KAAK,MAC/B,KAAK,SAAS,KAAK,QAAM,KAAK;AAAA,EACpC;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,GAAGC,GAAmB;AACpB,WAAO,KAAK,OAAO,KAAK,OAAO,KAAK,SAASA,IAAI,KAAK,QAAQ,KAAK,IAAI;AAAA,EACzE;AAAA,EAEA,IAAI,UAAkB;AACpB,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAIC,IAAM;AACV,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,CAAAA,KAAO,KAAK,MAAM,CAAC;AACzD,WAAOA,IAAM,KAAK;AAAA,EACpB;AAAA,EAEA,IAAI,MAAc;AAChB,QAAIC,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,CAAI,KAAK,MAAM,CAAC,IAAKA,MAAGA,IAAI,KAAK,MAAM,CAAC;AAC9E,WAAOA;AAAA,EACT;AAAA,EAEA,IAAI,MAAc;AAChB,UAAMC,IAAM,KAAK;AACjB,WAAOA,IAAM,IAAI,MAAOA,IAAM;AAAA,EAChC;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,GACZ,KAAK,SAAS;AAAA,EAChB;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"inspectWorld.js","sources":["../../src/debug/inspectWorld.ts"],"sourcesContent":["import type { ComponentType, Entity, World } from '../ecs/World';\n\n/** One entity as the inspector lists it. */\nexport interface EntityRow {\n entity: Entity;\n /** `Name` or `Name {…}` per component it holds, of those asked about. */\n components: string[];\n}\n\n/**\n * Lists up to `limit` entities with a short preview of each component's value: those holding any of `components`\n * when the game names some (only those shown), else every live entity with all it holds (`World.entities`,\n * `componentsOf`). For the debug overlay: it allocates, and runs a few times a second only while the overlay is open.\n */\nexport function inspectWorld(world: World, components?: readonly ComponentType<unknown>[], limit = 20, previewLength = 40): EntityRow[] {\n if (!components?.length) {\n return world\n .entities()\n .slice(0, limit)\n .map((e) => ({ entity: e, components: world.componentsOf(e).map((type) => describe(type, world.get(e, type), previewLength)) }));\n }\n const rows = new Map<Entity, EntityRow>();\n for (const type of components) {\n world.each(type, (e, value) => {\n let row = rows.get(e);\n if (!row) {\n if (rows.size >= limit) return;\n row = { entity: e, components: [] };\n rows.set(e, row);\n }\n row.components.push(describe(type, value, previewLength));\n });\n }\n return [...rows.values()].sort((a, b) => a.entity - b.entity);\n}\n\nfunction describe(type: ComponentType<unknown>, value: unknown, max: number): string {\n // a field component's stored value is only its slot: the numbers live in the world's columns\n if ('fields' in type) return type.name;\n let text: string;\n try {\n text = typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value);\n } catch {\n text = '[unprintable]';\n }\n if (text === undefined) return type.name;\n return `${type.name} ${text.length > max ? text.slice(0, max - 1) + '…' : text}`;\n}\n"],"names":["inspectWorld","world","components","limit","previewLength","type","describe","rows","e","value","row","a","b","max","text"],"mappings":"AAcO,SAASA,EAAaC,GAAcC,GAAgDC,IAAQ,IAAIC,IAAgB,IAAiB;AACtI,MAAI,CAACF,GAAY;AACf,WAAOD,EACJ,SAAA,EACA,MAAM,GAAGE,CAAK,EACd,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,YAAYF,EAAM,aAAa,CAAC,EAAE,IAAI,CAACI,MAASC,EAASD,GAAMJ,EAAM,IAAI,GAAGI,CAAI,GAAGD,CAAa,CAAC,IAAI;AAEnI,QAAMG,wBAAW,IAAA;AACjB,aAAWF,KAAQH;AACjB,IAAAD,EAAM,KAAKI,GAAM,CAACG,GAAGC,MAAU;AAC7B,UAAIC,IAAMH,EAAK,IAAIC,CAAC;AACpB,UAAI,CAACE,GAAK;AACR,YAAIH,EAAK,QAAQJ,EAAO;AACxB,QAAAO,IAAM,EAAE,QAAQF,GAAG,YAAY,CAAA,EAAC,GAChCD,EAAK,IAAIC,GAAGE,CAAG;AAAA,MACjB;AACA,MAAAA,EAAI,WAAW,KAAKJ,EAASD,GAAMI,GAAOL,CAAa,CAAC;AAAA,IAC1D,CAAC;AAEH,SAAO,CAAC,GAAGG,EAAK,OAAA,CAAQ,EAAE,KAAK,CAACI,GAAGC,MAAMD,EAAE,SAASC,EAAE,MAAM;AAC9D;AAEA,SAASN,EAASD,GAA8BI,GAAgBI,GAAqB;AAEnF,MAAI,YAAYR,EAAM,QAAOA,EAAK;AAClC,MAAIS;AACJ,MAAI;AACF,IAAAA,IAAO,OAAOL,KAAU,YAAYA,MAAU,OAAO,KAAK,UAAUA,CAAK,IAAI,OAAOA,CAAK;AAAA,EAC3F,QAAQ;AACN,IAAAK,IAAO;AAAA,EACT;AACA,SAAIA,MAAS,SAAkBT,EAAK,OAC7B,GAAGA,EAAK,IAAI,IAAIS,EAAK,SAASD,IAAMC,EAAK,MAAM,GAAGD,IAAM,CAAC,IAAI,MAAMC,CAAI;AAChF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ErrorDeduper.js","sources":["../../src/diagnostics/ErrorDeduper.ts"],"sourcesContent":["/**\n * Lets through each distinct error once, and only so many in all: a loop throwing every frame is one report, not\n * sixty a second. Errors are told apart by the first `keyLength` characters of their message.\n */\nexport class ErrorDeduper {\n private readonly seen = new Set<string>();\n\n constructor(\n private readonly max = 10,\n private readonly keyLength = 120,\n ) {}\n\n /** The key to report under, or null when this error was already let through or the allowance is spent. */\n admit(message: string): string | null {\n const key = message.slice(0, this.keyLength);\n if (this.seen.has(key) || this.seen.size >= this.max) return null;\n this.seen.add(key);\n return key;\n }\n\n /** Forgets what was let through (a new session). */\n reset(): void {\n this.seen.clear();\n }\n}\n\n/** The first `lines` lines of a stack, at most `max` characters. */\nexport function trimStack(stack: string | undefined, lines = 4, max = 600): string {\n return (stack ?? '').split('\\n').slice(0, lines).join('\\n').slice(0, max);\n}\n"],"names":["ErrorDeduper","max","keyLength","message","key","trimStack","stack","lines"],"mappings":"AAIO,MAAMA,EAAa;AAAA,EAGxB,YACmBC,IAAM,IACNC,IAAY,KAC7B;AAFiB,SAAA,MAAAD,GACA,KAAA,YAAAC;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAJF,2BAAW,IAAA;AAAA;AAAA,EAQ5B,MAAMC,GAAgC;AACpC,UAAMC,IAAMD,EAAQ,MAAM,GAAG,KAAK,SAAS;AAC3C,WAAI,KAAK,KAAK,IAAIC,CAAG,KAAK,KAAK,KAAK,QAAQ,KAAK,MAAY,QAC7D,KAAK,KAAK,IAAIA,CAAG,GACVA;AAAA,EACT;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,KAAK,MAAA;AAAA,EACZ;AACF;AAGO,SAASC,EAAUC,GAA2BC,IAAQ,GAAGN,IAAM,KAAa;AACjF,UAAQK,KAAS,IAAI,MAAM;AAAA,CAAI,EAAE,MAAM,GAAGC,CAAK,EAAE,KAAK;AAAA,CAAI,EAAE,MAAM,GAAGN,CAAG;AAC1E;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"FrameSampler.js","sources":["../../src/diagnostics/FrameSampler.ts"],"sourcesContent":["import { type Ticker, UPDATE_PRIORITY } from 'pixi.js';\nimport { looksThrottled, summarizeFrames, type FrameSummary } from './frameStats';\n\nexport interface FrameSamplerOptions {\n /** How long one sample spans, ms (default a minute). */\n windowMs?: number;\n /** A frame longer than this is a hitch, ms (default 50). */\n hitchMs?: number;\n /** Fewer frames than this in a window is no sample (default 30). */\n minFrames?: number;\n /** A window whose median frame is over this came from a throttled background window and is dropped (default 500). */\n throttledMedianMs?: number;\n /** Frames are not counted while this says so (default: the page is hidden). */\n paused?: () => boolean;\n now?: () => number;\n}\n\n/**\n * Measures how a game really runs on a player's device: each frame costs one number pushed on an array, and every\n * `windowMs` the window is summed into a `FrameSummary` handed to `onSample` — FPS, the 1% low, the 95th-percentile\n * frame, hitches. The engine sends nothing anywhere: what the game does with a sample (a readout, a report to its\n * server) is the callback's.\n */\nexport class FrameSampler {\n private frames: number[] = [];\n private since: number;\n private readonly opts: Required<Omit<FrameSamplerOptions, 'paused' | 'now'>>;\n private readonly paused: () => boolean;\n private readonly now: () => number;\n\n constructor(private readonly onSample: (summary: FrameSummary) => void, options: FrameSamplerOptions = {}) {\n this.opts = {\n windowMs: options.windowMs ?? 60_000,\n hitchMs: options.hitchMs ?? 50,\n minFrames: options.minFrames ?? 30,\n throttledMedianMs: options.throttledMedianMs ?? 500,\n };\n this.paused = options.paused ?? (() => typeof document !== 'undefined' && document.hidden);\n this.now = options.now ?? (() => performance.now());\n this.since = this.now();\n }\n\n /** One frame of `deltaMs`; sums the window once it is full. */\n push(deltaMs: number): void {\n if (this.paused()) return;\n this.frames.push(deltaMs);\n const now = this.now();\n const span = now - this.since;\n if (span < this.opts.windowMs) return;\n const sample = this.frames;\n this.frames = [];\n this.since = now;\n if (sample.length < this.opts.minFrames) return;\n const summary = summarizeFrames(sample, span, this.opts.hitchMs);\n if (looksThrottled(summary, this.opts.throttledMedianMs)) return;\n this.onSample(summary);\n }\n\n /** Starts a fresh window, dropping what was counted (after a scene change, say). */\n reset(): void {\n this.frames = [];\n this.since = this.now();\n }\n\n /** Feeds the sampler from a Pixi ticker, after the render; returns the detach. */\n attach(ticker: Ticker): () => void {\n const tick = () => this.push(ticker.deltaMS);\n ticker.add(tick, undefined, UPDATE_PRIORITY.UTILITY);\n return () => ticker.remove(tick);\n }\n}\n"],"names":["FrameSampler","onSample","options","deltaMs","now","span","sample","summary","summarizeFrames","looksThrottled","ticker","tick","UPDATE_PRIORITY"],"mappings":";;AAuBO,MAAMA,EAAa;AAAA,EAOxB,YAA6BC,GAA2CC,IAA+B,IAAI;AAA9E,SAAA,WAAAD,GAC3B,KAAK,OAAO;AAAA,MACV,UAAUC,EAAQ,YAAY;AAAA,MAC9B,SAASA,EAAQ,WAAW;AAAA,MAC5B,WAAWA,EAAQ,aAAa;AAAA,MAChC,mBAAmBA,EAAQ,qBAAqB;AAAA,IAAA,GAElD,KAAK,SAASA,EAAQ,WAAW,MAAM,OAAO,WAAa,OAAe,SAAS,SACnF,KAAK,MAAMA,EAAQ,QAAQ,MAAM,YAAY,QAC7C,KAAK,QAAQ,KAAK,IAAA;AAAA,EACpB;AAAA,EAV6B;AAAA,EANrB,SAAmB,CAAA;AAAA,EACnB;AAAA,EACS;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAejB,KAAKC,GAAuB;AAC1B,QAAI,KAAK,SAAU;AACnB,SAAK,OAAO,KAAKA,CAAO;AACxB,UAAMC,IAAM,KAAK,IAAA,GACXC,IAAOD,IAAM,KAAK;AACxB,QAAIC,IAAO,KAAK,KAAK,SAAU;AAC/B,UAAMC,IAAS,KAAK;AAGpB,QAFA,KAAK,SAAS,CAAA,GACd,KAAK,QAAQF,GACTE,EAAO,SAAS,KAAK,KAAK,UAAW;AACzC,UAAMC,IAAUC,EAAgBF,GAAQD,GAAM,KAAK,KAAK,OAAO;AAC/D,IAAII,EAAeF,GAAS,KAAK,KAAK,iBAAiB,KACvD,KAAK,SAASA,CAAO;AAAA,EACvB;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,SAAS,CAAA,GACd,KAAK,QAAQ,KAAK,IAAA;AAAA,EACpB;AAAA;AAAA,EAGA,OAAOG,GAA4B;AACjC,UAAMC,IAAO,MAAM,KAAK,KAAKD,EAAO,OAAO;AAC3C,WAAAA,EAAO,IAAIC,GAAM,QAAWC,EAAgB,OAAO,GAC5C,MAAMF,EAAO,OAAOC,CAAI;AAAA,EACjC;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"bootWatch.js","sources":["../../src/diagnostics/bootWatch.ts"],"sourcesContent":["/** What `bootWatch` reports when a start hangs. */\nexport interface BootStall {\n /** The step the start stood on. */\n step: string;\n /** Every step reached, in order, with the ms since the watch began when each was reached. */\n steps: { name: string; atMs: number }[];\n /** `stuck: <step>`, a line short enough for a player's screenshot. */\n detail: string;\n}\n\nexport interface BootWatch {\n /** Moves the watch on to a named step. */\n step(name: string): void;\n /** The start finished: the watch stops. */\n done(): void;\n}\n\n/**\n * Says when a start has not finished `ms` after it began, naming the step it stood on. A world that never got built\n * otherwise leaves the player looking at a loading line forever while the rest of the page goes on under it; named\n * steps turn \"it hangs\" into \"it hangs loading textures\".\n */\nexport function bootWatch(ms: number, onStuck: (stall: BootStall) => void, now: () => number = () => performance.now()): BootWatch {\n const began = now();\n const steps = [{ name: 'init', atMs: 0 }];\n let finished = false;\n const timer = setTimeout(() => {\n if (finished) return;\n const step = steps[steps.length - 1]!.name;\n onStuck({ step, steps: steps.slice(), detail: `stuck: ${step}` });\n }, ms);\n return {\n step(name: string) {\n if (!finished) steps.push({ name, atMs: Math.round(now() - began) });\n },\n done() {\n finished = true;\n clearTimeout(timer);\n },\n };\n}\n"],"names":["bootWatch","ms","onStuck","now","began","steps","finished","timer","step","name"],"mappings":"AAsBO,SAASA,EAAUC,GAAYC,GAAqCC,IAAoB,MAAM,YAAY,OAAkB;AACjI,QAAMC,IAAQD,EAAA,GACRE,IAAQ,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG;AACxC,MAAIC,IAAW;AACf,QAAMC,IAAQ,WAAW,MAAM;AAC7B,QAAID,EAAU;AACd,UAAME,IAAOH,EAAMA,EAAM,SAAS,CAAC,EAAG;AACtC,IAAAH,EAAQ,EAAE,MAAAM,GAAM,OAAOH,EAAM,SAAS,QAAQ,UAAUG,CAAI,GAAA,CAAI;AAAA,EAClE,GAAGP,CAAE;AACL,SAAO;AAAA,IACL,KAAKQ,GAAc;AACjB,MAAKH,KAAUD,EAAM,KAAK,EAAE,MAAAI,GAAM,MAAM,KAAK,MAAMN,MAAQC,CAAK,GAAG;AAAA,IACrE;AAAA,IACA,OAAO;AACL,MAAAE,IAAW,IACX,aAAaC,CAAK;AAAA,IACpB;AAAA,EAAA;AAEJ;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"captureErrors.js","sources":["../../src/diagnostics/captureErrors.ts"],"sourcesContent":["import { Stage } from '../core/Stage';\nimport { ErrorDeduper, trimStack } from './ErrorDeduper';\n\n/** One error, as handed to `captureErrors`' callback. */\nexport interface CapturedError {\n message: string;\n /** The first lines of its stack, or ''. */\n stack: string;\n /** `uncaught`, `rejection`, or `frame` for one a `Stage` step caught so the frame could go on. */\n source: 'uncaught' | 'rejection' | 'frame';\n}\n\nexport interface CaptureErrorsOptions {\n /** Distinct errors reported in all (default 10). */\n max?: number;\n /** Also report what a `Stage` frame caught (`Stage.onCaught`; default true): there a fault shows only as a stuck view. */\n frames?: boolean;\n /** Shared with the game's own reports (`admit` before sending), so one error is not told twice. */\n deduper?: ErrorDeduper;\n}\n\n/**\n * Hands every error nothing else caught — `error` and `unhandledrejection` on the window, and what a frame caught —\n * to `onError`, each distinct one once. The engine sends nothing itself; reporting it is the game's. Returns the\n * uninstall, which puts back whatever held `Stage.onCaught` before.\n */\nexport function captureErrors(onError: (e: CapturedError) => void, options: CaptureErrorsOptions = {}): () => void {\n const dedupe = options.deduper ?? new ErrorDeduper(options.max ?? 10);\n const report = (message: string, stack: string | undefined, source: CapturedError['source']) => {\n const key = dedupe.admit(message);\n if (key !== null) onError({ message: key, stack: trimStack(stack), source });\n };\n const onWindowError = (e: ErrorEvent) => report(e.message || String(e.error), (e.error as { stack?: string } | undefined)?.stack, 'uncaught');\n const onRejection = (e: PromiseRejectionEvent) => {\n const r = e.reason as { message?: string; stack?: string } | undefined;\n report(r?.message ?? String(e.reason), r?.stack, 'rejection');\n };\n const onCaught = (where: string, err: unknown) => {\n const e = err as { message?: string; stack?: string } | undefined;\n report(`${where}: ${e?.message ?? String(err)}`, e?.stack, 'frame');\n };\n const win = typeof window !== 'undefined' ? window : undefined;\n win?.addEventListener('error', onWindowError);\n win?.addEventListener('unhandledrejection', onRejection);\n const previous = Stage.onCaught;\n if (options.frames !== false) {\n Stage.onCaught = (where, err) => {\n previous?.(where, err);\n onCaught(where, err);\n };\n }\n const installed = Stage.onCaught;\n return () => {\n win?.removeEventListener('error', onWindowError);\n win?.removeEventListener('unhandledrejection', onRejection);\n if (options.frames !== false && Stage.onCaught === installed) Stage.onCaught = previous;\n };\n}\n"],"names":["captureErrors","onError","options","dedupe","ErrorDeduper","report","message","stack","source","key","trimStack","onWindowError","onRejection","r","onCaught","where","err","e","win","previous","Stage","installed"],"mappings":";;AA0BO,SAASA,EAAcC,GAAqCC,IAAgC,IAAgB;AACjH,QAAMC,IAASD,EAAQ,WAAW,IAAIE,EAAaF,EAAQ,OAAO,EAAE,GAC9DG,IAAS,CAACC,GAAiBC,GAA2BC,MAAoC;AAC9F,UAAMC,IAAMN,EAAO,MAAMG,CAAO;AAChC,IAAIG,MAAQ,QAAMR,EAAQ,EAAE,SAASQ,GAAK,OAAOC,EAAUH,CAAK,GAAG,QAAAC,EAAA,CAAQ;AAAA,EAC7E,GACMG,IAAgB,CAAC,MAAkBN,EAAO,EAAE,WAAW,OAAO,EAAE,KAAK,GAAI,EAAE,OAA0C,OAAO,UAAU,GACtIO,IAAc,CAAC,MAA6B;AAChD,UAAMC,IAAI,EAAE;AACZ,IAAAR,EAAOQ,GAAG,WAAW,OAAO,EAAE,MAAM,GAAGA,GAAG,OAAO,WAAW;AAAA,EAC9D,GACMC,IAAW,CAACC,GAAeC,MAAiB;AAChD,UAAMC,IAAID;AACV,IAAAX,EAAO,GAAGU,CAAK,KAAKE,GAAG,WAAW,OAAOD,CAAG,CAAC,IAAIC,GAAG,OAAO,OAAO;AAAA,EACpE,GACMC,IAAM,OAAO,SAAW,MAAc,SAAS;AACrD,EAAAA,GAAK,iBAAiB,SAASP,CAAa,GAC5CO,GAAK,iBAAiB,sBAAsBN,CAAW;AACvD,QAAMO,IAAWC,EAAM;AACvB,EAAIlB,EAAQ,WAAW,OACrBkB,EAAM,WAAW,CAACL,GAAOC,MAAQ;AAC/B,IAAAG,IAAWJ,GAAOC,CAAG,GACrBF,EAASC,GAAOC,CAAG;AAAA,EACrB;AAEF,QAAMK,IAAYD,EAAM;AACxB,SAAO,MAAM;AACX,IAAAF,GAAK,oBAAoB,SAASP,CAAa,GAC/CO,GAAK,oBAAoB,sBAAsBN,CAAW,GACtDV,EAAQ,WAAW,MAASkB,EAAM,aAAaC,QAAiB,WAAWF;AAAA,EACjF;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"contextLoss.js","sources":["../../src/diagnostics/contextLoss.ts"],"sourcesContent":["/*\n * A WebGL context can be taken away at any time: a phone's GPU resetting, a driver crash, an Android WebView taking\n * the GPU back from an app in the background. Pixi does not rebuild a lost world by itself, so the game must — and a\n * GPU that keeps dropping it will not be saved by another try, so after a few losses in a short while it should stop\n * and say so instead of rebuilding forever.\n */\n\nexport interface ContextLossPolicy {\n /** Losses tolerated inside `windowMs` before giving up (default 3). */\n maxLosses?: number;\n /** How far back losses are counted, ms (default two minutes). */\n windowMs?: number;\n}\n\nexport interface ContextLossVerdict {\n /** The losses still inside the window, this one included (unless the page was hidden). */\n losses: number[];\n /** Stop rebuilding: the GPU keeps failing. */\n giveUp: boolean;\n /** How long to wait before rebuilding (`rebuildDelayMs`). */\n delayMs: number;\n}\n\n/**\n * One more context loss at `now`, given the earlier ones. A loss while the page is `hidden` is the system taking the\n * GPU back, not the GPU failing, so it is not counted against it.\n */\nexport function noteContextLoss(earlier: readonly number[], now: number, hidden = false, policy: ContextLossPolicy = {}): ContextLossVerdict {\n const { maxLosses = 3, windowMs = 120_000 } = policy;\n const kept = earlier.filter((t) => now - t < windowMs);\n const losses = hidden ? kept : kept.concat(now);\n return { losses, giveUp: losses.length > maxLosses, delayMs: rebuildDelayMs(losses.length) };\n}\n\n/**\n * How long to wait before rebuilding after a loss, with `losses` counted in the window: a GPU that has just reset is\n * given longer each time it drops the context again (a rebuild at once was dropped at once on a phone).\n */\nexport function rebuildDelayMs(losses: number): number {\n return losses <= 1 ? 250 : losses === 2 ? 2_000 : 5_000;\n}\n\n/**\n * Calls `onLost` with a verdict each time `canvas` (the renderer's) loses its WebGL context; the game tears its world\n * down and rebuilds it after `delayMs`, or shows an error when `giveUp`. The event's default is prevented, which is\n * what lets the browser restore the context at all. Returns the uninstall.\n */\nexport function watchContextLoss(\n canvas: EventTarget,\n onLost: (verdict: ContextLossVerdict) => void,\n options: ContextLossPolicy & { onRestored?: () => void; now?: () => number; hidden?: () => boolean } = {},\n): () => void {\n const now = options.now ?? (() => performance.now());\n const hidden = options.hidden ?? (() => typeof document !== 'undefined' && document.hidden);\n let losses: number[] = [];\n const lost = (e: Event) => {\n e.preventDefault();\n const verdict = noteContextLoss(losses, now(), hidden(), options);\n losses = verdict.losses;\n onLost(verdict);\n };\n const restored = () => options.onRestored?.();\n canvas.addEventListener('webglcontextlost', lost);\n canvas.addEventListener('webglcontextrestored', restored);\n return () => {\n canvas.removeEventListener('webglcontextlost', lost);\n canvas.removeEventListener('webglcontextrestored', restored);\n };\n}\n"],"names":["noteContextLoss","earlier","now","hidden","policy","maxLosses","windowMs","kept","t","losses","rebuildDelayMs","watchContextLoss","canvas","onLost","options","lost","e","verdict","restored"],"mappings":"AA2BO,SAASA,EAAgBC,GAA4BC,GAAaC,IAAS,IAAOC,IAA4B,IAAwB;AAC3I,QAAM,EAAE,WAAAC,IAAY,GAAG,UAAAC,IAAW,SAAYF,GACxCG,IAAON,EAAQ,OAAO,CAACO,MAAMN,IAAMM,IAAIF,CAAQ,GAC/CG,IAASN,IAASI,IAAOA,EAAK,OAAOL,CAAG;AAC9C,SAAO,EAAE,QAAAO,GAAQ,QAAQA,EAAO,SAASJ,GAAW,SAASK,EAAeD,EAAO,MAAM,EAAA;AAC3F;AAMO,SAASC,EAAeD,GAAwB;AACrD,SAAOA,KAAU,IAAI,MAAMA,MAAW,IAAI,MAAQ;AACpD;AAOO,SAASE,EACdC,GACAC,GACAC,IAAuG,CAAA,GAC3F;AACZ,QAAMZ,IAAMY,EAAQ,QAAQ,MAAM,YAAY,IAAA,IACxCX,IAASW,EAAQ,WAAW,MAAM,OAAO,WAAa,OAAe,SAAS;AACpF,MAAIL,IAAmB,CAAA;AACvB,QAAMM,IAAO,CAACC,MAAa;AACzB,IAAAA,EAAE,eAAA;AACF,UAAMC,IAAUjB,EAAgBS,GAAQP,KAAOC,EAAA,GAAUW,CAAO;AAChE,IAAAL,IAASQ,EAAQ,QACjBJ,EAAOI,CAAO;AAAA,EAChB,GACMC,IAAW,MAAMJ,EAAQ,aAAA;AAC/B,SAAAF,EAAO,iBAAiB,oBAAoBG,CAAI,GAChDH,EAAO,iBAAiB,wBAAwBM,CAAQ,GACjD,MAAM;AACX,IAAAN,EAAO,oBAAoB,oBAAoBG,CAAI,GACnDH,EAAO,oBAAoB,wBAAwBM,CAAQ;AAAA,EAC7D;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"frameStats.js","sources":["../../src/diagnostics/frameStats.ts"],"sourcesContent":["/** A summary of the frames of one sampling window. */\nexport interface FrameSummary {\n /** Frames rendered. */\n frames: number;\n /** Frames a second over the window. */\n fps: number;\n /** The \"1% low\": the frame rate of the slowest hundredth of frames, which is what stutter feels like. */\n fpsLow: number;\n /** The 95th-percentile frame time, ms. */\n frameP95: number;\n /** The median frame time, ms. */\n frameMedian: number;\n /** The longest frame, ms. */\n worstMs: number;\n /** Frames longer than the hitch threshold: the ones a player feels. */\n hitches: number;\n}\n\n/** The value at fraction `p` (0..1) of an ascending list, by nearest rank; 0 for an empty list. */\nexport function percentile(sorted: readonly number[], p: number): number {\n if (sorted.length === 0) return 0;\n return sorted[Math.min(sorted.length - 1, Math.max(0, Math.floor(sorted.length * p)))]!;\n}\n\n/** Frames longer than `thresholdMs`. */\nexport function countHitches(frameMs: readonly number[], thresholdMs: number): number {\n let n = 0;\n for (const d of frameMs) if (d > thresholdMs) n++;\n return n;\n}\n\nconst round1 = (n: number) => Math.round(n * 10) / 10;\n\n/** The summary of frame times (ms) rendered over `spanMs`; a frame over `hitchMs` (default 50) is a hitch. */\nexport function summarizeFrames(frameMs: readonly number[], spanMs: number, hitchMs = 50): FrameSummary {\n const sorted = [...frameMs].sort((a, b) => a - b);\n return {\n frames: frameMs.length,\n fps: spanMs > 0 ? round1((frameMs.length * 1000) / spanMs) : 0,\n fpsLow: round1(1000 / Math.max(1, percentile(sorted, 0.99))),\n frameP95: round1(percentile(sorted, 0.95)),\n frameMedian: round1(percentile(sorted, 0.5)),\n worstMs: round1(sorted[sorted.length - 1] ?? 0),\n hitches: countHitches(frameMs, hitchMs),\n };\n}\n\n/**\n * A window the browser throttled: one covered by another window is not `hidden`, but runs at about a frame a second,\n * and a second copy of a game parked behind the first read as its heaviest scene. Nothing really played runs with\n * its median frame half a second long.\n */\nexport function looksThrottled(summary: FrameSummary, medianLimitMs = 500): boolean {\n return summary.frameMedian > medianLimitMs;\n}\n"],"names":["percentile","sorted","p","countHitches","frameMs","thresholdMs","n","d","round1","summarizeFrames","spanMs","hitchMs","a","b","looksThrottled","summary","medianLimitMs"],"mappings":"AAmBO,SAASA,EAAWC,GAA2BC,GAAmB;AACvE,SAAID,EAAO,WAAW,IAAU,IACzBA,EAAO,KAAK,IAAIA,EAAO,SAAS,GAAG,KAAK,IAAI,GAAG,KAAK,MAAMA,EAAO,SAASC,CAAC,CAAC,CAAC,CAAC;AACvF;AAGO,SAASC,EAAaC,GAA4BC,GAA6B;AACpF,MAAIC,IAAI;AACR,aAAWC,KAAKH,EAAS,CAAIG,IAAIF,KAAaC;AAC9C,SAAOA;AACT;AAEA,MAAME,IAAS,CAACF,MAAc,KAAK,MAAMA,IAAI,EAAE,IAAI;AAG5C,SAASG,EAAgBL,GAA4BM,GAAgBC,IAAU,IAAkB;AACtG,QAAMV,IAAS,CAAC,GAAGG,CAAO,EAAE,KAAK,CAACQ,GAAGC,MAAMD,IAAIC,CAAC;AAChD,SAAO;AAAA,IACL,QAAQT,EAAQ;AAAA,IAChB,KAAKM,IAAS,IAAIF,EAAQJ,EAAQ,SAAS,MAAQM,CAAM,IAAI;AAAA,IAC7D,QAAQF,EAAO,MAAO,KAAK,IAAI,GAAGR,EAAWC,GAAQ,IAAI,CAAC,CAAC;AAAA,IAC3D,UAAUO,EAAOR,EAAWC,GAAQ,IAAI,CAAC;AAAA,IACzC,aAAaO,EAAOR,EAAWC,GAAQ,GAAG,CAAC;AAAA,IAC3C,SAASO,EAAOP,EAAOA,EAAO,SAAS,CAAC,KAAK,CAAC;AAAA,IAC9C,SAASE,EAAaC,GAASO,CAAO;AAAA,EAAA;AAE1C;AAOO,SAASG,EAAeC,GAAuBC,IAAgB,KAAc;AAClF,SAAOD,EAAQ,cAAcC;AAC/B;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"gpuInfo.js","sources":["../../src/diagnostics/gpuInfo.ts"],"sourcesContent":["import type { Renderer } from 'pixi.js';\n\nfunction glOf(renderer: Renderer): WebGLRenderingContext | undefined {\n return (renderer as { gl?: WebGLRenderingContext }).gl;\n}\n\n/**\n * The largest texture side the GPU takes (WebGL's MAX_TEXTURE_SIZE), or null when the renderer is not WebGL. Many\n * phones stop at 4096, so an atlas planned for 8192 must split its pages there (`atlasPageLimit`).\n */\nexport function maxTextureSize(renderer: Renderer): number | null {\n const gl = glOf(renderer);\n if (!gl) return null;\n const n = Number(gl.getParameter(gl.MAX_TEXTURE_SIZE));\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\n/** The GPU's own name where the browser gives it (WEBGL_debug_renderer_info), else null; for telling devices apart. */\nexport function gpuName(renderer: Renderer, max = 80): string | null {\n try {\n const gl = glOf(renderer);\n const ext = gl?.getExtension('WEBGL_debug_renderer_info');\n return gl && ext ? String(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)).slice(0, max) : null;\n } catch {\n return null;\n }\n}\n"],"names":["glOf","renderer","maxTextureSize","gl","n","gpuName","max","ext"],"mappings":"AAEA,SAASA,EAAKC,GAAuD;AACnE,SAAQA,EAA4C;AACtD;AAMO,SAASC,EAAeD,GAAmC;AAChE,QAAME,IAAKH,EAAKC,CAAQ;AACxB,MAAI,CAACE,EAAI,QAAO;AAChB,QAAMC,IAAI,OAAOD,EAAG,aAAaA,EAAG,gBAAgB,CAAC;AACrD,SAAO,OAAO,SAASC,CAAC,KAAKA,IAAI,IAAIA,IAAI;AAC3C;AAGO,SAASC,EAAQJ,GAAoBK,IAAM,IAAmB;AACnE,MAAI;AACF,UAAMH,IAAKH,EAAKC,CAAQ,GAClBM,IAAMJ,GAAI,aAAa,2BAA2B;AACxD,WAAOA,KAAMI,IAAM,OAAOJ,EAAG,aAAaI,EAAI,uBAAuB,CAAC,EAAE,MAAM,GAAGD,CAAG,IAAI;AAAA,EAC1F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"renderGuard.js","sources":["../../src/diagnostics/renderGuard.ts"],"sourcesContent":["import { type Application, UPDATE_PRIORITY } from 'pixi.js';\n\n/** An error as one short line, for a player's screenshot or a report. */\nexport function describeError(err: unknown, max = 160): string {\n const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return text.length > max ? `${text.slice(0, max)}…` : text;\n}\n\n/**\n * Replaces Pixi's render on the ticker with one that survives a throw and reports a renderer that keeps failing.\n * A ticker listener that throws never asks for the next frame, and `Stage`'s per-step guard covers the game's steps,\n * not the renderer's: a render that threw on its first frame froze the canvas in silence while the page went on.\n * A single bad frame is let go; `failures` in a row are reported once. Returns the uninstall, which puts Pixi's own\n * render back.\n */\nexport function guardRender(app: Application, onFail: (detail: string, err: unknown) => void, failures = 30): () => void {\n app.ticker.remove(app.render, app);\n let inARow = 0;\n let told = false;\n const render = () => {\n try {\n app.render();\n inARow = 0;\n } catch (err) {\n inARow++;\n if (inARow === 1) console.error('[render] frame failed', err);\n if (inARow >= failures && !told) {\n told = true;\n onFail(`render: ${describeError(err)}`, err);\n }\n }\n };\n app.ticker.add(render, undefined, UPDATE_PRIORITY.LOW);\n return () => {\n app.ticker.remove(render);\n app.ticker.add(app.render, app, UPDATE_PRIORITY.LOW);\n };\n}\n"],"names":["describeError","err","max","text","guardRender","app","onFail","failures","inARow","told","render","UPDATE_PRIORITY"],"mappings":";AAGO,SAASA,EAAcC,GAAcC,IAAM,KAAa;AAC7D,QAAMC,IAAOF,aAAe,QAAQ,GAAGA,EAAI,IAAI,KAAKA,EAAI,OAAO,KAAK,OAAOA,CAAG;AAC9E,SAAOE,EAAK,SAASD,IAAM,GAAGC,EAAK,MAAM,GAAGD,CAAG,CAAC,MAAMC;AACxD;AASO,SAASC,EAAYC,GAAkBC,GAAgDC,IAAW,IAAgB;AACvH,EAAAF,EAAI,OAAO,OAAOA,EAAI,QAAQA,CAAG;AACjC,MAAIG,IAAS,GACTC,IAAO;AACX,QAAMC,IAAS,MAAM;AACnB,QAAI;AACF,MAAAL,EAAI,OAAA,GACJG,IAAS;AAAA,IACX,SAASP,GAAK;AACZ,MAAAO,KACIA,MAAW,KAAG,QAAQ,MAAM,yBAAyBP,CAAG,GACxDO,KAAUD,KAAY,CAACE,MACzBA,IAAO,IACPH,EAAO,WAAWN,EAAcC,CAAG,CAAC,IAAIA,CAAG;AAAA,IAE/C;AAAA,EACF;AACA,SAAAI,EAAI,OAAO,IAAIK,GAAQ,QAAWC,EAAgB,GAAG,GAC9C,MAAM;AACX,IAAAN,EAAI,OAAO,OAAOK,CAAM,GACxBL,EAAI,OAAO,IAAIA,EAAI,QAAQA,GAAKM,EAAgB,GAAG;AAAA,EACrD;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"SparseSet.js","sources":["../../src/ecs/SparseSet.ts"],"sourcesContent":["/**\n * One component type's storage: a sparse set. `sparse[index]` is where the entity's value sits in the dense arrays\n * (or -1), and `entities`/`values` are packed with no holes, so walking a component is a walk over two flat arrays\n * and finding one is two array reads. Removing swaps the last entry into the hole.\n *\n * Indexed by the entity's slot index (`entityIndex`), not the whole id: the generation is checked by the world.\n */\nexport class SparseSet<T> {\n private sparse: Int32Array = new Int32Array(64).fill(-1);\n /** Packed entity ids, in the same order as `values`. */\n readonly entities: number[] = [];\n readonly values: T[] = [];\n /** Bumped whenever an entity joins or leaves the set (not when a value changes): query caches key on it. */\n version = 0;\n\n get size(): number {\n return this.entities.length;\n }\n\n has(index: number): boolean {\n return index < this.sparse.length && this.sparse[index]! >= 0;\n }\n\n /** The value at slot `index`, or undefined. */\n get(index: number): T | undefined {\n if (index >= this.sparse.length) return undefined;\n const d = this.sparse[index]!;\n return d >= 0 ? this.values[d] : undefined;\n }\n\n set(index: number, entity: number, value: T): void {\n this.grow(index);\n const d = this.sparse[index]!;\n if (d >= 0) {\n this.values[d] = value;\n this.entities[d] = entity;\n return;\n }\n this.sparse[index] = this.entities.length;\n this.version++;\n this.entities.push(entity);\n this.values.push(value);\n }\n\n /** Removes slot `index`; true if it was there. */\n delete(index: number, indexOf: (entity: number) => number): boolean {\n if (index >= this.sparse.length) return false;\n const d = this.sparse[index]!;\n if (d < 0) return false;\n const last = this.entities.length - 1;\n if (d !== last) {\n const moved = this.entities[last]!;\n this.entities[d] = moved;\n this.values[d] = this.values[last]!;\n this.sparse[indexOf(moved)] = d;\n }\n this.entities.pop();\n this.values.pop();\n this.sparse[index] = -1;\n this.version++;\n return true;\n }\n\n clear(): void {\n this.version++;\n this.sparse.fill(-1);\n this.entities.length = 0;\n this.values.length = 0;\n }\n\n private grow(index: number): void {\n if (index < this.sparse.length) return;\n let n = this.sparse.length;\n while (n <= index) n *= 2;\n const next = new Int32Array(n).fill(-1);\n next.set(this.sparse);\n this.sparse = next;\n }\n}\n"],"names":["SparseSet","index","d","entity","value","indexOf","last","moved","n","next"],"mappings":"AAOO,MAAMA,EAAa;AAAA,EAChB,SAAqB,IAAI,WAAW,EAAE,EAAE,KAAK,EAAE;AAAA;AAAA,EAE9C,WAAqB,CAAA;AAAA,EACrB,SAAc,CAAA;AAAA;AAAA,EAEvB,UAAU;AAAA,EAEV,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAIC,GAAwB;AAC1B,WAAOA,IAAQ,KAAK,OAAO,UAAU,KAAK,OAAOA,CAAK,KAAM;AAAA,EAC9D;AAAA;AAAA,EAGA,IAAIA,GAA8B;AAChC,QAAIA,KAAS,KAAK,OAAO,OAAQ;AACjC,UAAMC,IAAI,KAAK,OAAOD,CAAK;AAC3B,WAAOC,KAAK,IAAI,KAAK,OAAOA,CAAC,IAAI;AAAA,EACnC;AAAA,EAEA,IAAID,GAAeE,GAAgBC,GAAgB;AACjD,SAAK,KAAKH,CAAK;AACf,UAAMC,IAAI,KAAK,OAAOD,CAAK;AAC3B,QAAIC,KAAK,GAAG;AACV,WAAK,OAAOA,CAAC,IAAIE,GACjB,KAAK,SAASF,CAAC,IAAIC;AACnB;AAAA,IACF;AACA,SAAK,OAAOF,CAAK,IAAI,KAAK,SAAS,QACnC,KAAK,WACL,KAAK,SAAS,KAAKE,CAAM,GACzB,KAAK,OAAO,KAAKC,CAAK;AAAA,EACxB;AAAA;AAAA,EAGA,OAAOH,GAAeI,GAA8C;AAClE,QAAIJ,KAAS,KAAK,OAAO,OAAQ,QAAO;AACxC,UAAMC,IAAI,KAAK,OAAOD,CAAK;AAC3B,QAAIC,IAAI,EAAG,QAAO;AAClB,UAAMI,IAAO,KAAK,SAAS,SAAS;AACpC,QAAIJ,MAAMI,GAAM;AACd,YAAMC,IAAQ,KAAK,SAASD,CAAI;AAChC,WAAK,SAASJ,CAAC,IAAIK,GACnB,KAAK,OAAOL,CAAC,IAAI,KAAK,OAAOI,CAAI,GACjC,KAAK,OAAOD,EAAQE,CAAK,CAAC,IAAIL;AAAA,IAChC;AACA,gBAAK,SAAS,IAAA,GACd,KAAK,OAAO,IAAA,GACZ,KAAK,OAAOD,CAAK,IAAI,IACrB,KAAK,WACE;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,WACL,KAAK,OAAO,KAAK,EAAE,GACnB,KAAK,SAAS,SAAS,GACvB,KAAK,OAAO,SAAS;AAAA,EACvB;AAAA,EAEQ,KAAKA,GAAqB;AAChC,QAAIA,IAAQ,KAAK,OAAO,OAAQ;AAChC,QAAIO,IAAI,KAAK,OAAO;AACpB,WAAOA,KAAKP,IAAO,CAAAO,KAAK;AACxB,UAAMC,IAAO,IAAI,WAAWD,CAAC,EAAE,KAAK,EAAE;AACtC,IAAAC,EAAK,IAAI,KAAK,MAAM,GACpB,KAAK,SAASA;AAAA,EAChB;AACF;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"World.js","sources":["../../src/ecs/World.ts"],"sourcesContent":["import { SparseSet } from './SparseSet';\nimport { growColumns, isFields, type Columns, type FieldSpec, type FieldValues, type FieldsType } from './fields';\n\n/**\n * An entity–component–system store. Entities are numbers; components are data kept in one sparse set per component\n * type; systems are functions run in the order they were added, each in a phase:\n *\n * - `fixed` runs once per simulation step (`FixedLoop`), for movement and rules;\n * - `frame` runs once per rendered frame, for drawing and anything purely visual.\n *\n * Storage is a sparse set per component (packed arrays, O(1) lookups), the layout fast ECS libraries use. For hot\n * loops prefer `each` (a callback, no allocation per entity) over `query` (an iterator yielding a fresh row).\n *\n * Entity ids are recycled: an id carries its slot index and a generation, so an id kept after its entity was\n * destroyed never matches the entity that later reuses the slot.\n *\n * Changing structure while walking: adding components is always safe (new entries are not visited this walk).\n * Removing a component or destroying an entity while a `query`, an `each` or a system run is in progress is deferred\n * until it ends, so nothing is skipped or visited twice.\n */\n\nexport type Entity = number;\n\n/** A component type: a typed key. Make one per kind of data with `defineComponent`. */\nexport interface ComponentType<T> {\n readonly id: number;\n readonly name: string;\n /** Phantom field carrying `T`; never set. */\n readonly __type?: T;\n}\n\nlet nextComponentId = 0;\n/** Every component type made, by id: what a debugger names a store by. */\nconst TYPES: ComponentType<unknown>[] = [];\n\nexport function defineComponent<T>(name: string): ComponentType<T> {\n const type = { id: nextComponentId++, name };\n TYPES[type.id] = type;\n return type;\n}\n\n/** A component of numeric fields kept in typed-array columns (`fields.ts`): the fast form for data every body has. */\nexport function defineFields<const S extends FieldSpec>(name: string, fields: S): FieldsType<S> {\n const type = { id: nextComponentId++, name, fields };\n TYPES[type.id] = type;\n return type;\n}\n\nexport type SystemPhase = 'fixed' | 'frame';\n\nexport interface SystemContext {\n world: World;\n /** Seconds this run covers: one step in `fixed`, the frame's length in `frame`. */\n dt: number;\n /** `performance.now()` of the frame. */\n now: number;\n /** In `frame`: the share of a step since the last fixed step (0–1), for interpolation. 0 in `fixed`. */\n alpha: number;\n}\n\nexport interface System {\n readonly name: string;\n readonly phase: SystemPhase;\n run(ctx: SystemContext): void;\n /** Called when the system is taken out of its world (or the world is cleared). */\n dispose?(): void;\n}\n\n/** A system from a function. */\nexport function system(name: string, phase: SystemPhase, run: (ctx: SystemContext) => void): System {\n return { name, phase, run };\n}\n\nexport type ComponentValues<T extends readonly ComponentType<unknown>[]> = { [K in keyof T]: T[K] extends ComponentType<infer V> ? V : never };\n\n/** Slot index in the low 22 bits (4 million live entities), generation above. */\nconst INDEX_BITS = 22;\nconst INDEX_MASK = (1 << INDEX_BITS) - 1;\nconst MAX_GENERATION = 2 ** 31;\n\n/** The slot an entity id sits in. */\nexport const entityIndex = (e: Entity): number => e & INDEX_MASK;\n\ntype Pending = { kind: 'destroy'; e: Entity } | { kind: 'remove'; e: Entity; type: ComponentType<unknown> };\n\nexport class World {\n /** Current generation per slot; an id is alive when its generation matches and the slot is in use. */\n private generations: number[] = [];\n private readonly inUse: boolean[] = [];\n private readonly free: number[] = [];\n private live = 0;\n private readonly stores: (SparseSet<unknown> | undefined)[] = [];\n /** Typed-array columns of the field components, by component id. */\n private readonly cols: (Record<string, ArrayLike<number>> | undefined)[] = [];\n private readonly systems: System[] = [];\n private readonly pending: Pending[] = [];\n /** `slots` results by the component ids asked for, rebuilt only when one of those sets changed membership. */\n private readonly slotCache = new Map<string, { versions: number[]; slots: Int32Array; view: Int32Array }>();\n /** Walks (`query`, `each`, system runs) in progress: structure changes wait until it is 0. */\n private walking = 0;\n\n /** A new entity with no components. */\n create(): Entity {\n let index: number;\n if (this.free.length) {\n index = this.free.pop()!;\n } else {\n index = this.generations.length;\n if (index > INDEX_MASK) throw new Error('World: too many live entities');\n this.generations.push(0);\n this.inUse.push(false);\n }\n this.inUse[index] = true;\n this.live++;\n return this.generations[index]! * (INDEX_MASK + 1) + index;\n }\n\n /** A new entity with these components. */\n spawn(...components: [ComponentType<unknown>, unknown][]): Entity {\n const e = this.create();\n for (const [type, value] of components) this.add(e, type, value);\n return e;\n }\n\n isAlive(e: Entity): boolean {\n const index = entityIndex(e);\n return this.inUse[index] === true && this.generations[index] === Math.floor(e / (INDEX_MASK + 1));\n }\n\n /** Every live entity, for tools (a debugger, a save); allocates, so not for a per-frame path. */\n entities(): Entity[] {\n const out: Entity[] = [];\n for (let i = 0; i < this.inUse.length; i++) if (this.inUse[i]) out.push(this.generations[i]! * (INDEX_MASK + 1) + i);\n return out;\n }\n\n /** The component types `e` holds, for tools; allocates. */\n componentsOf(e: Entity): ComponentType<unknown>[] {\n if (!this.isAlive(e)) return [];\n const index = entityIndex(e);\n const out: ComponentType<unknown>[] = [];\n this.stores.forEach((store, id) => {\n if (store?.has(index)) out.push(TYPES[id]!);\n });\n return out;\n }\n\n /** Live entities. */\n get size(): number {\n return this.live;\n }\n\n /** Removes an entity and every component it had (deferred while a walk is in progress). */\n destroy(e: Entity): void {\n if (!this.isAlive(e)) return;\n if (this.walking > 0) {\n this.pending.push({ kind: 'destroy', e });\n return;\n }\n const index = entityIndex(e);\n for (const store of this.stores) store?.delete(index, entityIndex);\n this.inUse[index] = false;\n this.generations[index] = (this.generations[index]! + 1) % MAX_GENERATION;\n this.free.push(index);\n this.live--;\n }\n\n /** Gives an entity a field component; fields left out start at 0. Returns the slot index into its columns. */\n add<S extends FieldSpec>(e: Entity, type: FieldsType<S>, values?: FieldValues<S>): number;\n add<T>(e: Entity, type: ComponentType<T>, value: T): T;\n add(e: Entity, type: ComponentType<unknown>, value?: unknown): unknown {\n if (!this.isAlive(e)) throw new Error(`World.add: entity ${e} is not alive`);\n const index = entityIndex(e);\n if (isFields(type)) {\n const cols = this.columnsOf(type, index + 1) as Record<string, { [i: number]: number }>;\n const values = (value ?? {}) as Record<string, number>;\n for (const name of Object.keys(type.fields)) cols[name]![index] = values[name] ?? 0;\n this.store(type).set(index, e, index);\n return index;\n }\n this.store(type).set(index, e, value);\n return value;\n }\n\n /**\n * The typed-array columns of a field component, indexed by slot. Fetch them per system run: adding entities may\n * grow them into new arrays.\n */\n columns<S extends FieldSpec>(type: FieldsType<S>): Columns<S> {\n return this.columnsOf(type, this.generations.length) as unknown as Columns<S>;\n }\n\n get<T>(e: Entity, type: ComponentType<T>): T | undefined {\n if (!this.isAlive(e)) return undefined;\n return this.stores[type.id]?.get(entityIndex(e)) as T | undefined;\n }\n\n /** The component, or an error naming what is missing: for code that knows the entity has it. */\n require<T>(e: Entity, type: ComponentType<T>): T {\n const store = this.stores[type.id];\n if (!this.isAlive(e) || !store?.has(entityIndex(e))) throw new Error(`World.require: entity ${e} has no ${type.name}`);\n return store.get(entityIndex(e)) as T;\n }\n\n has(e: Entity, type: ComponentType<unknown>): boolean {\n return this.isAlive(e) && (this.stores[type.id]?.has(entityIndex(e)) ?? false);\n }\n\n /** Takes one component off an entity (deferred while a walk is in progress). */\n remove(e: Entity, type: ComponentType<unknown>): void {\n if (!this.isAlive(e)) return;\n if (this.walking > 0) {\n this.pending.push({ kind: 'remove', e, type });\n return;\n }\n this.stores[type.id]?.delete(entityIndex(e), entityIndex);\n }\n\n /** How many entities hold `type`. */\n count(type: ComponentType<unknown>): number {\n return this.stores[type.id]?.size ?? 0;\n }\n\n /**\n * Calls `fn(entity, ...components)` for every live entity holding all of `types`. Allocates nothing per entity:\n * the form for systems that walk thousands of bodies every step.\n */\n each<const T extends readonly ComponentType<unknown>[]>(...args: [...types: T, fn: (e: Entity, ...values: ComponentValues<T>) => void]): void {\n const fn = args[args.length - 1] as (e: Entity, ...values: unknown[]) => void;\n const types = args.slice(0, -1) as unknown as T;\n const sets = this.setsFor(types);\n if (!sets) return;\n const lead = smallest(sets);\n const n = lead.entities.length;\n this.walking++;\n try {\n if (sets.length === 1) {\n for (let i = n - 1; i >= 0; i--) fn(lead.entities[i]!, lead.values[i]);\n } else if (sets.length === 2) {\n const [a, b] = sets as [SparseSet<unknown>, SparseSet<unknown>];\n for (let i = n - 1; i >= 0; i--) {\n const e = lead.entities[i]!;\n const index = entityIndex(e);\n if (!a.has(index) || !b.has(index)) continue;\n fn(e, a.get(index), b.get(index));\n }\n } else {\n const row: unknown[] = new Array(sets.length);\n for (let i = n - 1; i >= 0; i--) {\n const e = lead.entities[i]!;\n const index = entityIndex(e);\n let all = true;\n for (let s = 0; s < sets.length; s++) {\n const set = sets[s]!;\n if (!set.has(index)) {\n all = false;\n break;\n }\n row[s] = set.get(index);\n }\n if (all) fn(e, ...row);\n }\n }\n } finally {\n this.endWalk();\n }\n }\n\n /**\n * The slot index of every entity holding all of `types`, as a typed array: the fastest walk there is, made for\n * field components (`defineFields`), whose columns are indexed by slot.\n *\n * ```ts\n * const p = world.columns(Position), v = world.columns(Velocity);\n * const slots = world.slots(Position, Velocity);\n * for (let k = 0; k < slots.length; k++) { const i = slots[k]!; p.x[i]! += v.x[i]! * dt; }\n * ```\n *\n * The result is cached and rebuilt only when an entity joins or leaves one of those components, so asking every\n * step is cheap. It is a view the world owns: read it, don't keep it past the step, and fetch it again after\n * adding or removing components.\n */\n slots(...types: ComponentType<unknown>[]): Int32Array {\n const key = types.map((t) => t.id).join(',');\n const sets = this.setsFor(types);\n if (!sets) return EMPTY_SLOTS;\n let cached = this.slotCache.get(key);\n if (cached && cached.versions.every((v, k) => v === sets[k]!.version)) return cached.view;\n const lead = smallest(sets);\n const out = cached && cached.slots.length >= lead.size ? cached.slots : new Int32Array(Math.max(16, lead.size));\n let n = 0;\n for (let d = 0; d < lead.entities.length; d++) {\n const index = entityIndex(lead.entities[d]!);\n let all = true;\n for (const set of sets) {\n if (set !== lead && !set.has(index)) {\n all = false;\n break;\n }\n }\n if (all) out[n++] = index;\n }\n cached = { versions: sets.map((x) => x.version), slots: out, view: out.subarray(0, n) };\n this.slotCache.set(key, cached);\n return cached.view;\n }\n\n /**\n * Every live entity holding all of `types`, with those components in the same order. Yields a fresh row per\n * entity: convenient, but `each` is the form for hot loops.\n */\n *query<const T extends readonly ComponentType<unknown>[]>(...types: T): IterableIterator<[Entity, ...ComponentValues<T>]> {\n const sets = this.setsFor(types);\n if (!sets) return;\n const lead = smallest(sets);\n this.walking++;\n try {\n for (let i = lead.entities.length - 1; i >= 0; i--) {\n const e = lead.entities[i]!;\n const index = entityIndex(e);\n const row: unknown[] = [e];\n let all = true;\n for (const set of sets) {\n if (!set.has(index)) {\n all = false;\n break;\n }\n row.push(set.get(index));\n }\n if (all) yield row as [Entity, ...ComponentValues<T>];\n }\n } finally {\n this.endWalk();\n }\n }\n\n /** The first entity matching the query, or undefined: for singletons (the player, the camera target). */\n first<const T extends readonly ComponentType<unknown>[]>(...types: T): [Entity, ...ComponentValues<T>] | undefined {\n for (const row of this.query(...types)) return row;\n return undefined;\n }\n\n addSystem(s: System): this {\n if (this.systems.some((x) => x.name === s.name)) throw new Error(`World.addSystem: a system named \"${s.name}\" is already added`);\n this.systems.push(s);\n return this;\n }\n\n removeSystem(name: string): void {\n const i = this.systems.findIndex((s) => s.name === name);\n if (i < 0) return;\n const [s] = this.systems.splice(i, 1);\n s!.dispose?.();\n }\n\n /** Runs every system of `phase` in the order added. A system that throws is reported and the rest still run. */\n run(phase: SystemPhase, ctx: Omit<SystemContext, 'world'>, onError?: (system: System, err: unknown) => void): void {\n const full: SystemContext = { dt: ctx.dt, now: ctx.now, alpha: ctx.alpha, world: this };\n this.walking++;\n try {\n for (const s of this.systems) {\n if (s.phase !== phase) continue;\n try {\n s.run(full);\n } catch (err) {\n if (onError) onError(s, err);\n else throw err;\n }\n }\n } finally {\n this.endWalk();\n }\n }\n\n /** Every entity, component and system gone. */\n clear(): void {\n for (const s of this.systems.splice(0)) s.dispose?.();\n for (const store of this.stores) store?.clear();\n this.generations = this.generations.map((g) => (g + 1) % MAX_GENERATION);\n this.inUse.fill(false);\n this.free.length = 0;\n for (let i = this.generations.length - 1; i >= 0; i--) this.free.push(i);\n this.live = 0;\n this.pending.length = 0;\n this.slotCache.clear();\n }\n\n private columnsOf(type: FieldsType<FieldSpec>, slots: number): Record<string, ArrayLike<number>> {\n let cols = this.cols[type.id];\n const have = cols ? (Object.values(cols)[0]?.length ?? 0) : 0;\n if (!cols || have < slots) {\n let n = Math.max(64, have);\n while (n < slots) n *= 2;\n cols = growColumns(type.fields, n, cols);\n this.cols[type.id] = cols;\n }\n return cols;\n }\n\n private store<T>(type: ComponentType<T>): SparseSet<unknown> {\n let s = this.stores[type.id];\n if (!s) {\n s = new SparseSet<unknown>();\n this.stores[type.id] = s;\n }\n return s;\n }\n\n /** The stores for `types`, or null when one has never been used (so nothing can match). */\n private setsFor(types: readonly ComponentType<unknown>[]): SparseSet<unknown>[] | null {\n if (types.length === 0) return null;\n const sets: SparseSet<unknown>[] = [];\n for (const t of types) {\n const s = this.stores[t.id];\n if (!s || s.size === 0) return null;\n sets.push(s);\n }\n return sets;\n }\n\n private endWalk(): void {\n this.walking--;\n if (this.walking > 0 || this.pending.length === 0) return;\n const pending = this.pending.splice(0);\n for (const p of pending) {\n if (p.kind === 'destroy') this.destroy(p.e);\n else this.remove(p.e, p.type);\n }\n }\n}\n\nconst EMPTY_SLOTS = new Int32Array(0);\n\nfunction smallest(sets: SparseSet<unknown>[]): SparseSet<unknown> {\n let lead = sets[0]!;\n for (const s of sets) if (s.size < lead.size) lead = s;\n return lead;\n}\n"],"names":["nextComponentId","TYPES","defineComponent","name","type","defineFields","fields","system","phase","run","INDEX_BITS","INDEX_MASK","MAX_GENERATION","entityIndex","e","World","index","components","value","out","i","store","id","isFields","cols","values","args","fn","types","sets","lead","smallest","n","a","b","row","all","s","set","key","t","EMPTY_SLOTS","cached","v","k","d","x","ctx","onError","full","err","g","slots","have","growColumns","SparseSet","pending","p"],"mappings":";;AA+BA,IAAIA,IAAkB;AAEtB,MAAMC,IAAkC,CAAA;AAEjC,SAASC,EAAmBC,GAAgC;AACjE,QAAMC,IAAO,EAAE,IAAIJ,KAAmB,MAAAG,EAAA;AACtC,SAAAF,EAAMG,EAAK,EAAE,IAAIA,GACVA;AACT;AAGO,SAASC,EAAwCF,GAAcG,GAA0B;AAC9F,QAAMF,IAAO,EAAE,IAAIJ,KAAmB,MAAAG,GAAM,QAAAG,EAAA;AAC5C,SAAAL,EAAMG,EAAK,EAAE,IAAIA,GACVA;AACT;AAuBO,SAASG,EAAOJ,GAAcK,GAAoBC,GAA2C;AAClG,SAAO,EAAE,MAAAN,GAAM,OAAAK,GAAO,KAAAC,EAAA;AACxB;AAKA,MAAMC,IAAa,IACbC,KAAc,KAAKD,KAAc,GACjCE,IAAiB,KAAK,IAGfC,IAAc,CAACC,MAAsBA,IAAIH;AAI/C,MAAMI,EAAM;AAAA;AAAA,EAET,cAAwB,CAAA;AAAA,EACf,QAAmB,CAAA;AAAA,EACnB,OAAiB,CAAA;AAAA,EAC1B,OAAO;AAAA,EACE,SAA6C,CAAA;AAAA;AAAA,EAE7C,OAA0D,CAAA;AAAA,EAC1D,UAAoB,CAAA;AAAA,EACpB,UAAqB,CAAA;AAAA;AAAA,EAErB,gCAAgB,IAAA;AAAA;AAAA,EAEzB,UAAU;AAAA;AAAA,EAGlB,SAAiB;AACf,QAAIC;AACJ,QAAI,KAAK,KAAK;AACZ,MAAAA,IAAQ,KAAK,KAAK,IAAA;AAAA,SACb;AAEL,UADAA,IAAQ,KAAK,YAAY,QACrBA,IAAQL,EAAY,OAAM,IAAI,MAAM,+BAA+B;AACvE,WAAK,YAAY,KAAK,CAAC,GACvB,KAAK,MAAM,KAAK,EAAK;AAAA,IACvB;AACA,gBAAK,MAAMK,CAAK,IAAI,IACpB,KAAK,QACE,KAAK,YAAYA,CAAK,KAAML,IAAa,KAAKK;AAAA,EACvD;AAAA;AAAA,EAGA,SAASC,GAAyD;AAChE,UAAMH,IAAI,KAAK,OAAA;AACf,eAAW,CAACV,GAAMc,CAAK,KAAKD,EAAY,MAAK,IAAIH,GAAGV,GAAMc,CAAK;AAC/D,WAAOJ;AAAA,EACT;AAAA,EAEA,QAAQA,GAAoB;AAC1B,UAAME,IAAQH,EAAYC,CAAC;AAC3B,WAAO,KAAK,MAAME,CAAK,MAAM,MAAQ,KAAK,YAAYA,CAAK,MAAM,KAAK,MAAMF,KAAKH,IAAa,EAAE;AAAA,EAClG;AAAA;AAAA,EAGA,WAAqB;AACnB,UAAMQ,IAAgB,CAAA;AACtB,aAASC,IAAI,GAAGA,IAAI,KAAK,MAAM,QAAQA,KAAS,KAAK,MAAMA,CAAC,KAAGD,EAAI,KAAK,KAAK,YAAYC,CAAC,KAAMT,IAAa,KAAKS,CAAC;AACnH,WAAOD;AAAA,EACT;AAAA;AAAA,EAGA,aAAaL,GAAqC;AAChD,QAAI,CAAC,KAAK,QAAQA,CAAC,UAAU,CAAA;AAC7B,UAAME,IAAQH,EAAYC,CAAC,GACrBK,IAAgC,CAAA;AACtC,gBAAK,OAAO,QAAQ,CAACE,GAAOC,MAAO;AACjC,MAAID,GAAO,IAAIL,CAAK,OAAO,KAAKf,EAAMqB,CAAE,CAAE;AAAA,IAC5C,CAAC,GACMH;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,QAAQL,GAAiB;AACvB,QAAI,CAAC,KAAK,QAAQA,CAAC,EAAG;AACtB,QAAI,KAAK,UAAU,GAAG;AACpB,WAAK,QAAQ,KAAK,EAAE,MAAM,WAAW,GAAAA,GAAG;AACxC;AAAA,IACF;AACA,UAAME,IAAQH,EAAYC,CAAC;AAC3B,eAAWO,KAAS,KAAK,OAAQ,CAAAA,GAAO,OAAOL,GAAOH,CAAW;AACjE,SAAK,MAAMG,CAAK,IAAI,IACpB,KAAK,YAAYA,CAAK,KAAK,KAAK,YAAYA,CAAK,IAAK,KAAKJ,GAC3D,KAAK,KAAK,KAAKI,CAAK,GACpB,KAAK;AAAA,EACP;AAAA,EAKA,IAAIF,GAAWV,GAA8Bc,GAA0B;AACrE,QAAI,CAAC,KAAK,QAAQJ,CAAC,SAAS,IAAI,MAAM,qBAAqBA,CAAC,eAAe;AAC3E,UAAME,IAAQH,EAAYC,CAAC;AAC3B,QAAIS,EAASnB,CAAI,GAAG;AAClB,YAAMoB,IAAO,KAAK,UAAUpB,GAAMY,IAAQ,CAAC,GACrCS,IAAUP,KAAS,CAAA;AACzB,iBAAWf,KAAQ,OAAO,KAAKC,EAAK,MAAM,EAAG,CAAAoB,EAAKrB,CAAI,EAAGa,CAAK,IAAIS,EAAOtB,CAAI,KAAK;AAClF,kBAAK,MAAMC,CAAI,EAAE,IAAIY,GAAOF,GAAGE,CAAK,GAC7BA;AAAA,IACT;AACA,gBAAK,MAAMZ,CAAI,EAAE,IAAIY,GAAOF,GAAGI,CAAK,GAC7BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAA6Bd,GAAiC;AAC5D,WAAO,KAAK,UAAUA,GAAM,KAAK,YAAY,MAAM;AAAA,EACrD;AAAA,EAEA,IAAOU,GAAWV,GAAuC;AACvD,QAAK,KAAK,QAAQU,CAAC;AACnB,aAAO,KAAK,OAAOV,EAAK,EAAE,GAAG,IAAIS,EAAYC,CAAC,CAAC;AAAA,EACjD;AAAA;AAAA,EAGA,QAAWA,GAAWV,GAA2B;AAC/C,UAAMiB,IAAQ,KAAK,OAAOjB,EAAK,EAAE;AACjC,QAAI,CAAC,KAAK,QAAQU,CAAC,KAAK,CAACO,GAAO,IAAIR,EAAYC,CAAC,CAAC,EAAG,OAAM,IAAI,MAAM,yBAAyBA,CAAC,WAAWV,EAAK,IAAI,EAAE;AACrH,WAAOiB,EAAM,IAAIR,EAAYC,CAAC,CAAC;AAAA,EACjC;AAAA,EAEA,IAAIA,GAAWV,GAAuC;AACpD,WAAO,KAAK,QAAQU,CAAC,MAAM,KAAK,OAAOV,EAAK,EAAE,GAAG,IAAIS,EAAYC,CAAC,CAAC,KAAK;AAAA,EAC1E;AAAA;AAAA,EAGA,OAAOA,GAAWV,GAAoC;AACpD,QAAK,KAAK,QAAQU,CAAC,GACnB;AAAA,UAAI,KAAK,UAAU,GAAG;AACpB,aAAK,QAAQ,KAAK,EAAE,MAAM,UAAU,GAAAA,GAAG,MAAAV,GAAM;AAC7C;AAAA,MACF;AACA,WAAK,OAAOA,EAAK,EAAE,GAAG,OAAOS,EAAYC,CAAC,GAAGD,CAAW;AAAA;AAAA,EAC1D;AAAA;AAAA,EAGA,MAAMT,GAAsC;AAC1C,WAAO,KAAK,OAAOA,EAAK,EAAE,GAAG,QAAQ;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAA2DsB,GAAmF;AAC5I,UAAMC,IAAKD,EAAKA,EAAK,SAAS,CAAC,GACzBE,IAAQF,EAAK,MAAM,GAAG,EAAE,GACxBG,IAAO,KAAK,QAAQD,CAAK;AAC/B,QAAI,CAACC,EAAM;AACX,UAAMC,IAAOC,EAASF,CAAI,GACpBG,IAAIF,EAAK,SAAS;AACxB,SAAK;AACL,QAAI;AACF,UAAID,EAAK,WAAW;AAClB,iBAAST,IAAIY,IAAI,GAAGZ,KAAK,GAAGA,IAAK,CAAAO,EAAGG,EAAK,SAASV,CAAC,GAAIU,EAAK,OAAOV,CAAC,CAAC;AAAA,eAC5DS,EAAK,WAAW,GAAG;AAC5B,cAAM,CAACI,GAAGC,CAAC,IAAIL;AACf,iBAAST,IAAIY,IAAI,GAAGZ,KAAK,GAAGA,KAAK;AAC/B,gBAAMN,IAAIgB,EAAK,SAASV,CAAC,GACnBJ,IAAQH,EAAYC,CAAC;AAC3B,UAAI,CAACmB,EAAE,IAAIjB,CAAK,KAAK,CAACkB,EAAE,IAAIlB,CAAK,KACjCW,EAAGb,GAAGmB,EAAE,IAAIjB,CAAK,GAAGkB,EAAE,IAAIlB,CAAK,CAAC;AAAA,QAClC;AAAA,MACF,OAAO;AACL,cAAMmB,IAAiB,IAAI,MAAMN,EAAK,MAAM;AAC5C,iBAAST,IAAIY,IAAI,GAAGZ,KAAK,GAAGA,KAAK;AAC/B,gBAAMN,IAAIgB,EAAK,SAASV,CAAC,GACnBJ,IAAQH,EAAYC,CAAC;AAC3B,cAAIsB,IAAM;AACV,mBAASC,IAAI,GAAGA,IAAIR,EAAK,QAAQQ,KAAK;AACpC,kBAAMC,IAAMT,EAAKQ,CAAC;AAClB,gBAAI,CAACC,EAAI,IAAItB,CAAK,GAAG;AACnB,cAAAoB,IAAM;AACN;AAAA,YACF;AACA,YAAAD,EAAIE,CAAC,IAAIC,EAAI,IAAItB,CAAK;AAAA,UACxB;AACA,UAAIoB,KAAKT,EAAGb,GAAG,GAAGqB,CAAG;AAAA,QACvB;AAAA,MACF;AAAA,IACF,UAAA;AACE,WAAK,QAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,SAASP,GAA6C;AACpD,UAAMW,IAAMX,EAAM,IAAI,CAACY,MAAMA,EAAE,EAAE,EAAE,KAAK,GAAG,GACrCX,IAAO,KAAK,QAAQD,CAAK;AAC/B,QAAI,CAACC,EAAM,QAAOY;AAClB,QAAIC,IAAS,KAAK,UAAU,IAAIH,CAAG;AACnC,QAAIG,KAAUA,EAAO,SAAS,MAAM,CAACC,GAAGC,MAAMD,MAAMd,EAAKe,CAAC,EAAG,OAAO,UAAUF,EAAO;AACrF,UAAMZ,IAAOC,EAASF,CAAI,GACpBV,IAAMuB,KAAUA,EAAO,MAAM,UAAUZ,EAAK,OAAOY,EAAO,QAAQ,IAAI,WAAW,KAAK,IAAI,IAAIZ,EAAK,IAAI,CAAC;AAC9G,QAAIE,IAAI;AACR,aAASa,IAAI,GAAGA,IAAIf,EAAK,SAAS,QAAQe,KAAK;AAC7C,YAAM7B,IAAQH,EAAYiB,EAAK,SAASe,CAAC,CAAE;AAC3C,UAAIT,IAAM;AACV,iBAAWE,KAAOT;AAChB,YAAIS,MAAQR,KAAQ,CAACQ,EAAI,IAAItB,CAAK,GAAG;AACnC,UAAAoB,IAAM;AACN;AAAA,QACF;AAEF,MAAIA,MAAKjB,EAAIa,GAAG,IAAIhB;AAAA,IACtB;AACA,WAAA0B,IAAS,EAAE,UAAUb,EAAK,IAAI,CAACiB,MAAMA,EAAE,OAAO,GAAG,OAAO3B,GAAK,MAAMA,EAAI,SAAS,GAAGa,CAAC,EAAA,GACpF,KAAK,UAAU,IAAIO,GAAKG,CAAM,GACvBA,EAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,CAAC,SAA4Dd,GAA6D;AACxH,UAAMC,IAAO,KAAK,QAAQD,CAAK;AAC/B,QAAI,CAACC,EAAM;AACX,UAAMC,IAAOC,EAASF,CAAI;AAC1B,SAAK;AACL,QAAI;AACF,eAAST,IAAIU,EAAK,SAAS,SAAS,GAAGV,KAAK,GAAGA,KAAK;AAClD,cAAMN,IAAIgB,EAAK,SAASV,CAAC,GACnBJ,IAAQH,EAAYC,CAAC,GACrBqB,IAAiB,CAACrB,CAAC;AACzB,YAAIsB,IAAM;AACV,mBAAWE,KAAOT,GAAM;AACtB,cAAI,CAACS,EAAI,IAAItB,CAAK,GAAG;AACnB,YAAAoB,IAAM;AACN;AAAA,UACF;AACA,UAAAD,EAAI,KAAKG,EAAI,IAAItB,CAAK,CAAC;AAAA,QACzB;AACA,QAAIoB,MAAK,MAAMD;AAAA,MACjB;AAAA,IACF,UAAA;AACE,WAAK,QAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGA,SAA4DP,GAAuD;AACjH,eAAWO,KAAO,KAAK,MAAM,GAAGP,CAAK,EAAG,QAAOO;AAAA,EAEjD;AAAA,EAEA,UAAUE,GAAiB;AACzB,QAAI,KAAK,QAAQ,KAAK,CAACS,MAAMA,EAAE,SAAST,EAAE,IAAI,SAAS,IAAI,MAAM,oCAAoCA,EAAE,IAAI,oBAAoB;AAC/H,gBAAK,QAAQ,KAAKA,CAAC,GACZ;AAAA,EACT;AAAA,EAEA,aAAalC,GAAoB;AAC/B,UAAMiB,IAAI,KAAK,QAAQ,UAAU,CAACiB,MAAMA,EAAE,SAASlC,CAAI;AACvD,QAAIiB,IAAI,EAAG;AACX,UAAM,CAACiB,CAAC,IAAI,KAAK,QAAQ,OAAOjB,GAAG,CAAC;AACpC,IAAAiB,EAAG,UAAA;AAAA,EACL;AAAA;AAAA,EAGA,IAAI7B,GAAoBuC,GAAmCC,GAAwD;AACjH,UAAMC,IAAsB,EAAE,IAAIF,EAAI,IAAI,KAAKA,EAAI,KAAK,OAAOA,EAAI,OAAO,OAAO,KAAA;AACjF,SAAK;AACL,QAAI;AACF,iBAAWV,KAAK,KAAK;AACnB,YAAIA,EAAE,UAAU7B;AAChB,cAAI;AACF,YAAA6B,EAAE,IAAIY,CAAI;AAAA,UACZ,SAASC,GAAK;AACZ,gBAAIF,EAAS,CAAAA,EAAQX,GAAGa,CAAG;AAAA,gBACtB,OAAMA;AAAA,UACb;AAAA,IAEJ,UAAA;AACE,WAAK,QAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,eAAWb,KAAK,KAAK,QAAQ,OAAO,CAAC,KAAK,UAAA;AAC1C,eAAWhB,KAAS,KAAK,OAAQ,CAAAA,GAAO,MAAA;AACxC,SAAK,cAAc,KAAK,YAAY,IAAI,CAAC8B,OAAOA,IAAI,KAAKvC,CAAc,GACvE,KAAK,MAAM,KAAK,EAAK,GACrB,KAAK,KAAK,SAAS;AACnB,aAASQ,IAAI,KAAK,YAAY,SAAS,GAAGA,KAAK,GAAGA,IAAK,MAAK,KAAK,KAAKA,CAAC;AACvE,SAAK,OAAO,GACZ,KAAK,QAAQ,SAAS,GACtB,KAAK,UAAU,MAAA;AAAA,EACjB;AAAA,EAEQ,UAAUhB,GAA6BgD,GAAkD;AAC/F,QAAI5B,IAAO,KAAK,KAAKpB,EAAK,EAAE;AAC5B,UAAMiD,IAAO7B,IAAQ,OAAO,OAAOA,CAAI,EAAE,CAAC,GAAG,UAAU,IAAK;AAC5D,QAAI,CAACA,KAAQ6B,IAAOD,GAAO;AACzB,UAAI,IAAI,KAAK,IAAI,IAAIC,CAAI;AACzB,aAAO,IAAID,IAAO,MAAK;AACvB,MAAA5B,IAAO8B,EAAYlD,EAAK,QAAQ,GAAGoB,CAAI,GACvC,KAAK,KAAKpB,EAAK,EAAE,IAAIoB;AAAA,IACvB;AACA,WAAOA;AAAA,EACT;AAAA,EAEQ,MAASpB,GAA4C;AAC3D,QAAI,IAAI,KAAK,OAAOA,EAAK,EAAE;AAC3B,WAAK,MACH,IAAI,IAAImD,EAAA,GACR,KAAK,OAAOnD,EAAK,EAAE,IAAI,IAElB;AAAA,EACT;AAAA;AAAA,EAGQ,QAAQwB,GAAuE;AACrF,QAAIA,EAAM,WAAW,EAAG,QAAO;AAC/B,UAAMC,IAA6B,CAAA;AACnC,eAAWW,KAAKZ,GAAO;AACrB,YAAMS,IAAI,KAAK,OAAOG,EAAE,EAAE;AAC1B,UAAI,CAACH,KAAKA,EAAE,SAAS,EAAG,QAAO;AAC/B,MAAAR,EAAK,KAAKQ,CAAC;AAAA,IACb;AACA,WAAOR;AAAA,EACT;AAAA,EAEQ,UAAgB;AAEtB,QADA,KAAK,WACD,KAAK,UAAU,KAAK,KAAK,QAAQ,WAAW,EAAG;AACnD,UAAM2B,IAAU,KAAK,QAAQ,OAAO,CAAC;AACrC,eAAWC,KAAKD;AACd,MAAIC,EAAE,SAAS,YAAW,KAAK,QAAQA,EAAE,CAAC,IACrC,KAAK,OAAOA,EAAE,GAAGA,EAAE,IAAI;AAAA,EAEhC;AACF;AAEA,MAAMhB,IAAc,IAAI,WAAW,CAAC;AAEpC,SAASV,EAASF,GAAgD;AAChE,MAAIC,IAAOD,EAAK,CAAC;AACjB,aAAW,KAAKA,EAAM,CAAI,EAAE,OAAOC,EAAK,SAAMA,IAAO;AACrD,SAAOA;AACT;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"fields.js","sources":["../../src/ecs/fields.ts"],"sourcesContent":["import type { ComponentType } from './World';\n\n/**\n * Numeric components stored as columns of typed arrays instead of one object per entity: `Position.x` and\n * `Position.y` are two `Float32Array`s indexed by the entity's slot. A system walking thousands of bodies then reads\n * memory in order and makes no garbage, which is several times faster than objects at scale (see `bench/`).\n *\n * ```ts\n * const Position = defineFields('Position', { x: 'f32', y: 'f32' });\n * world.add(e, Position, { x: 10, y: 20 });\n * const p = world.columns(Position); // fetch per run: columns grow (and are replaced) as entities are added\n * world.each(Position, (e, i) => { p.x[i] += 1; });\n * ```\n *\n * In `each`/`query` a field component's value is the entity's slot index `i`, the index into every column.\n */\nexport type FieldKind = 'f32' | 'f64' | 'i8' | 'i16' | 'i32' | 'u8' | 'u16' | 'u32';\n\nexport type FieldSpec = Record<string, FieldKind>;\n\nexport type ArrayOf<K extends FieldKind> = K extends 'f32'\n ? Float32Array\n : K extends 'f64'\n ? Float64Array\n : K extends 'i8'\n ? Int8Array\n : K extends 'i16'\n ? Int16Array\n : K extends 'i32'\n ? Int32Array\n : K extends 'u8'\n ? Uint8Array\n : K extends 'u16'\n ? Uint16Array\n : Uint32Array;\n\n/** One typed array per field, indexed by slot. */\nexport type Columns<S extends FieldSpec> = { [K in keyof S]: ArrayOf<S[K]> };\n\n/** What `add` takes for a field component: any subset of its fields (the rest start at 0). */\nexport type FieldValues<S extends FieldSpec> = { [K in keyof S]?: number };\n\n/** A component of numeric fields. Its value in `each`/`query` is the slot index. */\nexport interface FieldsType<S extends FieldSpec> extends ComponentType<number> {\n readonly fields: S;\n}\n\nconst CTORS: Record<FieldKind, new (n: number) => ArrayLike<number> & { set(a: ArrayLike<number>): void }> = {\n f32: Float32Array,\n f64: Float64Array,\n i8: Int8Array,\n i16: Int16Array,\n i32: Int32Array,\n u8: Uint8Array,\n u16: Uint16Array,\n u32: Uint32Array,\n};\n\nexport function isFields(type: ComponentType<unknown>): type is FieldsType<FieldSpec> {\n return (type as { fields?: unknown }).fields !== undefined;\n}\n\n/** Fresh columns of at least `n` slots, keeping what `old` held. */\nexport function growColumns(spec: FieldSpec, n: number, old?: Record<string, ArrayLike<number>>): Record<string, ArrayLike<number>> {\n const out: Record<string, ArrayLike<number>> = {};\n for (const [name, kind] of Object.entries(spec)) {\n const col = new CTORS[kind](n);\n const prev = old?.[name];\n if (prev) col.set(prev);\n out[name] = col;\n }\n return out;\n}\n"],"names":["CTORS","isFields","type","growColumns","spec","n","old","out","name","kind","col","prev"],"mappings":"AA+CA,MAAMA,IAAuG;AAAA,EAC3G,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AACP;AAEO,SAASC,EAASC,GAA6D;AACpF,SAAQA,EAA8B,WAAW;AACnD;AAGO,SAASC,EAAYC,GAAiBC,GAAWC,GAA4E;AAClI,QAAMC,IAAyC,CAAA;AAC/C,aAAW,CAACC,GAAMC,CAAI,KAAK,OAAO,QAAQL,CAAI,GAAG;AAC/C,UAAMM,IAAM,IAAIV,EAAMS,CAAI,EAAEJ,CAAC,GACvBM,IAAOL,IAAME,CAAI;AACvB,IAAIG,KAAMD,EAAI,IAAIC,CAAI,GACtBJ,EAAIC,CAAI,IAAIE;AAAA,EACd;AACA,SAAOH;AACT;"}