ecspresso 0.12.1 → 0.12.2

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 (52) hide show
  1. package/dist/index.js +4 -0
  2. package/dist/{src/index.js.map → index.js.map} +2 -2
  3. package/dist/plugins/audio.js +4 -0
  4. package/dist/{src/plugins → plugins}/audio.js.map +2 -2
  5. package/dist/plugins/bounds.js +4 -0
  6. package/dist/{src/plugins → plugins}/bounds.js.map +2 -2
  7. package/dist/plugins/camera.js +4 -0
  8. package/dist/{src/plugins → plugins}/camera.js.map +2 -2
  9. package/dist/plugins/collision.js +4 -0
  10. package/dist/{src/plugins → plugins}/collision.js.map +2 -2
  11. package/dist/plugins/coroutine.js +4 -0
  12. package/dist/{src/plugins → plugins}/coroutine.js.map +2 -2
  13. package/dist/plugins/diagnostics.js +5 -0
  14. package/dist/{src/plugins → plugins}/diagnostics.js.map +2 -2
  15. package/dist/plugins/input.js +4 -0
  16. package/dist/{src/plugins → plugins}/input.js.map +2 -2
  17. package/dist/plugins/particles.js +4 -0
  18. package/dist/{src/plugins → plugins}/particles.js.map +2 -2
  19. package/dist/plugins/physics2D.js +4 -0
  20. package/dist/{src/plugins → plugins}/physics2D.js.map +2 -2
  21. package/dist/plugins/renderers/renderer2D.js +4 -0
  22. package/dist/{src/plugins → plugins}/renderers/renderer2D.js.map +2 -2
  23. package/dist/plugins/spatial-index.js +4 -0
  24. package/dist/{src/plugins → plugins}/spatial-index.js.map +2 -2
  25. package/dist/plugins/sprite-animation.js +4 -0
  26. package/dist/{src/plugins → plugins}/sprite-animation.js.map +2 -2
  27. package/dist/plugins/state-machine.js +4 -0
  28. package/dist/{src/plugins → plugins}/state-machine.js.map +2 -2
  29. package/dist/plugins/timers.js +4 -0
  30. package/dist/{src/plugins → plugins}/timers.js.map +2 -2
  31. package/dist/plugins/transform.js +4 -0
  32. package/dist/{src/plugins → plugins}/transform.js.map +2 -2
  33. package/dist/plugins/tween.js +4 -0
  34. package/dist/{src/plugins → plugins}/tween.js.map +2 -2
  35. package/package.json +1 -1
  36. package/dist/src/index.js +0 -4
  37. package/dist/src/plugins/audio.js +0 -4
  38. package/dist/src/plugins/bounds.js +0 -4
  39. package/dist/src/plugins/camera.js +0 -4
  40. package/dist/src/plugins/collision.js +0 -4
  41. package/dist/src/plugins/coroutine.js +0 -4
  42. package/dist/src/plugins/diagnostics.js +0 -5
  43. package/dist/src/plugins/input.js +0 -4
  44. package/dist/src/plugins/particles.js +0 -4
  45. package/dist/src/plugins/physics2D.js +0 -4
  46. package/dist/src/plugins/renderers/renderer2D.js +0 -4
  47. package/dist/src/plugins/spatial-index.js +0 -4
  48. package/dist/src/plugins/sprite-animation.js +0 -4
  49. package/dist/src/plugins/state-machine.js +0 -4
  50. package/dist/src/plugins/timers.js +0 -4
  51. package/dist/src/plugins/transform.js +0 -4
  52. package/dist/src/plugins/tween.js +0 -4
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * Camera / Viewport Plugin for ECSpresso\n *\n * Provides a camera entity with world/screen coordinate conversion, smooth follow,\n * trauma-based shake, bounds clamping, and logical viewport dimensions.\n *\n * This plugin is renderer-agnostic. PixiJS or other renderer integration (applying\n * cameraState to a container/stage transform) is the consumer's responsibility.\n *\n * Camera uses its own x/y/zoom/rotation rather than localTransform/worldTransform.\n * It reads the target entity's worldTransform for follow, but doesn't participate\n * in the transform hierarchy itself.\n */\n\nimport { definePlugin, type Plugin } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\nimport type ECSpresso from 'ecspresso';\nimport type { WorldConfigFrom } from '../type-utils';\nimport type { TransformWorldConfig } from './transform';\n\n// ==================== Component Types ====================\n\nexport interface Camera {\n\tx: number;\n\ty: number;\n\tzoom: number;\n\trotation: number;\n}\n\nexport interface CameraFollow {\n\ttarget: number;\n\tsmoothing: number;\n\tdeadzoneX: number;\n\tdeadzoneY: number;\n\toffsetX: number;\n\toffsetY: number;\n}\n\nexport interface CameraShake {\n\ttrauma: number;\n\ttraumaDecay: number;\n\tmaxOffsetX: number;\n\tmaxOffsetY: number;\n\tmaxRotation: number;\n}\n\nexport interface CameraBounds {\n\tminX: number;\n\tminY: number;\n\tmaxX: number;\n\tmaxY: number;\n}\n\nexport interface CameraComponentTypes {\n\tcamera: Camera;\n\tcameraFollow: CameraFollow;\n\tcameraShake: CameraShake;\n\tcameraBounds: CameraBounds;\n}\n\n\n// ==================== Resource Types ====================\n\nexport interface CameraState {\n\tx: number;\n\ty: number;\n\tzoom: number;\n\trotation: number;\n\tshakeOffsetX: number;\n\tshakeOffsetY: number;\n\tshakeRotation: number;\n\tviewportWidth: number;\n\tviewportHeight: number;\n}\n\nexport interface CameraResourceTypes {\n\tcameraState: CameraState;\n}\n\n// ==================== Plugin Options ====================\n\nexport interface CameraPluginOptions<G extends string = 'camera'> {\n\tviewportWidth?: number;\n\tviewportHeight?: number;\n\tsystemGroup?: G;\n\tphase?: SystemPhase;\n\trandomFn?: () => number;\n}\n\n// ==================== Default Values ====================\n\nexport const DEFAULT_CAMERA: Readonly<Camera> = {\n\tx: 0,\n\ty: 0,\n\tzoom: 1,\n\trotation: 0,\n};\n\nexport const DEFAULT_CAMERA_STATE: Readonly<CameraState> = {\n\tx: 0,\n\ty: 0,\n\tzoom: 1,\n\trotation: 0,\n\tshakeOffsetX: 0,\n\tshakeOffsetY: 0,\n\tshakeRotation: 0,\n\tviewportWidth: 800,\n\tviewportHeight: 600,\n};\n\n// ==================== Helper Functions ====================\n\nexport function createCamera(\n\tx = 0,\n\ty = 0,\n\tzoom = 1,\n\trotation = 0,\n): Pick<CameraComponentTypes, 'camera'> {\n\treturn {\n\t\tcamera: { x, y, zoom, rotation },\n\t};\n}\n\nexport function createCameraFollow(\n\ttarget: number,\n\toptions?: Partial<Omit<CameraFollow, 'target'>>,\n): Pick<CameraComponentTypes, 'cameraFollow'> {\n\treturn {\n\t\tcameraFollow: {\n\t\t\ttarget,\n\t\t\tsmoothing: options?.smoothing ?? 5,\n\t\t\tdeadzoneX: options?.deadzoneX ?? 0,\n\t\t\tdeadzoneY: options?.deadzoneY ?? 0,\n\t\t\toffsetX: options?.offsetX ?? 0,\n\t\t\toffsetY: options?.offsetY ?? 0,\n\t\t},\n\t};\n}\n\nexport function createCameraShake(\n\toptions?: Partial<CameraShake>,\n): Pick<CameraComponentTypes, 'cameraShake'> {\n\treturn {\n\t\tcameraShake: {\n\t\t\ttrauma: options?.trauma ?? 0,\n\t\t\ttraumaDecay: options?.traumaDecay ?? 1,\n\t\t\tmaxOffsetX: options?.maxOffsetX ?? 10,\n\t\t\tmaxOffsetY: options?.maxOffsetY ?? 10,\n\t\t\tmaxRotation: options?.maxRotation ?? 0.05,\n\t\t},\n\t};\n}\n\nexport function createCameraBounds(\n\tminX: number,\n\tminY: number,\n\tmaxX: number,\n\tmaxY: number,\n): Pick<CameraComponentTypes, 'cameraBounds'> {\n\treturn {\n\t\tcameraBounds: { minX, minY, maxX, maxY },\n\t};\n}\n\nexport function addTrauma<\n\tCfg extends WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>,\n>(\n\tecs: ECSpresso<Cfg>,\n\tentityId: number,\n\tamount: number,\n): void {\n\tconst shake = ecs.getComponent(entityId, 'cameraShake');\n\tif (!shake) return;\n\tshake.trauma = Math.min(1, Math.max(0, shake.trauma + amount));\n}\n\n// ==================== Coordinate Conversion ====================\n\nexport function worldToScreen(\n\tworldX: number,\n\tworldY: number,\n\tstate: CameraState,\n): { x: number; y: number } {\n\tconst dx = worldX - (state.x + state.shakeOffsetX);\n\tconst dy = worldY - (state.y + state.shakeOffsetY);\n\n\tconst angle = -(state.rotation + state.shakeRotation);\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\tconst rx = dx * cos - dy * sin;\n\tconst ry = dx * sin + dy * cos;\n\n\treturn {\n\t\tx: rx * state.zoom + state.viewportWidth / 2,\n\t\ty: ry * state.zoom + state.viewportHeight / 2,\n\t};\n}\n\nexport function screenToWorld(\n\tscreenX: number,\n\tscreenY: number,\n\tstate: CameraState,\n): { x: number; y: number } {\n\tconst cx = (screenX - state.viewportWidth / 2) / state.zoom;\n\tconst cy = (screenY - state.viewportHeight / 2) / state.zoom;\n\n\tconst angle = state.rotation + state.shakeRotation;\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\tconst rx = cx * cos - cy * sin;\n\tconst ry = cx * sin + cy * cos;\n\n\treturn {\n\t\tx: rx + state.x + state.shakeOffsetX,\n\t\ty: ry + state.y + state.shakeOffsetY,\n\t};\n}\n\n// ==================== Plugin Factory ====================\n\nexport function createCameraPlugin<G extends string = 'camera'>(\n\toptions?: CameraPluginOptions<G>,\n): Plugin<WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>, TransformWorldConfig, 'camera-follow' | 'camera-shake-update' | 'camera-bounds' | 'camera-state-sync', G> {\n\tconst {\n\t\tviewportWidth = 800,\n\t\tviewportHeight = 600,\n\t\tsystemGroup = 'camera',\n\t\tphase = 'postUpdate',\n\t\trandomFn = Math.random,\n\t} = options ?? {};\n\n\treturn definePlugin<WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>, TransformWorldConfig, 'camera-follow' | 'camera-shake-update' | 'camera-bounds' | 'camera-state-sync', G>({\n\t\tid: 'camera',\n\t\tinstall(world) {\n\t\t\tworld.addResource('cameraState', {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tzoom: 1,\n\t\t\t\trotation: 0,\n\t\t\t\tshakeOffsetX: 0,\n\t\t\t\tshakeOffsetY: 0,\n\t\t\t\tshakeRotation: 0,\n\t\t\t\tviewportWidth,\n\t\t\t\tviewportHeight,\n\t\t\t});\n\n\t\t\t// camera-follow: priority 400 (after transform propagation at 500)\n\t\t\tworld\n\t\t\t\t.addSystem('camera-follow')\n\t\t\t\t.setPriority(400)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('cameras', {\n\t\t\t\t\twith: ['camera', 'cameraFollow'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, dt, ecs }) => {\n\t\t\t\t\tconst t = Math.min(1, dt);\n\t\t\t\t\tfor (const entity of queries.cameras) {\n\t\t\t\t\t\tconst { camera, cameraFollow } = entity.components;\n\t\t\t\t\t\tlet targetWorld;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\ttargetWorld = ecs.getComponent(cameraFollow.target, 'worldTransform');\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!targetWorld) continue;\n\t\t\t\t\t\tif (!targetWorld) continue;\n\n\t\t\t\t\t\tconst goalX = targetWorld.x + cameraFollow.offsetX;\n\t\t\t\t\t\tconst goalY = targetWorld.y + cameraFollow.offsetY;\n\t\t\t\t\t\tconst dx = goalX - camera.x;\n\t\t\t\t\t\tconst dy = goalY - camera.y;\n\n\t\t\t\t\t\tconst absDx = Math.abs(dx);\n\t\t\t\t\t\tconst absDy = Math.abs(dy);\n\n\t\t\t\t\t\tif (absDx > cameraFollow.deadzoneX) {\n\t\t\t\t\t\t\tconst sign = dx > 0 ? 1 : -1;\n\t\t\t\t\t\t\tconst excessX = dx - sign * cameraFollow.deadzoneX;\n\t\t\t\t\t\t\tconst factor = Math.min(1, cameraFollow.smoothing * t);\n\t\t\t\t\t\t\tcamera.x += excessX * factor;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (absDy > cameraFollow.deadzoneY) {\n\t\t\t\t\t\t\tconst sign = dy > 0 ? 1 : -1;\n\t\t\t\t\t\t\tconst excessY = dy - sign * cameraFollow.deadzoneY;\n\t\t\t\t\t\t\tconst factor = Math.min(1, cameraFollow.smoothing * t);\n\t\t\t\t\t\t\tcamera.y += excessY * factor;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// camera-shake-update: priority 390\n\t\t\tworld\n\t\t\t\t.addSystem('camera-shake-update')\n\t\t\t\t.setPriority(390)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('shakeCameras', {\n\t\t\t\t\twith: ['camera', 'cameraShake'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, dt }) => {\n\t\t\t\t\tfor (const entity of queries.shakeCameras) {\n\t\t\t\t\t\tconst { cameraShake } = entity.components;\n\t\t\t\t\t\tcameraShake.trauma = Math.max(0, cameraShake.trauma - cameraShake.traumaDecay * dt);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// camera-bounds: priority 380\n\t\t\tworld\n\t\t\t\t.addSystem('camera-bounds')\n\t\t\t\t.setPriority(380)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('boundedCameras', {\n\t\t\t\t\twith: ['camera', 'cameraBounds'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, ecs }) => {\n\t\t\t\t\tconst state = ecs.getResource('cameraState');\n\t\t\t\t\tfor (const entity of queries.boundedCameras) {\n\t\t\t\t\t\tconst { camera, cameraBounds } = entity.components;\n\t\t\t\t\t\tconst halfW = state.viewportWidth / (2 * camera.zoom);\n\t\t\t\t\t\tconst halfH = state.viewportHeight / (2 * camera.zoom);\n\n\t\t\t\t\t\tconst effectiveMinX = cameraBounds.minX + halfW;\n\t\t\t\t\t\tconst effectiveMaxX = cameraBounds.maxX - halfW;\n\t\t\t\t\t\tconst effectiveMinY = cameraBounds.minY + halfH;\n\t\t\t\t\t\tconst effectiveMaxY = cameraBounds.maxY - halfH;\n\n\t\t\t\t\t\tif (effectiveMinX > effectiveMaxX) {\n\t\t\t\t\t\t\tcamera.x = (cameraBounds.minX + cameraBounds.maxX) / 2;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcamera.x = Math.max(effectiveMinX, Math.min(effectiveMaxX, camera.x));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (effectiveMinY > effectiveMaxY) {\n\t\t\t\t\t\t\tcamera.y = (cameraBounds.minY + cameraBounds.maxY) / 2;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcamera.y = Math.max(effectiveMinY, Math.min(effectiveMaxY, camera.y));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// camera-state-sync: priority 370\n\t\t\tworld\n\t\t\t\t.addSystem('camera-state-sync')\n\t\t\t\t.setPriority(370)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setProcess(({ ecs }) => {\n\t\t\t\t\tconst state = ecs.getResource('cameraState');\n\t\t\t\t\tconst cameras = ecs.getEntitiesWithQuery(['camera']);\n\t\t\t\t\tconst first = cameras[0];\n\n\t\t\t\t\tif (!first) {\n\t\t\t\t\t\tstate.x = 0;\n\t\t\t\t\t\tstate.y = 0;\n\t\t\t\t\t\tstate.zoom = 1;\n\t\t\t\t\t\tstate.rotation = 0;\n\t\t\t\t\t\tstate.shakeOffsetX = 0;\n\t\t\t\t\t\tstate.shakeOffsetY = 0;\n\t\t\t\t\t\tstate.shakeRotation = 0;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst camera = first.components.camera;\n\t\t\t\t\tstate.x = camera.x;\n\t\t\t\t\tstate.y = camera.y;\n\t\t\t\t\tstate.zoom = camera.zoom;\n\t\t\t\t\tstate.rotation = camera.rotation;\n\n\t\t\t\t\tconst shake = ecs.getComponent(first.id, 'cameraShake');\n\t\t\t\t\tif (shake && shake.trauma > 0) {\n\t\t\t\t\t\tconst intensity = shake.trauma * shake.trauma;\n\t\t\t\t\t\tstate.shakeOffsetX = shake.maxOffsetX * intensity * (randomFn() * 2 - 1);\n\t\t\t\t\t\tstate.shakeOffsetY = shake.maxOffsetY * intensity * (randomFn() * 2 - 1);\n\t\t\t\t\t\tstate.shakeRotation = shake.maxRotation * intensity * (randomFn() * 2 - 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstate.shakeOffsetX = 0;\n\t\t\t\t\t\tstate.shakeOffsetY = 0;\n\t\t\t\t\t\tstate.shakeRotation = 0;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t},\n\t});\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "85631121506EC7F364756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "C69C18DDBD81816664756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,4 @@
1
+ var{defineProperty:D,getOwnPropertyNames:A,getOwnPropertyDescriptor:k}=Object,B=Object.prototype.hasOwnProperty;function v(z){return this[z]}var p=(z)=>{var J=(W??=new WeakMap).get(z),O;if(J)return J;if(J=D({},"__esModule",{value:!0}),z&&typeof z==="object"||typeof z==="function"){for(var Q of A(z))if(!B.call(J,Q))D(J,Q,{get:v.bind(z,Q),enumerable:!(O=k(z,Q))||O.enumerable})}return W.set(z,J),J},W;var C=(z)=>z;function X(z,J){this[z]=C.bind(null,J)}var y=(z,J)=>{for(var O in J)D(z,O,{get:J[O],enumerable:!0,configurable:!0,set:X.bind(J,O)})};var b=(z,J)=>()=>(z&&(J=z(z=0)),J);var d=((z)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(z,{get:(J,O)=>(typeof require<"u"?require:J)[O]}):z)(function(z){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+z+'" is not supported')});import{definePlugin as f}from"ecspresso";function L(z,J,O,Q,Z,$,N){if(!$&&!N)return null;let F={entityId:z,x:J,y:O,layer:Q,collidesWith:Z};if($)F.x+=$.offsetX??0,F.y+=$.offsetY??0,F.aabb={halfWidth:$.width/2,halfHeight:$.height/2};if(N)F.x+=N.offsetX??0,F.y+=N.offsetY??0,F.circle={radius:N.radius};return F}function P(z){return z("spatialIndex")}function Y(z,J,O,Q,Z,$,N,F){let E=Z-z,M=$-J,V=O+N-Math.abs(E),j=Q+F-Math.abs(M);if(V<=0||j<=0)return null;if(V<j)return{normalX:E>=0?1:-1,normalY:0,depth:V};return{normalX:0,normalY:M>=0?1:-1,depth:j}}function m(z,J,O,Q,Z,$){let N=Q-z,F=Z-J,E=N*N+F*F,M=O+$;if(E>=M*M)return null;let V=Math.sqrt(E);if(V===0)return{normalX:1,normalY:0,depth:M};return{normalX:N/V,normalY:F/V,depth:M-V}}function q(z,J,O,Q,Z,$,N){let F=Math.max(z-O,Math.min(Z,z+O)),E=Math.max(J-Q,Math.min($,J+Q)),M=Z-F,V=$-E,j=M*M+V*V;if(j>=N*N)return null;if(j===0){let U=Z-(z-O),G=z+O-Z,T=$-(J-Q),R=J+Q-$,H=Math.min(U,G,T,R);if(H===G)return{normalX:1,normalY:0,depth:G+N};if(H===U)return{normalX:-1,normalY:0,depth:U+N};if(H===R)return{normalX:0,normalY:1,depth:R+N};return{normalX:0,normalY:-1,depth:T+N}}let _=Math.sqrt(j);return{normalX:M/_,normalY:V/_,depth:N-_}}function S(z,J){if(z.aabb&&J.aabb)return Y(z.x,z.y,z.aabb.halfWidth,z.aabb.halfHeight,J.x,J.y,J.aabb.halfWidth,J.aabb.halfHeight);if(z.circle&&J.circle)return m(z.x,z.y,z.circle.radius,J.x,J.y,J.circle.radius);if(z.aabb&&J.circle)return q(z.x,z.y,z.aabb.halfWidth,z.aabb.halfHeight,J.x,J.y,J.circle.radius);if(z.circle&&J.aabb){let O=q(J.x,J.y,J.aabb.halfWidth,J.aabb.halfHeight,z.x,z.y,z.circle.radius);if(!O)return null;return{normalX:-O.normalX,normalY:-O.normalY,depth:O.depth}}return null}var K=new Set;function g(z,J,O,Q){if(J)x(z,J,O,Q);else w(z,O,Q)}function w(z,J,O){for(let Q=0;Q<z.length;Q++){let Z=z[Q];if(!Z)continue;for(let $=Q+1;$<z.length;$++){let N=z[$];if(!N)continue;if(!Z.collidesWith.includes(N.layer)&&!N.collidesWith.includes(Z.layer))continue;let F=S(Z,N);if(!F)continue;J(Z,N,F,O)}}}function x(z,J,O,Q){let Z=new Map;for(let $=0;$<z.length;$++){let N=z[$];if(!N)continue;Z.set(N.entityId,N)}for(let $=0;$<z.length;$++){let N=z[$];if(!N)continue;let F=N.aabb?N.aabb.halfWidth:N.circle?N.circle.radius:0,E=N.aabb?N.aabb.halfHeight:N.circle?N.circle.radius:0;K.clear(),J.queryRectInto(N.x-F,N.y-E,N.x+F,N.y+E,K);for(let M of K){if(M<=N.entityId)continue;let V=Z.get(M);if(!V)continue;if(!N.collidesWith.includes(V.layer)&&!V.collidesWith.includes(N.layer))continue;let j=S(N,V);if(!j)continue;O(N,V,j,Q)}}}function o(z,J,O,Q){let Z={width:z,height:J};if(O!==void 0)Z.offsetX=O;if(Q!==void 0)Z.offsetY=Q;return{aabbCollider:Z}}function c(z,J,O){let Q={radius:z};if(J!==void 0)Q.offsetX=J;if(O!==void 0)Q.offsetY=O;return{circleCollider:Q}}function u(z,J){return{collisionLayer:{layer:z,collidesWith:J}}}function t(z){let J={};for(let O of Object.keys(z)){let Q=z[O];J[O]=()=>u(O,Q)}return J}function I(z){let J=z.indexOf(":");if(J===-1)throw Error(`Invalid collision pair key "${z}": must contain a colon separator (e.g. "player:enemy")`);let O=z.slice(0,J),Q=z.slice(J+1);if(O===""||Q==="")throw Error(`Invalid collision pair key "${z}": layer names must not be empty`);return[O,Q]}function i(z){let J=new Map,O=new Set;for(let Q of Object.keys(z))I(Q),O.add(Q);for(let Q of Object.keys(z)){let[Z,$]=I(Q),N=z[Q];if(!N)continue;J.set(Q,{callback:N,swapped:!1});let F=`${$}:${Z}`;if(F!==Q&&!O.has(F))J.set(F,{callback:N,swapped:!0})}return function({data:Z,ecs:$}){let N=J.get(Z.layerA+":"+Z.layerB);if(!N)return;if(N.swapped)N.callback(Z.entityB,Z.entityA,$);else N.callback(Z.entityA,Z.entityB,$)}}function h(z,J,O,Q){Q.publish("collision",{entityA:z.entityId,entityB:J.entityId,layerA:z.layer,layerB:J.layer,normal:{x:O.normalX,y:O.normalY},depth:O.depth})}function a(z){let{systemGroup:J="physics",priority:O=0,phase:Q="postUpdate"}=z;return f({id:"collision",install(Z){Z.addSystem("collision-detection").setPriority(O).inPhase(Q).inGroup(J).addQuery("collidables",{with:["worldTransform","collisionLayer"]}).setProcess(({queries:$,ecs:N})=>{let F=[];for(let M of $.collidables){let{worldTransform:V,collisionLayer:j}=M.components,_=L(M.id,V.x,V.y,j.layer,j.collidesWith,N.getComponent(M.id,"aabbCollider"),N.getComponent(M.id,"circleCollider"));if(_)F.push(_)}let E=P(N.tryGetResource.bind(N));g(F,E,h,N.eventBus)})}})}export{t as defineCollisionLayers,a as createCollisionPlugin,i as createCollisionPairHandler,u as createCollisionLayer,c as createCircleCollider,o as createAABBCollider};
2
+
3
+ //# debugId=09F543A272CDB7BC64756E2164756E21
4
+ //# sourceMappingURL=collision.js.map
@@ -5,7 +5,7 @@
5
5
  "/**\n * Collision Plugin for ECSpresso\n *\n * Provides layer-based collision detection with events.\n * Uses worldTransform for position (world-space collision).\n * Supports AABB and circle colliders.\n */\n\nimport { definePlugin, type Plugin, type BasePluginOptions } from 'ecspresso';\nimport type { WorldConfigFrom } from '../type-utils';\nimport type { TransformWorldConfig } from './transform';\nimport { buildBaseColliderInfo, detectCollisions, tryGetSpatialIndex, type Contact, type BaseColliderInfo } from '../utils/narrowphase';\n\n// ==================== Component Types ====================\n\n/**\n * Axis-Aligned Bounding Box collider.\n */\nexport interface AABBCollider {\n\t/** Width of the bounding box */\n\twidth: number;\n\t/** Height of the bounding box */\n\theight: number;\n\t/** X offset from entity position (default: 0) */\n\toffsetX?: number;\n\t/** Y offset from entity position (default: 0) */\n\toffsetY?: number;\n}\n\n/**\n * Circle collider.\n */\nexport interface CircleCollider {\n\t/** Radius of the circle */\n\tradius: number;\n\t/** X offset from entity position (default: 0) */\n\toffsetX?: number;\n\t/** Y offset from entity position (default: 0) */\n\toffsetY?: number;\n}\n\n/**\n * Collision layer configuration.\n */\nexport interface CollisionLayer<L extends string = never> {\n\t/** The layer this entity belongs to */\n\tlayer: L;\n\t/** Layers this entity can collide with */\n\tcollidesWith: readonly L[];\n}\n\n/**\n * Component types provided by the collision plugin.\n * Included automatically via `.withPlugin(createCollisionPlugin())`.\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createCollisionPlugin())\n * .withComponentTypes<{ sprite: Sprite; enemy: boolean }>()\n * .build();\n * ```\n */\nexport interface CollisionComponentTypes<L extends string = never> {\n\taabbCollider: AABBCollider;\n\tcircleCollider: CircleCollider;\n\tcollisionLayer: CollisionLayer<L>;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Event fired when two entities collide.\n */\nexport interface CollisionEvent<L extends string = never> {\n\t/** First entity in the collision */\n\tentityA: number;\n\t/** Second entity in the collision */\n\tentityB: number;\n\t/** Layer of the first entity */\n\tlayerA: L;\n\t/** Layer of the second entity */\n\tlayerB: L;\n\t/** Contact normal pointing from entityA toward entityB */\n\tnormal: { x: number; y: number };\n\t/** Penetration depth (positive = overlapping) */\n\tdepth: number;\n}\n\n/**\n * Event types provided by the collision plugin.\n */\nexport interface CollisionEventTypes<L extends string = never> {\n\tcollision: CollisionEvent<L>;\n}\n\n// ==================== Plugin Options ====================\n\n/**\n * Configuration options for the collision plugin.\n */\nexport interface CollisionPluginOptions<G extends string = 'physics'> extends BasePluginOptions<G> {\n\t/** Name of the collision event (default: 'collision') */\n\tcollisionEventName?: string;\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Create an AABB collider component.\n *\n * @param width Width of the bounding box\n * @param height Height of the bounding box\n * @param offsetX X offset from entity position\n * @param offsetY Y offset from entity position\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createAABBCollider(50, 30),\n * });\n * ```\n */\nexport function createAABBCollider(\n\twidth: number,\n\theight: number,\n\toffsetX?: number,\n\toffsetY?: number\n): { aabbCollider: AABBCollider } {\n\tconst collider: AABBCollider = { width, height };\n\tif (offsetX !== undefined) collider.offsetX = offsetX;\n\tif (offsetY !== undefined) collider.offsetY = offsetY;\n\treturn { aabbCollider: collider };\n}\n\n/**\n * Create a circle collider component.\n *\n * @param radius Radius of the circle\n * @param offsetX X offset from entity position\n * @param offsetY Y offset from entity position\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createCircleCollider(25),\n * });\n * ```\n */\nexport function createCircleCollider(\n\tradius: number,\n\toffsetX?: number,\n\toffsetY?: number\n): { circleCollider: CircleCollider } {\n\tconst collider: CircleCollider = { radius };\n\tif (offsetX !== undefined) collider.offsetX = offsetX;\n\tif (offsetY !== undefined) collider.offsetY = offsetY;\n\treturn { circleCollider: collider };\n}\n\n/**\n * Create a collision layer component.\n *\n * @param layer The layer this entity belongs to\n * @param collidesWith Layers this entity can collide with\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createAABBCollider(50, 30),\n * ...createCollisionLayer('player', ['enemy', 'obstacle']),\n * });\n * ```\n */\nexport function createCollisionLayer<L extends string>(\n\tlayer: L,\n\tcollidesWith: readonly L[]\n): Pick<CollisionComponentTypes<L>, 'collisionLayer'> {\n\treturn {\n\t\tcollisionLayer: { layer, collidesWith },\n\t};\n}\n\n/**\n * Layer factory result from defineCollisionLayers.\n */\nexport type LayerFactories<T extends Record<string, readonly string[]>> = {\n\t[K in keyof T]: () => Pick<CollisionComponentTypes<Extract<keyof T, string>>, 'collisionLayer'>;\n};\n\n/**\n * Extract layer names from a `defineCollisionLayers` result for use with\n * `createCollisionPairHandler`'s `L` type parameter.\n *\n * @example\n * ```typescript\n * const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });\n * type Layer = LayersOf<typeof layers>;\n * const handler = createCollisionPairHandler<ECS, Layer>({\n * 'player:enemy': (playerId, enemyId, ecs) => { ... },\n * });\n * ```\n */\nexport type LayersOf<T> = Extract<keyof T, string>;\n\n/**\n * Define collision layer relationships and get factory functions.\n *\n * @param rules Object mapping layer names to arrays of layers they collide with\n * @returns Object with factory functions for each layer\n *\n * @example\n * ```typescript\n * const layers = defineCollisionLayers({\n * player: ['enemy', 'enemyProjectile'],\n * playerProjectile: ['enemy'],\n * enemy: ['playerProjectile'],\n * enemyProjectile: ['player'],\n * });\n *\n * // Usage\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createAABBCollider(50, 30),\n * ...layers.player(),\n * });\n * ```\n */\n/**\n * Validates that all `collidesWith` values reference actual layer keys.\n * Catches typos at compile time.\n */\ntype ValidateCollidesWith<T> = {\n\t[K in keyof T]: T[K] extends readonly (infer V)[]\n\t\t? [V] extends [Extract<keyof T, string>] ? T[K] : readonly Extract<keyof T, string>[]\n\t\t: never;\n};\n\nexport function defineCollisionLayers<const T extends Record<string, readonly string[]>>(\n\trules: T & ValidateCollidesWith<T>\n): LayerFactories<T> {\n\ttype L = Extract<keyof T, string>;\n\tconst factories = {} as LayerFactories<T>;\n\n\tfor (const layer of Object.keys(rules) as Array<L>) {\n\t\tconst collidesWith = rules[layer] as readonly L[];\n\t\tfactories[layer] = () => createCollisionLayer<L>(layer, collidesWith);\n\t}\n\n\treturn factories;\n}\n\n// ==================== Collision Pair Handler ====================\n\n/**\n * Callback for a collision pair handler.\n *\n * @param firstEntityId Entity belonging to the first layer in the pair key\n * @param secondEntityId Entity belonging to the second layer in the pair key\n * @param ecs The ECS world instance (passed through from the subscriber)\n */\nexport type CollisionPairCallback<W = unknown> = (\n\tfirstEntityId: number,\n\tsecondEntityId: number,\n\tecs: W,\n) => void;\n\ninterface PairEntry<W> {\n\tcallback: CollisionPairCallback<W>;\n\tswapped: boolean;\n}\n\nfunction parsePairKey(key: string): [string, string] {\n\tconst colonIndex = key.indexOf(':');\n\tif (colonIndex === -1) {\n\t\tthrow new Error(`Invalid collision pair key \"${key}\": must contain a colon separator (e.g. \"player:enemy\")`);\n\t}\n\tconst layerA = key.slice(0, colonIndex);\n\tconst layerB = key.slice(colonIndex + 1);\n\tif (layerA === '' || layerB === '') {\n\t\tthrow new Error(`Invalid collision pair key \"${key}\": layer names must not be empty`);\n\t}\n\treturn [layerA, layerB];\n}\n\n/**\n * Create a collision pair handler that routes collision events to\n * layer-pair-specific callbacks.\n *\n * Registering `\"a:b\"` automatically handles both `(layerA=a, layerB=b)` and\n * `(layerA=b, layerB=a)`. Entity arguments are swapped to match the declared\n * key order. If both `\"a:b\"` and `\"b:a\"` are explicitly registered, each gets\n * its own handler with no implicit reverse.\n *\n * @typeParam W - The ECS world type (e.g. `ECSpresso<C, E, R>`). Defaults to `unknown`.\n * @typeParam L - Union of valid layer names. Defaults to `string`.\n * Provide specific layer names for compile-time key validation:\n * `createCollisionPairHandler<ECS, keyof typeof layers>({...})`\n *\n * @param pairs Object mapping `\"layerA:layerB\"` keys to callbacks\n * @returns A dispatch function to call with collision event data and ECS instance\n *\n * @example\n * ```typescript\n * // Basic usage:\n * const handler = createCollisionPairHandler<ECS>({\n * 'playerProjectile:enemy': (projectileId, enemyId, ecs) => {\n * ecs.commands.removeEntity(projectileId);\n * },\n * });\n *\n * // With layer name validation:\n * const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });\n * type Layer = LayersOf<typeof layers>;\n * const handler = createCollisionPairHandler<ECS, Layer>({\n * 'player:enemy': (playerId, enemyId, ecs) => { ... },\n * });\n *\n * ecs.eventBus.subscribe('collision', (data) => handler({ data, ecs }));\n * ```\n */\nexport function createCollisionPairHandler<W = unknown, L extends string = string>(\n\tpairs: { [K in `${L}:${L}`]?: CollisionPairCallback<W> }\n): (ctx: { data: CollisionEvent<L>; ecs: W }) => void;\nexport function createCollisionPairHandler<W = unknown>(\n\tpairs: Record<string, CollisionPairCallback<W> | undefined>\n): (ctx: { data: CollisionEvent<string>; ecs: W }) => void {\n\tconst lookup = new Map<string, PairEntry<W>>();\n\tconst explicitKeys = new Set<string>();\n\n\t// First pass: collect all explicit keys\n\tfor (const key of Object.keys(pairs)) {\n\t\tparsePairKey(key); // validate\n\t\texplicitKeys.add(key);\n\t}\n\n\t// Second pass: build lookup with forward + conditional reverse entries\n\tfor (const key of Object.keys(pairs)) {\n\t\tconst [layerA, layerB] = parsePairKey(key);\n\t\tconst callback = pairs[key];\n\t\tif (!callback) continue;\n\n\t\t// Forward entry\n\t\tlookup.set(key, { callback, swapped: false });\n\n\t\t// Reverse entry (only if the reverse key wasn't explicitly registered\n\t\t// and it's not a self-collision where forward === reverse)\n\t\tconst reverseKey = `${layerB}:${layerA}`;\n\t\tif (reverseKey !== key && !explicitKeys.has(reverseKey)) {\n\t\t\tlookup.set(reverseKey, { callback, swapped: true });\n\t\t}\n\t}\n\n\treturn function collisionPairDispatch({ data: event, ecs }: { data: CollisionEvent<string>; ecs: W }): void {\n\t\tconst entry = lookup.get(event.layerA + ':' + event.layerB);\n\t\tif (!entry) return;\n\n\t\tif (entry.swapped) {\n\t\t\tentry.callback(event.entityB, event.entityA, ecs);\n\t\t} else {\n\t\t\tentry.callback(event.entityA, event.entityB, ecs);\n\t\t}\n\t};\n}\n\n// ==================== Dependency Types ====================\n\n// ==================== Module-level Collision Callback ====================\n\ninterface CollisionEventBus<L extends string> {\n\tpublish(event: 'collision', data: CollisionEvent<L>): void;\n}\n\nfunction onCollisionDetected<L extends string>(\n\ta: BaseColliderInfo<L>,\n\tb: BaseColliderInfo<L>,\n\tcontact: Contact,\n\teventBus: CollisionEventBus<L>,\n): void {\n\teventBus.publish('collision', {\n\t\tentityA: a.entityId,\n\t\tentityB: b.entityId,\n\t\tlayerA: a.layer,\n\t\tlayerB: b.layer,\n\t\tnormal: { x: contact.normalX, y: contact.normalY },\n\t\tdepth: contact.depth,\n\t});\n}\n\n// ==================== Plugin Factory ====================\n\n/**\n * Create a collision plugin for ECSpresso.\n *\n * This plugin provides:\n * - Collision detection between entities with colliders\n * - AABB-AABB, circle-circle, and AABB-circle collision\n * - Layer-based filtering for collision pairs\n * - Deduplication of A-B / B-A collisions\n * - Automatic broadphase acceleration when spatialIndex resource is present\n *\n * Uses worldTransform for position (world-space collision detection).\n * The `layers` parameter is required for type inference — at runtime the\n * plugin does not consume it.\n *\n * @example\n * ```typescript\n * const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });\n * const ecs = ECSpresso\n * .create()\n * .withPlugin(createTransformPlugin())\n * .withPlugin(createCollisionPlugin({ layers }))\n * .build();\n *\n * // Entity with collision\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createAABBCollider(50, 30),\n * ...layers.player(),\n * });\n * ```\n */\nexport function createCollisionPlugin<L extends string, G extends string = 'physics'>(\n\toptions: CollisionPluginOptions<G> & { layers: LayerFactories<Record<L, readonly string[]>> }\n): Plugin<WorldConfigFrom<CollisionComponentTypes<L>, CollisionEventTypes<L>>, TransformWorldConfig, 'collision-detection', G> {\n\tconst {\n\t\tsystemGroup = 'physics',\n\t\tpriority = 0,\n\t\tphase = 'postUpdate',\n\t} = options;\n\n\treturn definePlugin<WorldConfigFrom<CollisionComponentTypes<L>, CollisionEventTypes<L>>, TransformWorldConfig, 'collision-detection', G>({\n\t\tid: 'collision',\n\t\tinstall(world) {\n\t\t\tworld\n\t\t\t\t.addSystem('collision-detection')\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('collidables', {\n\t\t\t\t\twith: ['worldTransform', 'collisionLayer'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, ecs }) => {\n\t\t\t\t\tconst colliders: BaseColliderInfo<L>[] = [];\n\n\t\t\t\t\tfor (const entity of queries.collidables) {\n\t\t\t\t\t\tconst { worldTransform, collisionLayer } = entity.components;\n\t\t\t\t\t\tconst info = buildBaseColliderInfo(\n\t\t\t\t\t\t\tentity.id, worldTransform.x, worldTransform.y,\n\t\t\t\t\t\t\tcollisionLayer.layer, collisionLayer.collidesWith,\n\t\t\t\t\t\t\tecs.getComponent(entity.id, 'aabbCollider'),\n\t\t\t\t\t\t\tecs.getComponent(entity.id, 'circleCollider'),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (info) colliders.push(info);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst si = tryGetSpatialIndex(ecs.tryGetResource.bind(ecs));\n\t\t\t\t\tdetectCollisions(colliders, si, onCollisionDetected<L>, ecs.eventBus);\n\t\t\t\t});\n\t\t},\n\t});\n}\n\n",
6
6
  "/**\n * Shared Narrowphase Module\n *\n * Provides contact-computing narrowphase tests and a generic collision\n * iteration pipeline used by both the collision plugin (event-only) and\n * the physics2D plugin (impulse response).\n */\n\nimport type { SpatialIndex } from './spatial-hash';\n\n// ==================== Contact ====================\n\n/** Contact result from a narrowphase test. Normal points from A toward B. */\nexport interface Contact {\n\tnormalX: number;\n\tnormalY: number;\n\t/** Penetration depth (positive = overlapping) */\n\tdepth: number;\n}\n\n// ==================== BaseColliderInfo ====================\n\n/** Minimum collider data shared by collision and physics bundles. */\nexport interface BaseColliderInfo<L extends string = string> {\n\tentityId: number;\n\tx: number;\n\ty: number;\n\tlayer: L;\n\tcollidesWith: readonly L[];\n\taabb?: { halfWidth: number; halfHeight: number };\n\tcircle?: { radius: number };\n}\n\n// ==================== Collider Construction ====================\n\n/**\n * Build a BaseColliderInfo from raw entity/collider component data.\n * Returns null if the entity has neither an AABB nor circle collider.\n * Shared by collision plugin (event-only) and physics2D plugin (impulse response).\n */\nexport function buildBaseColliderInfo<L extends string>(\n\tentityId: number,\n\tx: number,\n\ty: number,\n\tlayer: L,\n\tcollidesWith: readonly L[],\n\taabb: { width: number; height: number; offsetX?: number; offsetY?: number } | undefined,\n\tcircle: { radius: number; offsetX?: number; offsetY?: number } | undefined,\n): BaseColliderInfo<L> | null {\n\tif (!aabb && !circle) return null;\n\n\tconst info: BaseColliderInfo<L> = { entityId, x, y, layer, collidesWith };\n\n\tif (aabb) {\n\t\tinfo.x += aabb.offsetX ?? 0;\n\t\tinfo.y += aabb.offsetY ?? 0;\n\t\tinfo.aabb = { halfWidth: aabb.width / 2, halfHeight: aabb.height / 2 };\n\t}\n\n\tif (circle) {\n\t\tinfo.x += circle.offsetX ?? 0;\n\t\tinfo.y += circle.offsetY ?? 0;\n\t\tinfo.circle = { radius: circle.radius };\n\t}\n\n\treturn info;\n}\n\n// ==================== Spatial Index Lookup ====================\n\n/**\n * Retrieve the optional spatialIndex resource, returning undefined when absent.\n * Centralizes the cross-plugin typed lookup so individual plugins don't each\n * need to import SpatialIndex or repeat the tryGetResource pattern.\n */\nexport function tryGetSpatialIndex(\n\ttryGetResource: <T>(key: string) => T | undefined,\n): SpatialIndex | undefined {\n\treturn tryGetResource<SpatialIndex>('spatialIndex');\n}\n\n// ==================== Narrowphase Tests ====================\n\nexport function computeAABBvsAABB(\n\tax: number, ay: number, ahw: number, ahh: number,\n\tbx: number, by: number, bhw: number, bhh: number,\n): Contact | null {\n\tconst dx = bx - ax;\n\tconst dy = by - ay;\n\tconst overlapX = (ahw + bhw) - Math.abs(dx);\n\tconst overlapY = (ahh + bhh) - Math.abs(dy);\n\n\tif (overlapX <= 0 || overlapY <= 0) return null;\n\n\tif (overlapX < overlapY) {\n\t\treturn {\n\t\t\tnormalX: dx >= 0 ? 1 : -1,\n\t\t\tnormalY: 0,\n\t\t\tdepth: overlapX,\n\t\t};\n\t}\n\treturn {\n\t\tnormalX: 0,\n\t\tnormalY: dy >= 0 ? 1 : -1,\n\t\tdepth: overlapY,\n\t};\n}\n\nexport function computeCircleVsCircle(\n\tax: number, ay: number, ar: number,\n\tbx: number, by: number, br: number,\n): Contact | null {\n\tconst dx = bx - ax;\n\tconst dy = by - ay;\n\tconst distSq = dx * dx + dy * dy;\n\tconst radiusSum = ar + br;\n\n\tif (distSq >= radiusSum * radiusSum) return null;\n\n\tconst dist = Math.sqrt(distSq);\n\tif (dist === 0) {\n\t\treturn { normalX: 1, normalY: 0, depth: radiusSum };\n\t}\n\treturn {\n\t\tnormalX: dx / dist,\n\t\tnormalY: dy / dist,\n\t\tdepth: radiusSum - dist,\n\t};\n}\n\nexport function computeAABBvsCircle(\n\taabbX: number, aabbY: number, ahw: number, ahh: number,\n\tcircleX: number, circleY: number, radius: number,\n): Contact | null {\n\tconst closestX = Math.max(aabbX - ahw, Math.min(circleX, aabbX + ahw));\n\tconst closestY = Math.max(aabbY - ahh, Math.min(circleY, aabbY + ahh));\n\n\tconst dx = circleX - closestX;\n\tconst dy = circleY - closestY;\n\tconst distSq = dx * dx + dy * dy;\n\n\tif (distSq >= radius * radius) return null;\n\n\t// Circle center inside AABB\n\tif (distSq === 0) {\n\t\tconst pushLeft = (circleX - (aabbX - ahw));\n\t\tconst pushRight = ((aabbX + ahw) - circleX);\n\t\tconst pushUp = (circleY - (aabbY - ahh));\n\t\tconst pushDown = ((aabbY + ahh) - circleY);\n\t\tconst minPush = Math.min(pushLeft, pushRight, pushUp, pushDown);\n\n\t\tif (minPush === pushRight) return { normalX: 1, normalY: 0, depth: pushRight + radius };\n\t\tif (minPush === pushLeft) return { normalX: -1, normalY: 0, depth: pushLeft + radius };\n\t\tif (minPush === pushDown) return { normalX: 0, normalY: 1, depth: pushDown + radius };\n\t\treturn { normalX: 0, normalY: -1, depth: pushUp + radius };\n\t}\n\n\tconst dist = Math.sqrt(distSq);\n\treturn {\n\t\tnormalX: dx / dist,\n\t\tnormalY: dy / dist,\n\t\tdepth: radius - dist,\n\t};\n}\n\n// ==================== Contact Dispatcher ====================\n\nexport function computeContact(a: BaseColliderInfo, b: BaseColliderInfo): Contact | null {\n\tif (a.aabb && b.aabb) {\n\t\treturn computeAABBvsAABB(\n\t\t\ta.x, a.y, a.aabb.halfWidth, a.aabb.halfHeight,\n\t\t\tb.x, b.y, b.aabb.halfWidth, b.aabb.halfHeight,\n\t\t);\n\t}\n\n\tif (a.circle && b.circle) {\n\t\treturn computeCircleVsCircle(\n\t\t\ta.x, a.y, a.circle.radius,\n\t\t\tb.x, b.y, b.circle.radius,\n\t\t);\n\t}\n\n\tif (a.aabb && b.circle) {\n\t\treturn computeAABBvsCircle(\n\t\t\ta.x, a.y, a.aabb.halfWidth, a.aabb.halfHeight,\n\t\t\tb.x, b.y, b.circle.radius,\n\t\t);\n\t}\n\n\tif (a.circle && b.aabb) {\n\t\tconst contact = computeAABBvsCircle(\n\t\t\tb.x, b.y, b.aabb.halfWidth, b.aabb.halfHeight,\n\t\t\ta.x, a.y, a.circle.radius,\n\t\t);\n\t\tif (!contact) return null;\n\t\treturn {\n\t\t\tnormalX: -contact.normalX,\n\t\t\tnormalY: -contact.normalY,\n\t\t\tdepth: contact.depth,\n\t\t};\n\t}\n\n\treturn null;\n}\n\n// ==================== Collision Iteration ====================\n\n/** Module-level reusable set for broadphase candidates. */\nconst _broadphaseCandidates = new Set<number>();\n\n/**\n * Generic collision detection pipeline: brute-force or broadphase,\n * with layer filtering and contact computation.\n *\n * Uses a context parameter forwarded to the callback to avoid\n * per-frame closure allocation.\n */\nexport function detectCollisions<I extends BaseColliderInfo, C>(\n\tcolliders: I[],\n\tspatialIndex: SpatialIndex | undefined,\n\tonContact: (a: I, b: I, contact: Contact, context: C) => void,\n\tcontext: C,\n): void {\n\tif (spatialIndex) {\n\t\tbroadphaseDetect(colliders, spatialIndex, onContact, context);\n\t} else {\n\t\tbruteForceDetect(colliders, onContact, context);\n\t}\n}\n\nfunction bruteForceDetect<I extends BaseColliderInfo, C>(\n\tcolliders: I[],\n\tonContact: (a: I, b: I, contact: Contact, context: C) => void,\n\tcontext: C,\n): void {\n\tfor (let i = 0; i < colliders.length; i++) {\n\t\tconst a = colliders[i];\n\t\tif (!a) continue;\n\n\t\tfor (let j = i + 1; j < colliders.length; j++) {\n\t\t\tconst b = colliders[j];\n\t\t\tif (!b) continue;\n\n\t\t\tif (!a.collidesWith.includes(b.layer) && !b.collidesWith.includes(a.layer)) continue;\n\n\t\t\tconst contact = computeContact(a, b);\n\t\t\tif (!contact) continue;\n\n\t\t\tonContact(a, b, contact, context);\n\t\t}\n\t}\n}\n\nfunction broadphaseDetect<I extends BaseColliderInfo, C>(\n\tcolliders: I[],\n\tspatialIndex: SpatialIndex,\n\tonContact: (a: I, b: I, contact: Contact, context: C) => void,\n\tcontext: C,\n): void {\n\tconst colliderMap = new Map<number, I>();\n\tfor (let i = 0; i < colliders.length; i++) {\n\t\tconst c = colliders[i];\n\t\tif (!c) continue;\n\t\tcolliderMap.set(c.entityId, c);\n\t}\n\n\tfor (let i = 0; i < colliders.length; i++) {\n\t\tconst a = colliders[i];\n\t\tif (!a) continue;\n\n\t\tconst aHalfW = a.aabb ? a.aabb.halfWidth : (a.circle ? a.circle.radius : 0);\n\t\tconst aHalfH = a.aabb ? a.aabb.halfHeight : (a.circle ? a.circle.radius : 0);\n\n\t\t_broadphaseCandidates.clear();\n\t\tspatialIndex.queryRectInto(\n\t\t\ta.x - aHalfW, a.y - aHalfH,\n\t\t\ta.x + aHalfW, a.y + aHalfH,\n\t\t\t_broadphaseCandidates,\n\t\t);\n\n\t\tfor (const bId of _broadphaseCandidates) {\n\t\t\tif (bId <= a.entityId) continue;\n\n\t\t\tconst b = colliderMap.get(bId);\n\t\t\tif (!b) continue;\n\n\t\t\tif (!a.collidesWith.includes(b.layer) && !b.collidesWith.includes(a.layer)) continue;\n\n\t\t\tconst contact = computeContact(a, b);\n\t\t\tif (!contact) continue;\n\n\t\t\tonContact(a, b, contact, context);\n\t\t}\n\t}\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "BC18D3BFF907B0AC64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "09F543A272CDB7BC64756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -0,0 +1,4 @@
1
+ var{defineProperty:N,getOwnPropertyNames:U,getOwnPropertyDescriptor:V}=Object,W=Object.prototype.hasOwnProperty;function X(j){return this[j]}var C=(j)=>{var x=(S??=new WeakMap).get(j),z;if(x)return x;if(x=N({},"__esModule",{value:!0}),j&&typeof j==="object"||typeof j==="function"){for(var D of U(j))if(!W.call(x,D))N(x,D,{get:X.bind(j,D),enumerable:!(z=V(j,D))||z.enumerable})}return S.set(j,x),x},S;var Y=(j)=>j;function Z(j,x){this[j]=Y.bind(null,x)}var E=(j,x)=>{for(var z in x)N(j,z,{get:x[z],enumerable:!0,configurable:!0,set:Z.bind(x,z)})};var F=(j,x)=>()=>(j&&(x=j(j=0)),x);var G=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(x,z)=>(typeof require<"u"?require:x)[z]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});import{definePlugin as _}from"ecspresso";function $(j,x){return{coroutine:{generator:j,onComplete:x?.onComplete}}}function*q(j){if(j<=0)return;let x=0;while(x<j){let z=yield;x+=z}}function*B(j){for(let x=0;x<j;x++)yield}function*b(j){while(!j())yield}function*T(...j){if(j.length===0)return;let x=j.map((z)=>{return z.next(0),{gen:z,done:!1}});while(x.some((z)=>!z.done)){let z=yield;for(let D of x){if(D.done)continue;if(D.gen.next(z).done)D.done=!0}}}function*w(...j){if(j.length===0)return;let x=j.map((z)=>{return z.next(0),{gen:z,done:!1}});try{while(!0){let z=yield;for(let D of x){if(D.done)continue;if(D.gen.next(z).done){D.done=!0;for(let K of x)if(!K.done)K.gen.return(),K.done=!0;return}}}}finally{for(let z of x)if(!z.done)z.gen.return(),z.done=!0}}function*A(j,x,z){let D=!1,H=j.subscribe(x,(K)=>{if(!z||z(K))D=!0});try{while(!D)yield}finally{H()}}function I(j,x){let z=j.getComponent(x,"coroutine");if(!z)return!1;return z.generator.return(),j.commands.removeComponent(x,"coroutine"),!0}function m(j){return{createCoroutine:$,waitForEvent:A}}function h(j){let{systemGroup:x="coroutines",priority:z=0,phase:D="update"}=j??{},H=new Set;return _({id:"coroutines",install(K){K.registerDispose("coroutine",({value:L,entityId:M})=>{L.generator.return(),H.delete(M)}),K.addSystem("coroutine-update").setPriority(z).inPhase(D).inGroup(x).addQuery("coroutines",{with:["coroutine"]}).setOnEntityEnter("coroutines",({entity:L})=>{L.components.coroutine.generator.next(0)}).setProcess(({queries:L,dt:M,ecs:O})=>{for(let J of L.coroutines){if(H.has(J.id)){H.delete(J.id);continue}let Q=J.components.coroutine;try{if(Q.generator.next(M).done)H.add(J.id),Q.onComplete?.({entityId:J.id}),O.commands.removeComponent(J.id,"coroutine")}catch(R){console.warn(`Coroutine error on entity ${J.id}:`,R),H.add(J.id),O.commands.removeComponent(J.id,"coroutine")}}})}})}export{b as waitUntil,q as waitSeconds,B as waitFrames,A as waitForEvent,w as race,T as parallel,h as createCoroutinePlugin,m as createCoroutineHelpers,$ as createCoroutine,I as cancelCoroutine};
2
+
3
+ //# debugId=4FBB31C8317254FC64756E2164756E21
4
+ //# sourceMappingURL=coroutine.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * Coroutine Plugin for ECSpresso\n *\n * ES6 generator-based coroutines for multi-step, frame-spanning scripted sequences.\n * A `coroutine` component holds a live generator. A system ticks all generators each\n * frame via `.next(dt)`. Helper generators (`waitSeconds`, `waitFrames`, `waitUntil`,\n * `waitForEvent`, `parallel`, `race`) compose via `yield*`.\n */\n\nimport { definePlugin, type Plugin, type BasePluginOptions } from 'ecspresso';\nimport type { EventsOfWorld, AnyECSpresso } from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../type-utils';\n\n// ==================== Generator Protocol ====================\n\n/**\n * Yields void, returns void, receives deltaTime (number) via `.next(dt)`.\n * First `.next(dt)` initializes the generator (runs to first yield, dt discarded per JS spec).\n * Subsequent `.next(dt)` resume from yield with dt as the yield expression value.\n */\nexport type CoroutineGenerator = Generator<void, void, number>;\n\n// ==================== Event Types ====================\n\nexport interface CoroutineEventData {\n\tentityId: number;\n}\n\n\n// ==================== Component Types ====================\n\nexport interface CoroutineState {\n\tgenerator: CoroutineGenerator;\n\tonComplete?: (data: CoroutineEventData) => void;\n}\n\nexport interface CoroutineComponentTypes {\n\tcoroutine: CoroutineState;\n}\n\n// ==================== Plugin Options ====================\n\nexport interface CoroutinePluginOptions<G extends string = 'coroutines'> extends BasePluginOptions<G> {}\n\n// ==================== Component Factory ====================\n\nexport interface CoroutineOptions {\n\tonComplete?: (data: CoroutineEventData) => void;\n}\n\n/**\n * Create a coroutine component for spawning or adding to an entity.\n *\n * @param generator - The generator function to drive\n * @param options - Optional configuration (onComplete event)\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createCoroutine(\n\tgenerator: CoroutineGenerator,\n\toptions?: CoroutineOptions,\n): Pick<CoroutineComponentTypes, 'coroutine'> {\n\treturn {\n\t\tcoroutine: {\n\t\t\tgenerator,\n\t\t\tonComplete: options?.onComplete,\n\t\t},\n\t};\n}\n\n// ==================== Helper Generators (standalone) ====================\n\n/**\n * Wait for a specified number of seconds. Accumulates dt until elapsed >= seconds.\n * If seconds <= 0, returns immediately.\n */\nexport function* waitSeconds(seconds: number): CoroutineGenerator {\n\tif (seconds <= 0) return;\n\tlet elapsed = 0;\n\twhile (elapsed < seconds) {\n\t\tconst dt: number = yield;\n\t\telapsed += dt;\n\t}\n}\n\n/**\n * Wait for a specified number of frames. Yields `frames` times.\n * If frames <= 0, returns immediately.\n */\nexport function* waitFrames(frames: number): CoroutineGenerator {\n\tfor (let i = 0; i < frames; i++) {\n\t\tyield;\n\t}\n}\n\n/**\n * Wait until a predicate returns true. Yields each frame until predicate is satisfied.\n * User closes over ecs if needed for state checks.\n */\nexport function* waitUntil(predicate: () => boolean): CoroutineGenerator {\n\twhile (!predicate()) {\n\t\tyield;\n\t}\n}\n\n/**\n * Run multiple coroutines in parallel. Completes when all finish.\n * Initializes all sub-generators, ticks all each frame.\n * Empty array = immediate return.\n */\nexport function* parallel(...coroutines: CoroutineGenerator[]): CoroutineGenerator {\n\tif (coroutines.length === 0) return;\n\n\t// Initialize all generators\n\tconst active = coroutines.map(gen => {\n\t\tgen.next(0);\n\t\treturn { gen, done: false };\n\t});\n\n\twhile (active.some(entry => !entry.done)) {\n\t\tconst dt: number = yield;\n\t\tfor (const entry of active) {\n\t\t\tif (entry.done) continue;\n\t\t\tconst result = entry.gen.next(dt);\n\t\t\tif (result.done) {\n\t\t\t\tentry.done = true;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Run multiple coroutines, completing when the first one finishes.\n * Calls `.return()` on remaining generators (triggers finally blocks).\n * Empty array = immediate return.\n */\nexport function* race(...coroutines: CoroutineGenerator[]): CoroutineGenerator {\n\tif (coroutines.length === 0) return;\n\n\t// Initialize all generators\n\tconst entries = coroutines.map(gen => {\n\t\tgen.next(0);\n\t\treturn { gen, done: false };\n\t});\n\n\ttry {\n\t\twhile (true) {\n\t\t\tconst dt: number = yield;\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.done) continue;\n\t\t\t\tconst result = entry.gen.next(dt);\n\t\t\t\tif (result.done) {\n\t\t\t\t\tentry.done = true;\n\t\t\t\t\t// Cancel all others\n\t\t\t\t\tfor (const other of entries) {\n\t\t\t\t\t\tif (!other.done) {\n\t\t\t\t\t\t\tother.gen.return();\n\t\t\t\t\t\t\tother.done = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} finally {\n\t\t// Clean up all on external cancellation\n\t\tfor (const entry of entries) {\n\t\t\tif (!entry.done) {\n\t\t\t\tentry.gen.return();\n\t\t\t\tentry.done = true;\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ==================== Helper Generator (ECS-dependent) ====================\n\n/**\n * Wait until a matching event fires on the event bus.\n * Subscribes via eventBus.subscribe, yields until event received, unsubscribes in finally block.\n *\n * @param eventBus - Object with subscribe method (typically ecs.eventBus)\n * @param eventType - Event type name to listen for\n * @param filter - Optional predicate to filter events\n */\nexport function* waitForEvent<ET extends Record<string, any>, E extends keyof ET & string>(\n\teventBus: { subscribe(type: E, cb: (data: ET[E]) => void): () => void },\n\teventType: E,\n\tfilter?: (data: ET[E]) => boolean,\n): CoroutineGenerator {\n\tlet received = false;\n\tconst unsubscribe = eventBus.subscribe(eventType, (data: ET[E]) => {\n\t\tif (!filter || filter(data)) {\n\t\t\treceived = true;\n\t\t}\n\t});\n\ttry {\n\t\twhile (!received) {\n\t\t\tyield;\n\t\t}\n\t} finally {\n\t\tunsubscribe();\n\t}\n}\n\n// ==================== Cancellation ====================\n\n/**\n * Structural interface for ECS methods used by cancelCoroutine.\n */\nexport interface CoroutineWorld {\n\tgetComponent(entityId: number, componentName: string): unknown | undefined;\n\tcommands: {\n\t\tremoveComponent(entityId: number, componentName: string): void;\n\t};\n}\n\n/**\n * Cancel a running coroutine on an entity. Calls generator.return() (triggers finally blocks)\n * and queues component removal.\n *\n * @returns true if the entity had a coroutine that was cancelled, false otherwise\n */\nexport function cancelCoroutine(ecs: CoroutineWorld, entityId: number): boolean {\n\tconst state = ecs.getComponent(entityId, 'coroutine') as CoroutineState | undefined;\n\tif (!state) return false;\n\tstate.generator.return();\n\tecs.commands.removeComponent(entityId, 'coroutine');\n\treturn true;\n}\n\n// ==================== Typed Helpers (replaces Kit Pattern) ====================\n\n/**\n * Type-safe coroutine helpers that validate event names against a world's event types.\n * Use `createCoroutineHelpers<typeof ecs>()` to get compile-time validation.\n */\nexport interface CoroutineHelpers<W extends AnyECSpresso> {\n\tcreateCoroutine: (\n\t\tgenerator: CoroutineGenerator,\n\t\toptions?: { onComplete?: (data: CoroutineEventData) => void },\n\t) => Pick<CoroutineComponentTypes, 'coroutine'>;\n\twaitForEvent: <E extends keyof EventsOfWorld<W> & string>(\n\t\teventBus: { subscribe(type: E, cb: (data: EventsOfWorld<W>[E]) => void): () => void },\n\t\teventType: E,\n\t\tfilter?: (data: EventsOfWorld<W>[E]) => boolean,\n\t) => CoroutineGenerator;\n}\n\n/**\n * Create typed coroutine helpers that validate event names at compile time.\n *\n * @example\n * ```typescript\n * const { createCoroutine, waitForEvent } = createCoroutineHelpers<typeof ecs>();\n * ecs.spawn({ ...createCoroutine(myGen(), { onComplete: (data) => console.log(data.entityId) }) });\n * ```\n */\nexport function createCoroutineHelpers<W extends AnyECSpresso>(_world?: W): CoroutineHelpers<W> {\n\treturn {\n\t\tcreateCoroutine: createCoroutine as CoroutineHelpers<W>['createCoroutine'],\n\t\twaitForEvent: waitForEvent as CoroutineHelpers<W>['waitForEvent'],\n\t};\n}\n\n// ==================== Plugin Factory ====================\n\n/**\n * Create a coroutine plugin for ECSpresso.\n *\n * This plugin provides:\n * - Coroutine system that ticks all generator-based coroutines each frame\n * - Automatic cleanup via dispose callback (triggers generator finally blocks)\n * - `onComplete` callback invocation\n * - Component removal on completion\n */\nexport function createCoroutinePlugin<G extends string = 'coroutines'>(\n\toptions?: CoroutinePluginOptions<G>,\n): Plugin<WorldConfigFrom<CoroutineComponentTypes>, EmptyConfig, 'coroutine-update', G> {\n\tconst {\n\t\tsystemGroup = 'coroutines',\n\t\tpriority = 0,\n\t\tphase = 'update',\n\t} = options ?? {};\n\n\t// Tracks entities whose coroutine completed this frame to prevent re-ticking\n\t// before the command buffer removes the component.\n\tconst finished = new Set<number>();\n\n\treturn definePlugin<WorldConfigFrom<CoroutineComponentTypes>, EmptyConfig, 'coroutine-update', G>({\n\t\tid: 'coroutines',\n\t\tinstall(world) {\n\t\t\tworld.registerDispose('coroutine', ({ value, entityId }) => {\n\t\t\t\tvalue.generator.return();\n\t\t\t\tfinished.delete(entityId);\n\t\t\t});\n\n\t\t\tworld\n\t\t\t\t.addSystem('coroutine-update')\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('coroutines', {\n\t\t\t\t\twith: ['coroutine'],\n\t\t\t\t})\n\t\t\t\t.setOnEntityEnter('coroutines', ({ entity }) => {\n\t\t\t\t\tentity.components.coroutine.generator.next(0);\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, dt, ecs }) => {\n\t\t\t\t\tfor (const entity of queries.coroutines) {\n\t\t\t\t\t\t// Already completed — skip until command buffer removes the component\n\t\t\t\t\t\tif (finished.has(entity.id)) {\n\t\t\t\t\t\t\tfinished.delete(entity.id);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst state = entity.components.coroutine;\n\n\t\t\t\t\t\t// Tick the generator\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = state.generator.next(dt);\n\t\t\t\t\t\t\tif (result.done) {\n\t\t\t\t\t\t\t\tfinished.add(entity.id);\n\t\t\t\t\t\t\t\tstate.onComplete?.({ entityId: entity.id });\n\t\t\t\t\t\t\t\tecs.commands.removeComponent(entity.id, 'coroutine');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.warn(`Coroutine error on entity ${entity.id}:`, error);\n\t\t\t\t\t\t\tfinished.add(entity.id);\n\t\t\t\t\t\t\tecs.commands.removeComponent(entity.id, 'coroutine');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t},\n\t});\n}\n"
6
6
  ],
7
- "mappings": "uuBASA,uBAAS,kBAgDF,SAAS,CAAe,CAC9B,EACA,EAC6C,CAC7C,MAAO,CACN,UAAW,CACV,YACA,WAAY,GAAS,UACtB,CACD,EASM,SAAU,CAAW,CAAC,EAAqC,CACjE,GAAI,GAAW,EAAG,OAClB,IAAI,EAAU,EACd,MAAO,EAAU,EAAS,CACzB,IAAM,EAAa,MACnB,GAAW,GAQN,SAAU,CAAU,CAAC,EAAoC,CAC/D,QAAS,EAAI,EAAG,EAAI,EAAQ,IAC3B,MAQK,SAAU,CAAS,CAAC,EAA8C,CACxE,MAAO,CAAC,EAAU,EACjB,MASK,SAAU,CAAQ,IAAI,EAAsD,CAClF,GAAI,EAAW,SAAW,EAAG,OAG7B,IAAM,EAAS,EAAW,IAAI,KAAO,CAEpC,OADA,EAAI,KAAK,CAAC,EACH,CAAE,MAAK,KAAM,EAAM,EAC1B,EAED,MAAO,EAAO,KAAK,KAAS,CAAC,EAAM,IAAI,EAAG,CACzC,IAAM,EAAa,MACnB,QAAW,KAAS,EAAQ,CAC3B,GAAI,EAAM,KAAM,SAEhB,GADe,EAAM,IAAI,KAAK,CAAE,EACrB,KACV,EAAM,KAAO,KAWV,SAAU,CAAI,IAAI,EAAsD,CAC9E,GAAI,EAAW,SAAW,EAAG,OAG7B,IAAM,EAAU,EAAW,IAAI,KAAO,CAErC,OADA,EAAI,KAAK,CAAC,EACH,CAAE,MAAK,KAAM,EAAM,EAC1B,EAED,GAAI,CACH,MAAO,GAAM,CACZ,IAAM,EAAa,MACnB,QAAW,KAAS,EAAS,CAC5B,GAAI,EAAM,KAAM,SAEhB,GADe,EAAM,IAAI,KAAK,CAAE,EACrB,KAAM,CAChB,EAAM,KAAO,GAEb,QAAW,KAAS,EACnB,GAAI,CAAC,EAAM,KACV,EAAM,IAAI,OAAO,EACjB,EAAM,KAAO,GAGf,iBAIF,CAED,QAAW,KAAS,EACnB,GAAI,CAAC,EAAM,KACV,EAAM,IAAI,OAAO,EACjB,EAAM,KAAO,IAgBV,SAAU,CAAyE,CACzF,EACA,EACA,EACqB,CACrB,IAAI,EAAW,GACT,EAAc,EAAS,UAAU,EAAW,CAAC,IAAgB,CAClE,GAAI,CAAC,GAAU,EAAO,CAAI,EACzB,EAAW,GAEZ,EACD,GAAI,CACH,MAAO,CAAC,EACP,aAEA,CACD,EAAY,GAsBP,SAAS,CAAe,CAAC,EAAqB,EAA2B,CAC/E,IAAM,EAAQ,EAAI,aAAa,EAAU,WAAW,EACpD,GAAI,CAAC,EAAO,MAAO,GAGnB,OAFA,EAAM,UAAU,OAAO,EACvB,EAAI,SAAS,gBAAgB,EAAU,WAAW,EAC3C,GA8BD,SAAS,CAA8C,CAAC,EAAiC,CAC/F,MAAO,CACN,gBAAiB,EACjB,aAAc,CACf,EAcM,SAAS,CAAsD,CACrE,EACuF,CACvF,IACC,cAAc,aACd,WAAW,EACX,QAAQ,UACL,GAAW,CAAC,EAIV,EAAW,IAAI,IAErB,OAAO,EAA2F,CACjG,GAAI,aACJ,OAAO,CAAC,EAAO,CACd,EAAM,gBAAgB,YAAa,EAAG,QAAO,cAAe,CAC3D,EAAM,UAAU,OAAO,EACvB,EAAS,OAAO,CAAQ,EACxB,EAED,EACE,UAAU,kBAAkB,EAC5B,YAAY,CAAQ,EACpB,QAAQ,CAAK,EACb,QAAQ,CAAW,EACnB,SAAS,aAAc,CACvB,KAAM,CAAC,WAAW,CACnB,CAAC,EACA,iBAAiB,aAAc,EAAG,YAAa,CAC/C,EAAO,WAAW,UAAU,UAAU,KAAK,CAAC,EAC5C,EACA,WAAW,EAAG,UAAS,KAAI,SAAU,CACrC,QAAW,KAAU,EAAQ,WAAY,CAExC,GAAI,EAAS,IAAI,EAAO,EAAE,EAAG,CAC5B,EAAS,OAAO,EAAO,EAAE,EACzB,SAGD,IAAM,EAAQ,EAAO,WAAW,UAGhC,GAAI,CAEH,GADe,EAAM,UAAU,KAAK,CAAE,EAC3B,KACV,EAAS,IAAI,EAAO,EAAE,EACtB,EAAM,aAAa,CAAE,SAAU,EAAO,EAAG,CAAC,EAC1C,EAAI,SAAS,gBAAgB,EAAO,GAAI,WAAW,EAEnD,MAAO,EAAO,CACf,QAAQ,KAAK,6BAA6B,EAAO,MAAO,CAAK,EAC7D,EAAS,IAAI,EAAO,EAAE,EACtB,EAAI,SAAS,gBAAgB,EAAO,GAAI,WAAW,IAGrD,EAEJ,CAAC",
8
- "debugId": "3EF94FE7B5E8514A64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "4FBB31C8317254FC64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,5 @@
1
+ var{defineProperty:Z,getOwnPropertyNames:G,getOwnPropertyDescriptor:L}=Object,M=Object.prototype.hasOwnProperty;function N(j){return this[j]}var R=(j)=>{var k=($??=new WeakMap).get(j),q;if(k)return k;if(k=Z({},"__esModule",{value:!0}),j&&typeof j==="object"||typeof j==="function"){for(var z of G(j))if(!M.call(k,z))Z(k,z,{get:N.bind(j,z),enumerable:!(q=L(j,z))||q.enumerable})}return $.set(j,k),k},$;var Y=(j)=>j;function _(j,k){this[j]=Y.bind(null,k)}var B=(j,k)=>{for(var q in k)Z(j,q,{get:k[q],enumerable:!0,configurable:!0,set:_.bind(k,q)})};var C=(j,k)=>()=>(j&&(k=j(j=0)),k);var T=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(k,q)=>(typeof require<"u"?require:k)[q]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});import{definePlugin as D}from"ecspresso";function E(j){let k=new Float64Array(j),q=0,z=0;return{push(Q){if(k[q]=Q,q=(q+1)%j,z<j)z++},computeFps(){if(z<2)return 0;let Q=k[(q-1+j)%j]??0,U=k[(q-z+j)%j]??0,F=Q-U;if(F<=0)return 0;return(z-1)/F*1000},computeAverageFrameTime(){if(z<2)return 0;let Q=k[(q-1+j)%j]??0,U=k[(q-z+j)%j]??0,F=Q-U;if(F<=0)return 0;return F/(z-1)},get size(){return z}}}function x(j){let{systemGroup:k="diagnostics",enableTimingOnInit:q=!0,fpsSampleCount:z=60}=j??{},Q={fps:0,entityCount:0,systemTimings:new Map,phaseTimings:{preUpdate:0,fixedUpdate:0,update:0,postUpdate:0,render:0},averageFrameTime:0},U=E(z);return D({id:"diagnostics",install(F){F.addResource("diagnostics",Q),F.addSystem("diagnostics-collect").setPriority(-999999).inPhase("render").inGroup(k).setOnInitialize((J)=>{if(q)J.enableDiagnostics(!0)}).setOnDetach((J)=>{J.enableDiagnostics(!1)}).setProcess(({ecs:J})=>{let V=performance.now();U.push(V);let K=J.getResource("diagnostics"),H={fps:U.computeFps(),entityCount:J.entityCount,systemTimings:J.systemTimings,phaseTimings:J.phaseTimings,averageFrameTime:U.computeAverageFrameTime()};K.fps=H.fps,K.entityCount=H.entityCount,K.systemTimings=H.systemTimings,K.phaseTimings=H.phaseTimings,K.averageFrameTime=H.averageFrameTime})}})}var P={"top-left":"top:8px;left:8px","top-right":"top:8px;right:8px","bottom-left":"bottom:8px;left:8px","bottom-right":"bottom:8px;right:8px"};function S(j,k){let{position:q="top-left",updateInterval:z=200,showSystemTimings:Q=!0,maxSystemsShown:U=10}=k??{},F=document.createElement("div");F.style.cssText=`position:fixed;${P[q]};z-index:999999;background:rgba(0,0,0,0.8);color:#0f0;font:12px/1.4 monospace;padding:8px 12px;border-radius:4px;pointer-events:none;white-space:pre`,document.body.appendChild(F);let J=setInterval(()=>{let V=j.getResource("diagnostics"),K=[`FPS: ${V.fps.toFixed(0)}`,`Frame: ${V.averageFrameTime.toFixed(2)}ms`,`Entities: ${V.entityCount}`],H=V.phaseTimings;if(K.push(`Phases: pre=${H.preUpdate.toFixed(2)} fix=${H.fixedUpdate.toFixed(2)} upd=${H.update.toFixed(2)} post=${H.postUpdate.toFixed(2)} ren=${H.render.toFixed(2)}`),Q&&V.systemTimings.size>0){K.push("--- Systems ---");let A=[...V.systemTimings.entries()].sort((W,X)=>X[1]-W[1]).slice(0,U);for(let[W,X]of A)K.push(` ${W}: ${X.toFixed(3)}ms`)}F.textContent=K.join(`
2
+ `)},z);return()=>{clearInterval(J),F.remove()}}export{x as createDiagnosticsPlugin,S as createDiagnosticsOverlay};
3
+
4
+ //# debugId=5F648DEFFE91CC1264756E2164756E21
5
+ //# sourceMappingURL=diagnostics.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * Diagnostics Plugin for ECSpresso\n *\n * Runtime diagnostics: FPS, entity count, per-system timing, per-phase timing,\n * and an optional DOM overlay for visual debugging.\n */\n\nimport { definePlugin, type Plugin } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../type-utils';\n\n// ==================== Types ====================\n\nexport interface DiagnosticsData {\n\tfps: number;\n\tentityCount: number;\n\tsystemTimings: ReadonlyMap<string, number>;\n\tphaseTimings: Readonly<Record<SystemPhase, number>>;\n\taverageFrameTime: number;\n}\n\nexport interface DiagnosticsResourceTypes {\n\tdiagnostics: DiagnosticsData;\n}\n\nexport interface DiagnosticsPluginOptions<G extends string = 'diagnostics'> {\n\t/** System group name (default: 'diagnostics') */\n\tsystemGroup?: G;\n\t/** Enable timing collection on initialize (default: true) */\n\tenableTimingOnInit?: boolean;\n\t/** Number of frames to sample for FPS average (default: 60) */\n\tfpsSampleCount?: number;\n}\n\nexport interface DiagnosticsOverlayOptions {\n\t/** Corner position (default: 'top-left') */\n\tposition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\t/** Milliseconds between DOM updates (default: 200) */\n\tupdateInterval?: number;\n\t/** Show per-system timings (default: true) */\n\tshowSystemTimings?: boolean;\n\t/** Maximum systems to show in overlay (default: 10) */\n\tmaxSystemsShown?: number;\n}\n\n// ==================== Ring Buffer ====================\n\n/**\n * Fixed-size circular buffer for frame timestamps.\n * Avoids Array.shift() allocation on every frame.\n */\nfunction createRingBuffer(capacity: number) {\n\tconst buffer = new Float64Array(capacity);\n\tlet writeIndex = 0;\n\tlet count = 0;\n\n\treturn {\n\t\tpush(value: number): void {\n\t\t\tbuffer[writeIndex] = value;\n\t\t\twriteIndex = (writeIndex + 1) % capacity;\n\t\t\tif (count < capacity) count++;\n\t\t},\n\n\t\t/** Compute FPS from stored timestamps */\n\t\tcomputeFps(): number {\n\t\t\tif (count < 2) return 0;\n\t\t\tconst newest = buffer[(writeIndex - 1 + capacity) % capacity] ?? 0;\n\t\t\tconst oldest = buffer[(writeIndex - count + capacity) % capacity] ?? 0;\n\t\t\tconst elapsed = newest - oldest;\n\t\t\tif (elapsed <= 0) return 0;\n\t\t\treturn ((count - 1) / elapsed) * 1000;\n\t\t},\n\n\t\t/** Compute average frame time in ms */\n\t\tcomputeAverageFrameTime(): number {\n\t\t\tif (count < 2) return 0;\n\t\t\tconst newest = buffer[(writeIndex - 1 + capacity) % capacity] ?? 0;\n\t\t\tconst oldest = buffer[(writeIndex - count + capacity) % capacity] ?? 0;\n\t\t\tconst elapsed = newest - oldest;\n\t\t\tif (elapsed <= 0) return 0;\n\t\t\treturn elapsed / (count - 1);\n\t\t},\n\n\t\tget size(): number {\n\t\t\treturn count;\n\t\t},\n\t};\n}\n\n// ==================== Plugin Factory ====================\n\nexport function createDiagnosticsPlugin<G extends string = 'diagnostics'>(\n\toptions?: DiagnosticsPluginOptions<G>,\n): Plugin<WorldConfigFrom<{}, {}, DiagnosticsResourceTypes>, EmptyConfig, 'diagnostics-collect', G> {\n\tconst {\n\t\tsystemGroup = 'diagnostics',\n\t\tenableTimingOnInit = true,\n\t\tfpsSampleCount = 60,\n\t} = options ?? {};\n\n\tconst initialData: DiagnosticsData = {\n\t\tfps: 0,\n\t\tentityCount: 0,\n\t\tsystemTimings: new Map(),\n\t\tphaseTimings: { preUpdate: 0, fixedUpdate: 0, update: 0, postUpdate: 0, render: 0 },\n\t\taverageFrameTime: 0,\n\t};\n\n\tconst ringBuffer = createRingBuffer(fpsSampleCount);\n\n\treturn definePlugin<WorldConfigFrom<{}, {}, DiagnosticsResourceTypes>, EmptyConfig, 'diagnostics-collect', G>({\n\t\tid: 'diagnostics',\n\t\tinstall(world) {\n\t\t\tworld.addResource('diagnostics', initialData);\n\n\t\t\tworld\n\t\t\t\t.addSystem('diagnostics-collect')\n\t\t\t\t.setPriority(-999999)\n\t\t\t\t.inPhase('render')\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setOnInitialize((ecs) => {\n\t\t\t\t\tif (enableTimingOnInit) {\n\t\t\t\t\t\tecs.enableDiagnostics(true);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.setOnDetach((ecs) => {\n\t\t\t\t\tecs.enableDiagnostics(false);\n\t\t\t\t})\n\t\t\t\t.setProcess(({ ecs }) => {\n\t\t\t\t\tconst now = performance.now();\n\t\t\t\t\tringBuffer.push(now);\n\n\t\t\t\t\tconst resource = ecs.getResource('diagnostics');\n\t\t\t\t\tconst updated: DiagnosticsData = {\n\t\t\t\t\t\tfps: ringBuffer.computeFps(),\n\t\t\t\t\t\tentityCount: ecs.entityCount,\n\t\t\t\t\t\tsystemTimings: ecs.systemTimings,\n\t\t\t\t\t\tphaseTimings: ecs.phaseTimings,\n\t\t\t\t\t\taverageFrameTime: ringBuffer.computeAverageFrameTime(),\n\t\t\t\t\t};\n\n\t\t\t\t\t// Mutate fields on the existing resource object to avoid allocation\n\t\t\t\t\t(resource as { -readonly [K in keyof DiagnosticsData]: DiagnosticsData[K] }).fps = updated.fps;\n\t\t\t\t\t(resource as { -readonly [K in keyof DiagnosticsData]: DiagnosticsData[K] }).entityCount = updated.entityCount;\n\t\t\t\t\t(resource as { -readonly [K in keyof DiagnosticsData]: DiagnosticsData[K] }).systemTimings = updated.systemTimings;\n\t\t\t\t\t(resource as { -readonly [K in keyof DiagnosticsData]: DiagnosticsData[K] }).phaseTimings = updated.phaseTimings;\n\t\t\t\t\t(resource as { -readonly [K in keyof DiagnosticsData]: DiagnosticsData[K] }).averageFrameTime = updated.averageFrameTime;\n\t\t\t\t});\n\t\t},\n\t});\n}\n\n// ==================== Overlay Helper ====================\n\nconst POSITION_STYLES: Record<NonNullable<DiagnosticsOverlayOptions['position']>, string> = {\n\t'top-left': 'top:8px;left:8px',\n\t'top-right': 'top:8px;right:8px',\n\t'bottom-left': 'bottom:8px;left:8px',\n\t'bottom-right': 'bottom:8px;right:8px',\n} as const;\n\n/**\n * Create a DOM overlay that displays diagnostics data.\n * Returns a cleanup function that removes the element and clears the interval.\n *\n * @param ecs An ECSpresso instance with the diagnostics resource\n * @param options Overlay configuration\n * @returns Cleanup function\n */\nexport function createDiagnosticsOverlay<\n\tR extends DiagnosticsResourceTypes,\n>(\n\tecs: { getResource<K extends keyof R>(key: K): R[K] },\n\toptions?: DiagnosticsOverlayOptions,\n): () => void {\n\tconst {\n\t\tposition = 'top-left',\n\t\tupdateInterval = 200,\n\t\tshowSystemTimings = true,\n\t\tmaxSystemsShown = 10,\n\t} = options ?? {};\n\n\tconst el = document.createElement('div');\n\tel.style.cssText = `position:fixed;${POSITION_STYLES[position]};z-index:999999;background:rgba(0,0,0,0.8);color:#0f0;font:12px/1.4 monospace;padding:8px 12px;border-radius:4px;pointer-events:none;white-space:pre`;\n\tdocument.body.appendChild(el);\n\n\tconst intervalId = setInterval(() => {\n\t\tconst d = ecs.getResource('diagnostics' as keyof R) as DiagnosticsData;\n\n\t\tconst lines: string[] = [\n\t\t\t`FPS: ${d.fps.toFixed(0)}`,\n\t\t\t`Frame: ${d.averageFrameTime.toFixed(2)}ms`,\n\t\t\t`Entities: ${d.entityCount}`,\n\t\t];\n\n\t\tconst phases = d.phaseTimings;\n\t\tlines.push(\n\t\t\t`Phases: pre=${phases.preUpdate.toFixed(2)} fix=${phases.fixedUpdate.toFixed(2)} upd=${phases.update.toFixed(2)} post=${phases.postUpdate.toFixed(2)} ren=${phases.render.toFixed(2)}`,\n\t\t);\n\n\t\tif (showSystemTimings && d.systemTimings.size > 0) {\n\t\t\tlines.push('--- Systems ---');\n\t\t\tconst sorted = [...d.systemTimings.entries()]\n\t\t\t\t.sort((a, b) => b[1] - a[1])\n\t\t\t\t.slice(0, maxSystemsShown);\n\t\t\tfor (const [label, ms] of sorted) {\n\t\t\t\tlines.push(` ${label}: ${ms.toFixed(3)}ms`);\n\t\t\t}\n\t\t}\n\n\t\tel.textContent = lines.join('\\n');\n\t}, updateInterval);\n\n\treturn () => {\n\t\tclearInterval(intervalId);\n\t\tel.remove();\n\t};\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "B57D6397BDB064BA64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "5F648DEFFE91CC1264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,4 @@
1
+ var{defineProperty:B,getOwnPropertyNames:A,getOwnPropertyDescriptor:D}=Object,R=Object.prototype.hasOwnProperty;function v(q){return this[q]}var y=(q)=>{var C=(L??=new WeakMap).get(q),Q;if(C)return C;if(C=B({},"__esModule",{value:!0}),q&&typeof q==="object"||typeof q==="function"){for(var V of A(q))if(!R.call(C,V))B(C,V,{get:v.bind(q,V),enumerable:!(Q=D(q,V))||Q.enumerable})}return L.set(q,C),C},L;var P=(q)=>q;function S(q,C){this[q]=P.bind(null,C)}var l=(q,C)=>{for(var Q in C)B(q,Q,{get:C[Q],enumerable:!0,configurable:!0,set:S.bind(C,Q)})};var h=(q,C)=>()=>(q&&(C=q(q=0)),C);var d=((q)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(q,{get:(C,Q)=>(typeof require<"u"?require:C)[Q]}):q)(function(q){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+q+'" is not supported')});import{definePlugin as _}from"ecspresso";function i(q){return q}function b(){return{keysDown:new Set,keysPressed:[],keysReleased:[],buttonsDown:new Set,buttonsPressed:[],buttonsReleased:[],pointerX:0,pointerY:0,pointerDeltaX:0,pointerDeltaY:0,lastPointerX:0,lastPointerY:0,pointerMoved:!1}}var K=new Set,F=new Set;function k(){return{keysDown:K,keysPressed:K,keysReleased:K,buttonsDown:F,buttonsPressed:F,buttonsReleased:F,pointerX:0,pointerY:0,pointerDeltaX:0,pointerDeltaY:0,actionsActive:K,prevActionsActive:K}}function E(q,C,Q){let V=new Set;for(let[$,Z]of Object.entries(q)){let O=Z.keys?.some((W)=>C.has(W))??!1,J=Z.buttons?.some((W)=>Q.has(W))??!1;if(O||J)V.add($)}return V}function M(q,C,Q){let V=new Set(q.keysDown),$=new Set(q.keysPressed),Z=new Set(q.keysReleased),O=new Set(q.buttonsDown),J=new Set(q.buttonsPressed),W=new Set(q.buttonsReleased),f=q.pointerMoved?q.pointerX-q.lastPointerX:0,j=q.pointerMoved?q.pointerY-q.lastPointerY:0,x=E(Q,V,O),m={keysDown:V,keysPressed:$,keysReleased:Z,buttonsDown:O,buttonsPressed:J,buttonsReleased:W,pointerX:q.pointerX,pointerY:q.pointerY,pointerDeltaX:f,pointerDeltaY:j,actionsActive:x,prevActionsActive:C};return q.keysPressed=[],q.keysReleased=[],q.buttonsPressed=[],q.buttonsReleased=[],q.lastPointerX=q.pointerX,q.lastPointerY=q.pointerY,q.pointerMoved=!1,m}function w(q){let{systemGroup:C="input",priority:Q=100,phase:V="preUpdate",actions:$={},target:Z=globalThis}=q??{},O=b(),J=k(),W={...$},f=[],j={x:0,y:0},x={x:0,y:0},G={keyboard:{isDown:(z)=>J.keysDown.has(z),justPressed:(z)=>J.keysPressed.has(z),justReleased:(z)=>J.keysReleased.has(z)},pointer:{position:j,delta:x,isDown:(z)=>J.buttonsDown.has(z),justPressed:(z)=>J.buttonsPressed.has(z),justReleased:(z)=>J.buttonsReleased.has(z)},actions:{isActive:(z)=>J.actionsActive.has(z),justActivated:(z)=>J.actionsActive.has(z)&&!J.prevActionsActive.has(z),justDeactivated:(z)=>!J.actionsActive.has(z)&&J.prevActionsActive.has(z)},setActionMap(z){W={...z}},getActionMap(){return{...W}}};function I(z){let H=z;if(H.repeat)return;O.keysDown.add(H.key),O.keysPressed.push(H.key)}function U(z){let H=z;O.keysDown.delete(H.key),O.keysReleased.push(H.key)}function g(z){let H=z;O.buttonsDown.add(H.button),O.buttonsPressed.push(H.button)}function N(z){let H=z;O.pointerX=H.clientX,O.pointerY=H.clientY,O.pointerMoved=!0}function Y(z){let H=z;O.buttonsDown.delete(H.button),O.buttonsReleased.push(H.button)}function X(z,H){Z.addEventListener(z,H),f.push(()=>{Z.removeEventListener(z,H)})}return _({id:"input",install(z){z.addResource("inputState",G),z.addSystem("input-state").setPriority(Q).inPhase(V).inGroup(C).setOnInitialize(()=>{X("keydown",I),X("keyup",U),X("pointerdown",g),X("pointermove",N),X("pointerup",Y)}).setOnDetach(()=>{for(let H of f)H();f.length=0}).setProcess(()=>{let H=J.actionsActive;J=M(O,H,W),j.x=J.pointerX,j.y=J.pointerY,x.x=J.pointerDeltaX,x.y=J.pointerDeltaY})}})}export{w as createInputPlugin,i as createActionBinding};
2
+
3
+ //# debugId=AA1CB6B40EFAD7D764756E2164756E21
4
+ //# sourceMappingURL=input.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * Input Plugin for ECSpresso\n *\n * Provides frame-accurate keyboard, pointer (mouse + touch via PointerEvent),\n * and action mapping input. Resource-only plugin — input is polled via the\n * `inputState` resource. No ECS components or events.\n *\n * DOM events are accumulated between frames and snapshotted once per frame\n * in the system's process step, so all systems see consistent state.\n */\n\nimport { definePlugin, type Plugin, type BasePluginOptions } from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../type-utils';\n\n// ==================== Public Types ====================\n\nexport interface Vec2 {\n\tx: number;\n\ty: number;\n}\n\n// Key codes per the UI Events spec (KeyboardEvent.key values)\n// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values\n\ntype LowercaseLetter =\n\t| 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm'\n\t| 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';\n\ntype Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';\n\ntype Punctuation =\n\t| '`' | '~' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '(' | ')'\n\t| '-' | '_' | '=' | '+' | '[' | '{' | ']' | '}' | '\\\\' | '|'\n\t| ';' | ':' | \"'\" | '\"' | ',' | '<' | '.' | '>' | '/' | '?';\n\ntype ModifierKey =\n\t| 'Alt' | 'AltGraph' | 'CapsLock' | 'Control' | 'Fn' | 'FnLock'\n\t| 'Hyper' | 'Meta' | 'NumLock' | 'ScrollLock' | 'Shift'\n\t| 'Super' | 'Symbol' | 'SymbolLock';\n\ntype WhitespaceKey = 'Enter' | 'Tab' | ' ';\n\ntype NavigationKey =\n\t| `Arrow${'Down' | 'Left' | 'Right' | 'Up'}`\n\t| 'End' | 'Home' | 'PageDown' | 'PageUp';\n\ntype EditingKey =\n\t| 'Backspace' | 'Clear' | 'Copy' | 'CrSel' | 'Cut' | 'Delete'\n\t| 'EraseEof' | 'ExSel' | 'Insert' | 'Paste' | 'Redo' | 'Undo';\n\ntype UIKey =\n\t| 'Accept' | 'Again' | 'Attn' | 'Cancel' | 'ContextMenu' | 'Escape'\n\t| 'Execute' | 'Find' | 'Finish' | 'Help' | 'Pause' | 'Play'\n\t| 'Props' | 'Select' | 'ZoomIn' | 'ZoomOut';\n\ntype DeviceKey =\n\t| 'BrightnessDown' | 'BrightnessUp' | 'Eject' | 'Hibernate'\n\t| 'LogOff' | 'Power' | 'PowerOff' | 'PrintScreen' | 'Standby' | 'WakeUp';\n\ntype IMEKey =\n\t| 'AllCandidates' | 'Alphanumeric' | 'CodeInput' | 'Compose' | 'Convert'\n\t| 'FinalMode' | 'GroupFirst' | 'GroupLast' | 'GroupNext' | 'GroupPrevious'\n\t| 'ModeChange' | 'NextCandidate' | 'NonConvert' | 'PreviousCandidate'\n\t| 'Process' | 'SingleCandidate'\n\t| 'HangulMode' | 'HanjaMode' | 'JunjaMode'\n\t| 'Eisu' | 'Hankaku' | 'Hiragana' | 'HiraganaKatakana' | 'KanaMode'\n\t| 'KanjiMode' | 'Katakana' | 'Romaji' | 'Zenkaku' | 'ZenkakuHankaku';\n\ntype FunctionKey =\n\t| `F${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24}`\n\t| 'Soft1' | 'Soft2' | 'Soft3' | 'Soft4';\n\ntype PhoneKey =\n\t| 'AppSwitch' | 'Call' | 'Camera' | 'CameraFocus' | 'EndCall'\n\t| 'GoBack' | 'GoHome' | 'HeadsetHook' | 'LastNumberRedial'\n\t| 'Notification' | 'MannerMode' | 'VoiceDial';\n\ntype MultimediaKey =\n\t| 'ChannelDown' | 'ChannelUp'\n\t| `Media${\n\t\t'FastForward' | 'Pause' | 'Play' | 'PlayPause'\n\t\t| 'Record' | 'Rewind' | 'Stop' | 'TrackNext' | 'TrackPrevious'\n\t}`;\n\ntype AudioKey =\n\t| `Audio${\n\t\t'BalanceLeft' | 'BalanceRight' | 'BassDown' | 'BassBoostDown'\n\t\t| 'BassBoostToggle' | 'BassBoostUp' | 'BassUp' | 'FaderFront' | 'FaderRear'\n\t\t| 'SurroundModeNext' | 'TrebleDown' | 'TrebleUp'\n\t\t| 'VolumeDown' | 'VolumeMute' | 'VolumeUp'\n\t}`\n\t| `Microphone${'Toggle' | 'VolumeDown' | 'VolumeMute' | 'VolumeUp'}`;\n\ntype TVKey =\n\t| 'TV'\n\t| `TV${\n\t\t'3DMode' | 'AntennaCable' | 'AudioDescription' | 'AudioDescriptionMixDown'\n\t\t| 'AudioDescriptionMixUp' | 'ContentsMenu' | 'DataService' | 'Input'\n\t\t| 'InputComponent1' | 'InputComponent2' | 'InputComposite1' | 'InputComposite2'\n\t\t| 'InputHDMI1' | 'InputHDMI2' | 'InputHDMI3' | 'InputHDMI4' | 'InputVGA1'\n\t\t| 'MediaContext' | 'Network' | 'NumberEntry' | 'Power' | 'RadioService'\n\t\t| 'Satellite' | 'SatelliteBS' | 'SatelliteCS' | 'SatelliteToggle'\n\t\t| 'TerrestrialAnalog' | 'TerrestrialDigital' | 'Timer'\n\t}`;\n\ntype MediaControllerKey =\n\t| 'AVRInput' | 'AVRPower'\n\t| `Color${'F0Red' | 'F1Green' | 'F2Yellow' | 'F3Blue' | 'F4Grey' | 'F5Brown'}`\n\t| 'ClosedCaptionToggle' | 'Dimmer' | 'DisplaySwap' | 'DVR' | 'Exit'\n\t| `Favorite${'Clear' | 'Recall' | 'Store'}${0 | 1 | 2 | 3}`\n\t| 'Guide' | 'GuideNextDay' | 'GuidePreviousDay' | 'Info' | 'InstantReplay'\n\t| 'Link' | 'ListProgram' | 'LiveContent' | 'Lock'\n\t| `Media${\n\t\t'Apps' | 'AudioTrack' | 'Last' | 'SkipBackward'\n\t\t| 'SkipForward' | 'StepBackward' | 'StepForward' | 'TopMenu'\n\t}`\n\t| `Navigate${'In' | 'Next' | 'Out' | 'Previous'}`\n\t| 'NextFavoriteChannel' | 'NextUserProfile' | 'OnDemand' | 'Pairing'\n\t| `PinP${'Down' | 'Move' | 'Toggle' | 'Up'}`\n\t| `PlaySpeed${'Down' | 'Reset' | 'Up'}`\n\t| 'RandomToggle' | 'RcLowBattery' | 'RecordSpeedNext' | 'RfBypass'\n\t| 'ScanChannelsToggle' | 'ScreenModeNext' | 'Settings' | 'SplitScreenToggle'\n\t| 'STBInput' | 'STBPower' | 'Subtitle' | 'Teletext'\n\t| 'VideoModeNext' | 'Wink' | 'ZoomToggle';\n\ntype SpeechKey = 'SpeechCorrectionList' | 'SpeechInputToggle';\n\ntype DocumentKey =\n\t| 'Close' | 'New' | 'Open' | 'Print' | 'Save' | 'SpellCheck'\n\t| 'MailForward' | 'MailReply' | 'MailSend';\n\ntype LaunchKey = `Launch${\n\t| 'Calculator' | 'Calendar' | 'Contacts' | 'Mail' | 'MediaPlayer'\n\t| 'MusicPlayer' | 'MyComputer' | 'Phone' | 'ScreenSaver' | 'Spreadsheet'\n\t| 'WebBrowser' | 'WebCam' | 'WordProcessor'\n\t| `Application${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16}`\n}`;\n\ntype BrowserKey = `Browser${'Back' | 'Favorites' | 'Forward' | 'Home' | 'Refresh' | 'Search' | 'Stop'}`;\n\ntype NumpadKey = 'Decimal' | 'Key11' | 'Key12' | 'Multiply' | 'Add' | 'Divide' | 'Subtract' | 'Separator';\n\nexport type KeyCode =\n\t| LowercaseLetter | Uppercase<LowercaseLetter> | Digit | Punctuation\n\t| ModifierKey | WhitespaceKey | NavigationKey | EditingKey | UIKey | DeviceKey\n\t| IMEKey | FunctionKey | PhoneKey | MultimediaKey | AudioKey | TVKey\n\t| MediaControllerKey | SpeechKey | DocumentKey | LaunchKey | BrowserKey | NumpadKey\n\t| 'Unidentified' | 'Dead';\n\nexport interface KeyboardState {\n\tisDown(key: KeyCode): boolean;\n\tjustPressed(key: KeyCode): boolean;\n\tjustReleased(key: KeyCode): boolean;\n}\n\nexport interface PointerState {\n\treadonly position: Readonly<Vec2>;\n\treadonly delta: Readonly<Vec2>;\n\tisDown(button: number): boolean;\n\tjustPressed(button: number): boolean;\n\tjustReleased(button: number): boolean;\n}\n\nexport interface ActionState<A extends string = string> {\n\tisActive(action: A): boolean;\n\tjustActivated(action: A): boolean;\n\tjustDeactivated(action: A): boolean;\n}\n\nexport interface InputState<A extends string = string> {\n\treadonly keyboard: KeyboardState;\n\treadonly pointer: PointerState;\n\treadonly actions: ActionState<A>;\n\tsetActionMap(actions: ActionMap<A>): void;\n\tgetActionMap(): Readonly<ActionMap<A>>;\n}\n\nexport interface ActionBinding {\n\tkeys?: KeyCode[];\n\tbuttons?: number[];\n}\n\nexport type ActionMap<A extends string = string> = Record<A, ActionBinding>;\n\nexport interface InputResourceTypes<A extends string = string> {\n\tinputState: InputState<A>;\n}\n\nexport interface InputPluginOptions<A extends string = string, G extends string = 'input'> extends BasePluginOptions<G> {\n\t/** Initial action mappings */\n\tactions?: ActionMap<A>;\n\t/** EventTarget to attach listeners to (default: globalThis). Pass a custom target for testability. */\n\ttarget?: EventTarget;\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Create a single action binding.\n *\n * @param binding The binding configuration\n * @returns The same binding object\n */\nexport function createActionBinding(binding: ActionBinding): ActionBinding {\n\treturn binding;\n}\n\n// ==================== Internal Types ====================\n\ninterface RawInputState {\n\tkeysDown: Set<string>;\n\tkeysPressed: string[];\n\tkeysReleased: string[];\n\tbuttonsDown: Set<number>;\n\tbuttonsPressed: number[];\n\tbuttonsReleased: number[];\n\tpointerX: number;\n\tpointerY: number;\n\tpointerDeltaX: number;\n\tpointerDeltaY: number;\n\tlastPointerX: number;\n\tlastPointerY: number;\n\tpointerMoved: boolean;\n}\n\ninterface FrameSnapshot {\n\tkeysDown: ReadonlySet<string>;\n\tkeysPressed: ReadonlySet<string>;\n\tkeysReleased: ReadonlySet<string>;\n\tbuttonsDown: ReadonlySet<number>;\n\tbuttonsPressed: ReadonlySet<number>;\n\tbuttonsReleased: ReadonlySet<number>;\n\tpointerX: number;\n\tpointerY: number;\n\tpointerDeltaX: number;\n\tpointerDeltaY: number;\n\tactionsActive: ReadonlySet<string>;\n\tprevActionsActive: ReadonlySet<string>;\n}\n\n// ==================== Plugin Factory ====================\n\nfunction createRawInputState(): RawInputState {\n\treturn {\n\t\tkeysDown: new Set(),\n\t\tkeysPressed: [],\n\t\tkeysReleased: [],\n\t\tbuttonsDown: new Set(),\n\t\tbuttonsPressed: [],\n\t\tbuttonsReleased: [],\n\t\tpointerX: 0,\n\t\tpointerY: 0,\n\t\tpointerDeltaX: 0,\n\t\tpointerDeltaY: 0,\n\t\tlastPointerX: 0,\n\t\tlastPointerY: 0,\n\t\tpointerMoved: false,\n\t};\n}\n\nconst EMPTY_SET_STRING: ReadonlySet<string> = new Set<string>();\nconst EMPTY_SET_NUMBER: ReadonlySet<number> = new Set<number>();\n\nfunction createEmptySnapshot(): FrameSnapshot {\n\treturn {\n\t\tkeysDown: EMPTY_SET_STRING,\n\t\tkeysPressed: EMPTY_SET_STRING,\n\t\tkeysReleased: EMPTY_SET_STRING,\n\t\tbuttonsDown: EMPTY_SET_NUMBER,\n\t\tbuttonsPressed: EMPTY_SET_NUMBER,\n\t\tbuttonsReleased: EMPTY_SET_NUMBER,\n\t\tpointerX: 0,\n\t\tpointerY: 0,\n\t\tpointerDeltaX: 0,\n\t\tpointerDeltaY: 0,\n\t\tactionsActive: EMPTY_SET_STRING,\n\t\tprevActionsActive: EMPTY_SET_STRING,\n\t};\n}\n\nfunction computeActiveActions(\n\tactionMap: ActionMap,\n\tkeysDown: ReadonlySet<string>,\n\tbuttonsDown: ReadonlySet<number>,\n): Set<string> {\n\tconst active = new Set<string>();\n\tfor (const [name, binding] of Object.entries(actionMap)) {\n\t\tconst keyActive = binding.keys?.some((k) => keysDown.has(k)) ?? false;\n\t\tconst buttonActive = binding.buttons?.some((b) => buttonsDown.has(b)) ?? false;\n\t\tif (keyActive || buttonActive) {\n\t\t\tactive.add(name);\n\t\t}\n\t}\n\treturn active;\n}\n\nfunction snapshotRaw(raw: RawInputState, prevActionsActive: ReadonlySet<string>, actionMap: ActionMap): FrameSnapshot {\n\tconst keysDown = new Set(raw.keysDown);\n\tconst keysPressed = new Set(raw.keysPressed);\n\tconst keysReleased = new Set(raw.keysReleased);\n\tconst buttonsDown = new Set(raw.buttonsDown);\n\tconst buttonsPressed = new Set(raw.buttonsPressed);\n\tconst buttonsReleased = new Set(raw.buttonsReleased);\n\n\tconst pointerDeltaX = raw.pointerMoved ? raw.pointerX - raw.lastPointerX : 0;\n\tconst pointerDeltaY = raw.pointerMoved ? raw.pointerY - raw.lastPointerY : 0;\n\n\tconst actionsActive = computeActiveActions(actionMap, keysDown, buttonsDown);\n\n\tconst snapshot: FrameSnapshot = {\n\t\tkeysDown,\n\t\tkeysPressed,\n\t\tkeysReleased,\n\t\tbuttonsDown,\n\t\tbuttonsPressed,\n\t\tbuttonsReleased,\n\t\tpointerX: raw.pointerX,\n\t\tpointerY: raw.pointerY,\n\t\tpointerDeltaX,\n\t\tpointerDeltaY,\n\t\tactionsActive,\n\t\tprevActionsActive,\n\t};\n\n\t// Clear accumulation buffers\n\traw.keysPressed = [];\n\traw.keysReleased = [];\n\traw.buttonsPressed = [];\n\traw.buttonsReleased = [];\n\traw.lastPointerX = raw.pointerX;\n\traw.lastPointerY = raw.pointerY;\n\traw.pointerMoved = false;\n\n\treturn snapshot;\n}\n\n/**\n * Create an input plugin for ECSpresso.\n *\n * This plugin provides:\n * - Frame-accurate keyboard state (isDown, justPressed, justReleased)\n * - Pointer position/delta and button state (mouse + touch via PointerEvent)\n * - Named action mapping with runtime remapping\n * - Automatic listener cleanup on detach\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createInputPlugin({\n * actions: {\n * jump: { keys: [' ', 'ArrowUp'] },\n * shoot: { keys: ['z'], buttons: [0] },\n * },\n * }))\n * .build();\n *\n * // In a system:\n * const input = ecs.getResource('inputState');\n * if (input.actions.justActivated('jump')) { ... }\n * if (input.keyboard.isDown('ArrowRight')) { ... }\n * ```\n */\nexport function createInputPlugin<A extends string = string, G extends string = 'input'>(\n\toptions?: InputPluginOptions<A, G>\n): Plugin<WorldConfigFrom<{}, {}, InputResourceTypes<A>>, EmptyConfig, 'input-state', G> {\n\tconst {\n\t\tsystemGroup = 'input',\n\t\tpriority = 100,\n\t\tphase = 'preUpdate',\n\t\tactions: initialActions = {},\n\t\ttarget = globalThis,\n\t} = options ?? {};\n\n\t// Closure state\n\tconst raw = createRawInputState();\n\tlet snapshot = createEmptySnapshot();\n\tlet actionMap: ActionMap = { ...initialActions };\n\tconst cleanupFns: Array<() => void> = [];\n\n\t// The position/delta objects exposed via the resource.\n\t// Updated in-place each frame to avoid allocations.\n\tconst position: Vec2 = { x: 0, y: 0 };\n\tconst delta: Vec2 = { x: 0, y: 0 };\n\n\t// Build the InputState resource that closes over snapshot\n\tconst keyboard: KeyboardState = {\n\t\tisDown: (key) => snapshot.keysDown.has(key),\n\t\tjustPressed: (key) => snapshot.keysPressed.has(key),\n\t\tjustReleased: (key) => snapshot.keysReleased.has(key),\n\t};\n\n\tconst pointer: PointerState = {\n\t\tposition,\n\t\tdelta,\n\t\tisDown: (button) => snapshot.buttonsDown.has(button),\n\t\tjustPressed: (button) => snapshot.buttonsPressed.has(button),\n\t\tjustReleased: (button) => snapshot.buttonsReleased.has(button),\n\t};\n\n\tconst actionState: ActionState<A> = {\n\t\tisActive: (action) => snapshot.actionsActive.has(action),\n\t\tjustActivated: (action) =>\n\t\t\tsnapshot.actionsActive.has(action) && !snapshot.prevActionsActive.has(action),\n\t\tjustDeactivated: (action) =>\n\t\t\t!snapshot.actionsActive.has(action) && snapshot.prevActionsActive.has(action),\n\t};\n\n\tconst inputState: InputState<A> = {\n\t\tkeyboard,\n\t\tpointer,\n\t\tactions: actionState,\n\t\tsetActionMap(newMap) {\n\t\t\tactionMap = { ...newMap };\n\t\t},\n\t\tgetActionMap() {\n\t\t\treturn { ...actionMap } as ActionMap<A>;\n\t\t},\n\t};\n\n\t// DOM event handlers\n\tfunction onKeyDown(e: Event) {\n\t\tconst ke = e as KeyboardEvent;\n\t\tif (ke.repeat) return;\n\t\traw.keysDown.add(ke.key);\n\t\traw.keysPressed.push(ke.key);\n\t}\n\n\tfunction onKeyUp(e: Event) {\n\t\tconst ke = e as KeyboardEvent;\n\t\traw.keysDown.delete(ke.key);\n\t\traw.keysReleased.push(ke.key);\n\t}\n\n\tfunction onPointerDown(e: Event) {\n\t\tconst pe = e as PointerEvent;\n\t\traw.buttonsDown.add(pe.button);\n\t\traw.buttonsPressed.push(pe.button);\n\t}\n\n\tfunction onPointerMove(e: Event) {\n\t\tconst pe = e as PointerEvent;\n\t\traw.pointerX = pe.clientX;\n\t\traw.pointerY = pe.clientY;\n\t\traw.pointerMoved = true;\n\t}\n\n\tfunction onPointerUp(e: Event) {\n\t\tconst pe = e as PointerEvent;\n\t\traw.buttonsDown.delete(pe.button);\n\t\traw.buttonsReleased.push(pe.button);\n\t}\n\n\tfunction addListener(type: string, handler: (e: Event) => void) {\n\t\ttarget.addEventListener(type, handler);\n\t\tcleanupFns.push(() => { target.removeEventListener(type, handler); });\n\t}\n\n\treturn definePlugin<WorldConfigFrom<{}, {}, InputResourceTypes<A>>, EmptyConfig, 'input-state', G>({\n\t\tid: 'input',\n\t\tinstall(world) {\n\t\t\tworld.addResource('inputState', inputState);\n\n\t\t\tworld\n\t\t\t\t.addSystem('input-state')\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setOnInitialize(() => {\n\t\t\t\t\taddListener('keydown', onKeyDown);\n\t\t\t\t\taddListener('keyup', onKeyUp);\n\t\t\t\t\taddListener('pointerdown', onPointerDown);\n\t\t\t\t\taddListener('pointermove', onPointerMove);\n\t\t\t\t\taddListener('pointerup', onPointerUp);\n\t\t\t\t})\n\t\t\t\t.setOnDetach(() => {\n\t\t\t\t\tfor (const cleanup of cleanupFns) {\n\t\t\t\t\t\tcleanup();\n\t\t\t\t\t}\n\t\t\t\t\tcleanupFns.length = 0;\n\t\t\t\t})\n\t\t\t\t.setProcess(() => {\n\t\t\t\t\tconst prevActionsActive = snapshot.actionsActive;\n\t\t\t\t\tsnapshot = snapshotRaw(raw, prevActionsActive, actionMap);\n\n\t\t\t\t\t// Update the exposed position/delta objects in-place\n\t\t\t\t\tposition.x = snapshot.pointerX;\n\t\t\t\t\tposition.y = snapshot.pointerY;\n\t\t\t\t\tdelta.x = snapshot.pointerDeltaX;\n\t\t\t\t\tdelta.y = snapshot.pointerDeltaY;\n\t\t\t\t});\n\t\t},\n\t});\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "43414B185EC78B3864756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "AA1CB6B40EFAD7D764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,4 @@
1
+ var{defineProperty:W,getOwnPropertyNames:D,getOwnPropertyDescriptor:X}=Object,Y=Object.prototype.hasOwnProperty;function T(k){return this[k]}var x=(k)=>{var A=(_??=new WeakMap).get(k),B;if(A)return A;if(A=W({},"__esModule",{value:!0}),k&&typeof k==="object"||typeof k==="function"){for(var Z of D(k))if(!Y.call(A,Z))W(A,Z,{get:T.bind(k,Z),enumerable:!(B=X(k,Z))||B.enumerable})}return _.set(k,A),A},_;var C=(k)=>k;function R(k,A){this[k]=C.bind(null,A)}var y=(k,A)=>{for(var B in A)W(k,B,{get:A[B],enumerable:!0,configurable:!0,set:R.bind(A,B)})};var g=(k,A)=>()=>(k&&(A=k(k=0)),A);var w=((k)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(k,{get:(A,B)=>(typeof require<"u"?require:A)[B]}):k)(function(k){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+k+'" is not supported')});import{definePlugin as v}from"ecspresso";function M(k){if(typeof k==="number")return k;let[A,B]=k;return A+Math.random()*(B-A)}function I(k,A,B){if(k===A)return k;let Z=k>>16&255,L=k>>8&255,q=k&255,H=A>>16&255,j=A>>8&255,$=A&255,Q=Z+(H-Z)*B|0,V=L+(j-L)*B|0,J=q+($-q)*B|0;return Q<<16|V<<8|J}var O=Math.PI*2;function G(k){let A=k.startSize??1,B=k.startTint??16777215;return Object.freeze({maxParticles:k.maxParticles,texture:k.texture,spawnRate:k.spawnRate??10,burstCount:k.burstCount??0,duration:k.duration??-1,lifetime:k.lifetime??1,speed:k.speed??100,angle:k.angle??[0,O],emissionShape:k.emissionShape??"point",emissionRadius:k.emissionRadius??0,gravity:Object.freeze(k.gravity??{x:0,y:0}),startSize:A,endSize:k.endSize??A,startAlpha:k.startAlpha??1,endAlpha:k.endAlpha??0,startTint:B,endTint:k.endTint??B,startRotation:k.startRotation??0,rotationSpeed:k.rotationSpeed??0,blendMode:k.blendMode??"normal",worldSpace:k.worldSpace??!0})}function l(k,A){return{particleEmitter:{config:k,activeCount:0,spawnAccumulator:0,elapsed:0,playing:A?.playing??!0,pendingBurst:0,finished:!1,onComplete:A?.onComplete}}}function d(k,A,B){let Z=k.getComponent(A,"particleEmitter");if(!Z)return!1;return Z.pendingBurst+=B??Z.config.burstCount,k.markChanged(A,"particleEmitter"),!0}function s(k,A){let B=k.getComponent(A,"particleEmitter");if(!B)return!1;return B.playing=!1,!0}function m(k,A){let B=k.getComponent(A,"particleEmitter");if(!B)return!1;return B.playing=!0,!0}function b(k,A,B,Z,L){k.active=!0;let q=M(A.lifetime);if(k.life=q,k.maxLife=q,A.emissionShape==="circle"&&A.emissionRadius>0){let $=Math.random()*O,Q=Math.random()*A.emissionRadius;k.x=B+Math.cos($)*Q,k.y=Z+Math.sin($)*Q}else k.x=B,k.y=Z;let H=M(A.speed),j=M(A.angle)+L;k.vx=Math.cos(j)*H,k.vy=Math.sin(j)*H,k.startSize=M(A.startSize),k.endSize=M(A.endSize),k.size=k.startSize,k.startAlpha=M(A.startAlpha),k.endAlpha=M(A.endAlpha),k.alpha=k.startAlpha,k.tint=A.startTint,k.rotation=M(A.startRotation),k.rotationSpeed=M(A.rotationSpeed)}function P(k,A,B,Z,L,q){let H=k.config;k.elapsed+=B;let j=H.duration>=0&&k.elapsed>=H.duration;if(k.playing&&!j&&H.spawnRate>0){k.spawnAccumulator+=H.spawnRate*B;let F=Math.floor(k.spawnAccumulator);k.spawnAccumulator-=F;for(let U=0;U<F;U++){if(k.activeCount>=H.maxParticles)break;let z=A.particles[k.activeCount];if(!z)break;b(z,H,Z,L,q),k.activeCount++}}if(k.pendingBurst>0){let F=Math.min(k.pendingBurst,H.maxParticles-k.activeCount);for(let U=0;U<F;U++){let z=A.particles[k.activeCount];if(!z)break;b(z,H,Z,L,q),k.activeCount++}k.pendingBurst-=F}let Q=H.gravity.x,V=H.gravity.y,J=Q!==0||V!==0,N=H.startTint!==H.endTint,K=0;while(K<k.activeCount){let F=A.particles[K];if(!F)break;if(F.life-=B,F.life<=0){if(k.activeCount--,K<k.activeCount){let z=A.particles[k.activeCount];if(z){A.particles[K]=z,A.particles[k.activeCount]=F;let h=A.pixiParticles[K];A.pixiParticles[K]=A.pixiParticles[k.activeCount],A.pixiParticles[k.activeCount]=h}}F.active=!1;continue}if(J)F.vx+=Q*B,F.vy+=V*B;F.x+=F.vx*B,F.y+=F.vy*B;let U=1-F.life/F.maxLife;if(F.size=F.startSize+(F.endSize-F.startSize)*U,F.alpha=F.startAlpha+(F.endAlpha-F.startAlpha)*U,N)F.tint=I(H.startTint,H.endTint,U);F.rotation+=F.rotationSpeed*B,K++}}function E(k){let A=Array(k);for(let B=0;B<k;B++)A[B]={active:!1,x:0,y:0,vx:0,vy:0,life:0,maxLife:0,size:0,startSize:0,endSize:0,alpha:0,startAlpha:0,endAlpha:0,tint:16777215,rotation:0,rotationSpeed:0};return A}var o={explosion(k,A){return G({maxParticles:50,texture:k,spawnRate:0,burstCount:30,duration:1,lifetime:[0.3,0.8],speed:[100,300],angle:[0,O],startSize:[0.5,1.5],endSize:[0.1,0.3],startAlpha:1,endAlpha:0,...A})},smoke(k,A){return G({maxParticles:60,texture:k,spawnRate:15,duration:-1,lifetime:[1,3],speed:[20,60],angle:[-Math.PI/2-0.3,-Math.PI/2+0.3],startSize:[0.3,0.6],endSize:[1,2],startAlpha:0.4,endAlpha:0,...A})},fire(k,A){return G({maxParticles:80,texture:k,spawnRate:30,duration:-1,lifetime:[0.3,1],speed:[40,120],angle:[-Math.PI/2-0.5,-Math.PI/2+0.5],startSize:[0.5,1],endSize:[0.1,0.3],startAlpha:1,endAlpha:0,startTint:16746496,endTint:16720384,blendMode:"add",...A})},sparkle(k,A){return G({maxParticles:30,texture:k,spawnRate:10,duration:-1,lifetime:[0.5,1.5],speed:[10,40],angle:[0,O],startSize:[0.2,0.8],endSize:[0.1,0.4],startAlpha:[0.5,1],endAlpha:0,...A})},trail(k,A){return G({maxParticles:40,texture:k,spawnRate:20,duration:-1,lifetime:[0.3,0.8],speed:0,startSize:[0.5,1],endSize:[0.05,0.2],startAlpha:0.8,endAlpha:0,...A})}};function c(k){let{systemGroup:A="particles",priority:B=0,phase:Z="update"}=k??{},L=new Map;return v({id:"particles",install(q){q.registerRequired("particleEmitter","localTransform",()=>({x:0,y:0,rotation:0,scaleX:1,scaleY:1})),q.registerDispose("particleEmitter",({entityId:H})=>{let j=L.get(H);if(j){let $=j.pixiContainer;if($)$.removeFromParent?.(),$.destroy?.();L.delete(H)}}),q.addSystem("particle-update").setPriority(B).inPhase(Z).inGroup(A).addQuery("emitters",{with:["particleEmitter"]}).setProcess(({queries:H,dt:j,ecs:$})=>{for(let Q of H.emitters){let V=Q.components.particleEmitter,J=L.get(Q.id);if(!J)J={particles:E(V.config.maxParticles),pixiContainer:null,pixiParticles:[]},L.set(Q.id,J);let N=$.getComponent(Q.id,"worldTransform"),K=N?.x??0,F=N?.y??0,U=N?.rotation??0;P(V,J,j,K,F,U);let z=V.config;if(z.duration>=0&&V.elapsed>=z.duration&&V.activeCount===0&&!V.finished){if(V.finished=!0,V.onComplete)V.onComplete({entityId:Q.id});$.commands.removeComponent(Q.id,"particleEmitter")}}}),q.addSystem("particle-render-sync").setPriority(400).inPhase("render").inGroup(A).setOnInitialize(async(H)=>{let j=await import("pixi.js"),$=j.ParticleContainer,Q=j.Particle,V=H.tryGetResource("rootContainer");H.addReactiveQuery("particle-emitters",{with:["particleEmitter"],onEnter:(J)=>{let K=J.components.particleEmitter.config,F=new $({dynamicProperties:{position:!0,rotation:!0,color:!0,vertex:!0}});F.blendMode=K.blendMode;let U=[];for(let h=0;h<K.maxParticles;h++){let S=new Q({texture:K.texture});S.alpha=0,U.push(S),F.addParticle(S)}let z=E(K.maxParticles);if(V)if(H.getComponent(J.id,"renderLayer"))V.addChild(F);else V.addChild(F);L.set(J.id,{particles:z,pixiContainer:F,pixiParticles:U})},onExit:(J)=>{let N=L.get(J);if(N){let K=N.pixiContainer;if(K)K.removeFromParent?.(),K.destroy?.();L.delete(J)}}})}).setProcess(({ecs:H})=>{for(let[j,$]of L){let Q=H.getComponent(j,"particleEmitter");if(!Q)continue;let V=Q.config;if(!V.worldSpace){let J=H.getComponent(j,"worldTransform");if(J){let N=$.pixiContainer;N.position.set(J.x,J.y),N.rotation=J.rotation,N.scale.set(J.scaleX,J.scaleY)}}for(let J=0;J<Q.activeCount;J++){let N=$.particles[J],K=$.pixiParticles[J];if(!N||!K)continue;K.x=N.x,K.y=N.y,K.scaleX=N.size,K.scaleY=N.size,K.rotation=N.rotation,K.tint=N.tint,K.alpha=N.alpha}for(let J=Q.activeCount;J<V.maxParticles;J++){let N=$.pixiParticles[J];if(N)N.alpha=0}}})}})}function p(k,A){return k.get(A)}export{s as stopEmitter,M as sampleRange,m as resumeEmitter,o as particlePresets,I as lerpTint,p as getEmitterData,G as defineParticleEffect,c as createParticlePlugin,l as createParticleEmitter,d as burstParticles};
2
+
3
+ //# debugId=4CA5165A66C2506A64756E2164756E21
4
+ //# sourceMappingURL=particles.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * Particle System Plugin for ECSpresso\n *\n * High-performance particle system where particles live outside the ECS in\n * pre-allocated pools. Renders via PixiJS v8's ParticleContainer + Particle API.\n * Renderer2D is a required dependency.\n *\n * Follows the established plugin pattern: immutable shared config\n * (ParticleEffectConfig) + mutable per-entity state (ParticleEmitter) component,\n * side-storage Map for PixiJS objects, kit pattern for typed helpers.\n */\n\nimport { definePlugin, type Plugin, type BasePluginOptions } from 'ecspresso';\nimport type { BaseWorld } from 'ecspresso';\nimport type { WorldConfigFrom } from '../type-utils';\nimport type { TransformComponentTypes, LocalTransform } from 'ecspresso/plugins/transform';\n\n/** BaseWorld narrowed to particle components for typed access in helpers. */\ntype ParticleWorld = BaseWorld<ParticleComponentTypes>;\n\n// ==================== Value Types ====================\n\n/** Fixed value or random range [min, max] */\nexport type ParticleValue = number | readonly [number, number];\n\n/** Emission geometry */\nexport type EmissionShape = 'point' | 'circle';\n\n/** Blend modes for particle rendering */\nexport type ParticleBlendMode = 'normal' | 'add' | 'multiply' | 'screen';\n\n// ==================== Config Types ====================\n\n/**\n * User-facing config input for defining a particle effect.\n * All properties optional except maxParticles and texture.\n */\nexport interface ParticleEffectInput {\n\t/** Pool size — maximum simultaneous particles */\n\tmaxParticles: number;\n\t/** PixiJS Texture for particles */\n\ttexture: unknown;\n\t/** Particles per second (0 = burst-only, default: 10) */\n\tspawnRate?: number;\n\t/** Particles per burst (default: 0) */\n\tburstCount?: number;\n\t/** Emitter lifetime in seconds (-1 = infinite, default: -1) */\n\tduration?: number;\n\t/** Per-particle lifetime in seconds (default: 1) */\n\tlifetime?: ParticleValue;\n\t/** Initial speed in pixels/second (default: 100) */\n\tspeed?: ParticleValue;\n\t/** Emission direction in radians (default: [0, 2*PI]) */\n\tangle?: ParticleValue;\n\t/** Spawn geometry (default: 'point') */\n\temissionShape?: EmissionShape;\n\t/** Radius for 'circle' shape (default: 0) */\n\temissionRadius?: number;\n\t/** Acceleration in pixels/second^2 (default: {x: 0, y: 0}) */\n\tgravity?: { readonly x: number; readonly y: number };\n\t/** Initial scale (default: 1) */\n\tstartSize?: ParticleValue;\n\t/** Final scale (default: same as startSize) */\n\tendSize?: ParticleValue;\n\t/** Initial opacity (default: 1) */\n\tstartAlpha?: ParticleValue;\n\t/** Final opacity (default: 0) */\n\tendAlpha?: ParticleValue;\n\t/** Initial hex color (default: 0xffffff) */\n\tstartTint?: number;\n\t/** Final hex color (default: same as startTint) */\n\tendTint?: number;\n\t/** Initial rotation in radians (default: 0) */\n\tstartRotation?: ParticleValue;\n\t/** Rotation velocity in rad/s (default: 0) */\n\trotationSpeed?: ParticleValue;\n\t/** Blend mode (default: 'normal') */\n\tblendMode?: ParticleBlendMode;\n\t/** Particles in world coordinates (default: true) */\n\tworldSpace?: boolean;\n}\n\n/**\n * Frozen, fully-resolved particle effect config.\n * Output of defineParticleEffect.\n */\nexport interface ParticleEffectConfig {\n\treadonly maxParticles: number;\n\treadonly texture: unknown;\n\treadonly spawnRate: number;\n\treadonly burstCount: number;\n\treadonly duration: number;\n\treadonly lifetime: ParticleValue;\n\treadonly speed: ParticleValue;\n\treadonly angle: ParticleValue;\n\treadonly emissionShape: EmissionShape;\n\treadonly emissionRadius: number;\n\treadonly gravity: { readonly x: number; readonly y: number };\n\treadonly startSize: ParticleValue;\n\treadonly endSize: ParticleValue;\n\treadonly startAlpha: ParticleValue;\n\treadonly endAlpha: ParticleValue;\n\treadonly startTint: number;\n\treadonly endTint: number;\n\treadonly startRotation: ParticleValue;\n\treadonly rotationSpeed: ParticleValue;\n\treadonly blendMode: ParticleBlendMode;\n\treadonly worldSpace: boolean;\n}\n\n// ==================== Per-Particle Pool Element ====================\n\n/**\n * Mutable per-particle state. Pre-allocated, never GC'd.\n */\nexport interface ParticleState {\n\tactive: boolean;\n\tx: number;\n\ty: number;\n\tvx: number;\n\tvy: number;\n\tlife: number;\n\tmaxLife: number;\n\tsize: number;\n\tstartSize: number;\n\tendSize: number;\n\talpha: number;\n\tstartAlpha: number;\n\tendAlpha: number;\n\ttint: number;\n\trotation: number;\n\trotationSpeed: number;\n}\n\n// ==================== ECS Component ====================\n\n/**\n * Per-entity emitter state stored as an ECS component.\n */\nexport interface ParticleEmitter {\n\treadonly config: ParticleEffectConfig;\n\tactiveCount: number;\n\tspawnAccumulator: number;\n\telapsed: number;\n\tplaying: boolean;\n\tpendingBurst: number;\n\tfinished: boolean;\n\tonComplete?: (data: ParticleEmitterEventData) => void;\n}\n\n/**\n * Component types provided by the particle plugin.\n */\nexport interface ParticleComponentTypes {\n\tparticleEmitter: ParticleEmitter;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Data published when an emitter completes.\n */\nexport interface ParticleEmitterEventData {\n\tentityId: number;\n}\n\n// ==================== Plugin Options ====================\n\nexport interface ParticlePluginOptions<G extends string = 'particles'> extends BasePluginOptions<G> {}\n\n// ==================== Pure Functions (Simulation Engine) ====================\n\n/**\n * Sample a ParticleValue: returns fixed value or random within [min, max].\n */\nexport function sampleRange(value: ParticleValue): number {\n\tif (typeof value === 'number') return value;\n\tconst [min, max] = value;\n\treturn min + Math.random() * (max - min);\n}\n\n/**\n * Linear interpolation between two hex colors (RGB channels).\n */\nexport function lerpTint(start: number, end: number, t: number): number {\n\tif (start === end) return start;\n\tconst sr = (start >> 16) & 0xff;\n\tconst sg = (start >> 8) & 0xff;\n\tconst sb = start & 0xff;\n\tconst er = (end >> 16) & 0xff;\n\tconst eg = (end >> 8) & 0xff;\n\tconst eb = end & 0xff;\n\tconst r = (sr + (er - sr) * t) | 0;\n\tconst g = (sg + (eg - sg) * t) | 0;\n\tconst b = (sb + (eb - sb) * t) | 0;\n\treturn (r << 16) | (g << 8) | b;\n}\n\n// ==================== Config Builder ====================\n\nconst TWO_PI = Math.PI * 2;\n\n/**\n * Define a particle effect config with defaults applied and frozen.\n */\nexport function defineParticleEffect(input: ParticleEffectInput): ParticleEffectConfig {\n\tconst startSize = input.startSize ?? 1;\n\tconst startTint = input.startTint ?? 0xffffff;\n\treturn Object.freeze({\n\t\tmaxParticles: input.maxParticles,\n\t\ttexture: input.texture,\n\t\tspawnRate: input.spawnRate ?? 10,\n\t\tburstCount: input.burstCount ?? 0,\n\t\tduration: input.duration ?? -1,\n\t\tlifetime: input.lifetime ?? 1,\n\t\tspeed: input.speed ?? 100,\n\t\tangle: input.angle ?? [0, TWO_PI] as const,\n\t\temissionShape: input.emissionShape ?? 'point',\n\t\temissionRadius: input.emissionRadius ?? 0,\n\t\tgravity: Object.freeze(input.gravity ?? { x: 0, y: 0 }),\n\t\tstartSize,\n\t\tendSize: input.endSize ?? startSize,\n\t\tstartAlpha: input.startAlpha ?? 1,\n\t\tendAlpha: input.endAlpha ?? 0,\n\t\tstartTint,\n\t\tendTint: input.endTint ?? startTint,\n\t\tstartRotation: input.startRotation ?? 0,\n\t\trotationSpeed: input.rotationSpeed ?? 0,\n\t\tblendMode: input.blendMode ?? 'normal',\n\t\tworldSpace: input.worldSpace ?? true,\n\t});\n}\n\n// ==================== Component Factory ====================\n\n/**\n * Create a particleEmitter component suitable for spreading into spawn().\n */\nexport function createParticleEmitter(\n\tconfig: ParticleEffectConfig,\n\toptions?: {\n\t\tplaying?: boolean;\n\t\tonComplete?: (data: ParticleEmitterEventData) => void;\n\t},\n): Pick<ParticleComponentTypes, 'particleEmitter'> {\n\treturn {\n\t\tparticleEmitter: {\n\t\t\tconfig,\n\t\t\tactiveCount: 0,\n\t\t\tspawnAccumulator: 0,\n\t\t\telapsed: 0,\n\t\t\tplaying: options?.playing ?? true,\n\t\t\tpendingBurst: 0,\n\t\t\tfinished: false,\n\t\t\tonComplete: options?.onComplete,\n\t\t},\n\t};\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Queue a burst of particles on an emitter.\n * Returns false if entity has no particleEmitter component.\n */\nexport function burstParticles(\n\tecs: ParticleWorld,\n\tentityId: number,\n\tcount?: number,\n): boolean {\n\tconst emitter = ecs.getComponent(entityId, 'particleEmitter');\n\tif (!emitter) return false;\n\temitter.pendingBurst += count ?? emitter.config.burstCount;\n\tecs.markChanged(entityId, 'particleEmitter');\n\treturn true;\n}\n\n/**\n * Stop an emitter from spawning new particles.\n * Existing particles continue their lifecycle.\n */\nexport function stopEmitter(\n\tecs: ParticleWorld,\n\tentityId: number,\n): boolean {\n\tconst emitter = ecs.getComponent(entityId, 'particleEmitter');\n\tif (!emitter) return false;\n\temitter.playing = false;\n\treturn true;\n}\n\n/**\n * Resume a stopped emitter.\n */\nexport function resumeEmitter(\n\tecs: ParticleWorld,\n\tentityId: number,\n): boolean {\n\tconst emitter = ecs.getComponent(entityId, 'particleEmitter');\n\tif (!emitter) return false;\n\temitter.playing = true;\n\treturn true;\n}\n\n// ==================== Side Storage ====================\n\n/**\n * Runtime data stored outside the ECS, keyed by entity ID.\n */\nexport interface EmitterRuntimeData {\n\tparticles: ParticleState[];\n\tpixiContainer: unknown;\n\tpixiParticles: unknown[];\n}\n\n// ==================== Spawn Logic ====================\n\nfunction spawnParticle(\n\tparticle: ParticleState,\n\tconfig: ParticleEffectConfig,\n\temitterX: number,\n\temitterY: number,\n\temitterRotation: number,\n): void {\n\tparticle.active = true;\n\tconst life = sampleRange(config.lifetime);\n\tparticle.life = life;\n\tparticle.maxLife = life;\n\n\t// Position from emission shape\n\tif (config.emissionShape === 'circle' && config.emissionRadius > 0) {\n\t\tconst angle = Math.random() * TWO_PI;\n\t\tconst radius = Math.random() * config.emissionRadius;\n\t\tparticle.x = emitterX + Math.cos(angle) * radius;\n\t\tparticle.y = emitterY + Math.sin(angle) * radius;\n\t} else {\n\t\tparticle.x = emitterX;\n\t\tparticle.y = emitterY;\n\t}\n\n\t// Velocity from speed + angle + emitter rotation\n\tconst speed = sampleRange(config.speed);\n\tconst angle = sampleRange(config.angle) + emitterRotation;\n\tparticle.vx = Math.cos(angle) * speed;\n\tparticle.vy = Math.sin(angle) * speed;\n\n\t// Visual properties\n\tparticle.startSize = sampleRange(config.startSize);\n\tparticle.endSize = sampleRange(config.endSize);\n\tparticle.size = particle.startSize;\n\tparticle.startAlpha = sampleRange(config.startAlpha);\n\tparticle.endAlpha = sampleRange(config.endAlpha);\n\tparticle.alpha = particle.startAlpha;\n\tparticle.tint = config.startTint;\n\tparticle.rotation = sampleRange(config.startRotation);\n\tparticle.rotationSpeed = sampleRange(config.rotationSpeed);\n}\n\n// ==================== Update Logic ====================\n\nfunction updateParticles(\n\temitter: ParticleEmitter,\n\tdata: EmitterRuntimeData,\n\tdt: number,\n\temitterX: number,\n\temitterY: number,\n\temitterRotation: number,\n): void {\n\tconst config = emitter.config;\n\n\t// Update emitter elapsed time\n\temitter.elapsed += dt;\n\n\t// Determine if spawning is allowed\n\tconst durationExpired = config.duration >= 0 && emitter.elapsed >= config.duration;\n\tconst canSpawn = emitter.playing && !durationExpired;\n\n\t// Continuous spawning\n\tif (canSpawn && config.spawnRate > 0) {\n\t\temitter.spawnAccumulator += config.spawnRate * dt;\n\t\tconst toSpawn = Math.floor(emitter.spawnAccumulator);\n\t\temitter.spawnAccumulator -= toSpawn;\n\n\t\tfor (let i = 0; i < toSpawn; i++) {\n\t\t\tif (emitter.activeCount >= config.maxParticles) break;\n\t\t\tconst particle = data.particles[emitter.activeCount];\n\t\t\tif (!particle) break;\n\t\t\tspawnParticle(particle, config, emitterX, emitterY, emitterRotation);\n\t\t\temitter.activeCount++;\n\t\t}\n\t}\n\n\t// Burst spawning\n\tif (emitter.pendingBurst > 0) {\n\t\tconst burstCount = Math.min(\n\t\t\temitter.pendingBurst,\n\t\t\tconfig.maxParticles - emitter.activeCount,\n\t\t);\n\t\tfor (let i = 0; i < burstCount; i++) {\n\t\t\tconst particle = data.particles[emitter.activeCount];\n\t\t\tif (!particle) break;\n\t\t\tspawnParticle(particle, config, emitterX, emitterY, emitterRotation);\n\t\t\temitter.activeCount++;\n\t\t}\n\t\temitter.pendingBurst -= burstCount;\n\t}\n\n\t// Update active particles\n\tconst gravityX = config.gravity.x;\n\tconst gravityY = config.gravity.y;\n\tconst hasGravity = gravityX !== 0 || gravityY !== 0;\n\tconst hasTintLerp = config.startTint !== config.endTint;\n\n\tlet i = 0;\n\twhile (i < emitter.activeCount) {\n\t\tconst p = data.particles[i];\n\t\tif (!p) break;\n\n\t\tp.life -= dt;\n\n\t\tif (p.life <= 0) {\n\t\t\t// Swap-and-pop: move last active particle to this slot\n\t\t\temitter.activeCount--;\n\t\t\tif (i < emitter.activeCount) {\n\t\t\t\tconst last = data.particles[emitter.activeCount];\n\t\t\t\tif (last) {\n\t\t\t\t\t// Copy last particle data to current slot\n\t\t\t\t\tdata.particles[i] = last;\n\t\t\t\t\tdata.particles[emitter.activeCount] = p;\n\t\t\t\t\t// Also swap PixiJS particle refs\n\t\t\t\t\tconst tmpPixi = data.pixiParticles[i];\n\t\t\t\t\tdata.pixiParticles[i] = data.pixiParticles[emitter.activeCount];\n\t\t\t\t\tdata.pixiParticles[emitter.activeCount] = tmpPixi;\n\t\t\t\t}\n\t\t\t}\n\t\t\tp.active = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Physics\n\t\tif (hasGravity) {\n\t\t\tp.vx += gravityX * dt;\n\t\t\tp.vy += gravityY * dt;\n\t\t}\n\t\tp.x += p.vx * dt;\n\t\tp.y += p.vy * dt;\n\n\t\t// Interpolation\n\t\tconst t = 1 - p.life / p.maxLife;\n\t\tp.size = p.startSize + (p.endSize - p.startSize) * t;\n\t\tp.alpha = p.startAlpha + (p.endAlpha - p.startAlpha) * t;\n\n\t\tif (hasTintLerp) {\n\t\t\tp.tint = lerpTint(config.startTint, config.endTint, t);\n\t\t}\n\n\t\t// Rotation\n\t\tp.rotation += p.rotationSpeed * dt;\n\n\t\ti++;\n\t}\n}\n\n// ==================== Pool Allocation ====================\n\nfunction createParticlePool(maxParticles: number): ParticleState[] {\n\tconst pool: ParticleState[] = new Array(maxParticles);\n\tfor (let i = 0; i < maxParticles; i++) {\n\t\tpool[i] = {\n\t\t\tactive: false,\n\t\t\tx: 0, y: 0,\n\t\t\tvx: 0, vy: 0,\n\t\t\tlife: 0, maxLife: 0,\n\t\t\tsize: 0,\n\t\t\tstartSize: 0, endSize: 0,\n\t\t\talpha: 0,\n\t\t\tstartAlpha: 0, endAlpha: 0,\n\t\t\ttint: 0xffffff,\n\t\t\trotation: 0,\n\t\t\trotationSpeed: 0,\n\t\t};\n\t}\n\treturn pool;\n}\n\n// ==================== Presets ====================\n\nexport const particlePresets = {\n\texplosion(texture: unknown, overrides?: Partial<ParticleEffectInput>): ParticleEffectConfig {\n\t\treturn defineParticleEffect({\n\t\t\tmaxParticles: 50,\n\t\t\ttexture,\n\t\t\tspawnRate: 0,\n\t\t\tburstCount: 30,\n\t\t\tduration: 1,\n\t\t\tlifetime: [0.3, 0.8],\n\t\t\tspeed: [100, 300],\n\t\t\tangle: [0, TWO_PI],\n\t\t\tstartSize: [0.5, 1.5],\n\t\t\tendSize: [0.1, 0.3],\n\t\t\tstartAlpha: 1,\n\t\t\tendAlpha: 0,\n\t\t\t...overrides,\n\t\t});\n\t},\n\n\tsmoke(texture: unknown, overrides?: Partial<ParticleEffectInput>): ParticleEffectConfig {\n\t\treturn defineParticleEffect({\n\t\t\tmaxParticles: 60,\n\t\t\ttexture,\n\t\t\tspawnRate: 15,\n\t\t\tduration: -1,\n\t\t\tlifetime: [1, 3],\n\t\t\tspeed: [20, 60],\n\t\t\tangle: [-Math.PI / 2 - 0.3, -Math.PI / 2 + 0.3],\n\t\t\tstartSize: [0.3, 0.6],\n\t\t\tendSize: [1, 2],\n\t\t\tstartAlpha: 0.4,\n\t\t\tendAlpha: 0,\n\t\t\t...overrides,\n\t\t});\n\t},\n\n\tfire(texture: unknown, overrides?: Partial<ParticleEffectInput>): ParticleEffectConfig {\n\t\treturn defineParticleEffect({\n\t\t\tmaxParticles: 80,\n\t\t\ttexture,\n\t\t\tspawnRate: 30,\n\t\t\tduration: -1,\n\t\t\tlifetime: [0.3, 1],\n\t\t\tspeed: [40, 120],\n\t\t\tangle: [-Math.PI / 2 - 0.5, -Math.PI / 2 + 0.5],\n\t\t\tstartSize: [0.5, 1],\n\t\t\tendSize: [0.1, 0.3],\n\t\t\tstartAlpha: 1,\n\t\t\tendAlpha: 0,\n\t\t\tstartTint: 0xff8800,\n\t\t\tendTint: 0xff2200,\n\t\t\tblendMode: 'add',\n\t\t\t...overrides,\n\t\t});\n\t},\n\n\tsparkle(texture: unknown, overrides?: Partial<ParticleEffectInput>): ParticleEffectConfig {\n\t\treturn defineParticleEffect({\n\t\t\tmaxParticles: 30,\n\t\t\ttexture,\n\t\t\tspawnRate: 10,\n\t\t\tduration: -1,\n\t\t\tlifetime: [0.5, 1.5],\n\t\t\tspeed: [10, 40],\n\t\t\tangle: [0, TWO_PI],\n\t\t\tstartSize: [0.2, 0.8],\n\t\t\tendSize: [0.1, 0.4],\n\t\t\tstartAlpha: [0.5, 1],\n\t\t\tendAlpha: 0,\n\t\t\t...overrides,\n\t\t});\n\t},\n\n\ttrail(texture: unknown, overrides?: Partial<ParticleEffectInput>): ParticleEffectConfig {\n\t\treturn defineParticleEffect({\n\t\t\tmaxParticles: 40,\n\t\t\ttexture,\n\t\t\tspawnRate: 20,\n\t\t\tduration: -1,\n\t\t\tlifetime: [0.3, 0.8],\n\t\t\tspeed: 0,\n\t\t\tstartSize: [0.5, 1],\n\t\t\tendSize: [0.05, 0.2],\n\t\t\tstartAlpha: 0.8,\n\t\t\tendAlpha: 0,\n\t\t\t...overrides,\n\t\t});\n\t},\n} as const;\n\n// ==================== Plugin Factory ====================\n\ntype ParticleLabels = 'particle-update' | 'particle-render-sync';\n\ntype ParticleRequires = WorldConfigFrom<TransformComponentTypes & { renderLayer: string }>;\n\n/**\n * Create a particle system plugin for ECSpresso.\n *\n * Provides:\n * - Pre-allocated particle pools outside the entity system\n * - Continuous and burst emission modes\n * - Velocity, gravity, lifetime, interpolation (size, alpha, tint, rotation)\n * - World-space and local-space particle emission\n * - PixiJS ParticleContainer rendering (via renderer2D dependency)\n * - Presets for common effects (explosion, smoke, fire, sparkle, trail)\n *\n * Renderer2D is a required dependency.\n */\nexport function createParticlePlugin<\n\tG extends string = 'particles',\n>(\n\toptions?: ParticlePluginOptions<G>,\n): Plugin<WorldConfigFrom<ParticleComponentTypes>, ParticleRequires, ParticleLabels, G, never, 'particle-emitters'> {\n\tconst {\n\t\tsystemGroup = 'particles',\n\t\tpriority = 0,\n\t\tphase = 'update',\n\t} = options ?? {};\n\n\t// Side storage for runtime particle data\n\tconst emitterData = new Map<number, EmitterRuntimeData>();\n\n\treturn definePlugin<WorldConfigFrom<ParticleComponentTypes>, ParticleRequires, ParticleLabels, G, never, 'particle-emitters'>({\n\t\tid: 'particles',\n\t\tinstall(world) {\n\t\t\t// Required component: particleEmitter needs localTransform\n\t\t\tworld.registerRequired('particleEmitter', 'localTransform', (): LocalTransform => ({\n\t\t\t\tx: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1,\n\t\t\t}));\n\n\t\t\t// Dispose: clean up side storage when particleEmitter removed\n\t\t\tworld.registerDispose('particleEmitter', ({ entityId }: { value: ParticleEmitter; entityId: number }) => {\n\t\t\t\tconst data = emitterData.get(entityId);\n\t\t\t\tif (data) {\n\t\t\t\t\t// Remove PixiJS container from scene graph\n\t\t\t\t\tconst container = data.pixiContainer as { removeFromParent?: () => void; destroy?: () => void } | null;\n\t\t\t\t\tif (container) {\n\t\t\t\t\t\tcontainer.removeFromParent?.();\n\t\t\t\t\t\tcontainer.destroy?.();\n\t\t\t\t\t}\n\t\t\t\t\temitterData.delete(entityId);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// ==================== Particle Update System ====================\n\t\t\tworld\n\t\t\t\t.addSystem('particle-update')\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('emitters', {\n\t\t\t\t\twith: ['particleEmitter'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, dt, ecs }) => {\n\t\t\t\t\tfor (const entity of queries.emitters) {\n\t\t\t\t\t\tconst emitter = entity.components.particleEmitter;\n\n\t\t\t\t\t\t// Lazily create particle pool on first encounter\n\t\t\t\t\t\tlet data = emitterData.get(entity.id);\n\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\tdata = {\n\t\t\t\t\t\t\t\tparticles: createParticlePool(emitter.config.maxParticles),\n\t\t\t\t\t\t\t\tpixiContainer: null,\n\t\t\t\t\t\t\t\tpixiParticles: [],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\temitterData.set(entity.id, data);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst worldTransform = ecs.getComponent(entity.id, 'worldTransform');\n\t\t\t\t\t\tconst ex = worldTransform?.x ?? 0;\n\t\t\t\t\t\tconst ey = worldTransform?.y ?? 0;\n\t\t\t\t\t\tconst erot = worldTransform?.rotation ?? 0;\n\n\t\t\t\t\t\tupdateParticles(emitter, data, dt, ex, ey, erot);\n\n\t\t\t\t\t\t// Check completion\n\t\t\t\t\t\tconst config = emitter.config;\n\t\t\t\t\t\tconst durationExpired = config.duration >= 0 && emitter.elapsed >= config.duration;\n\t\t\t\t\t\tif (durationExpired && emitter.activeCount === 0 && !emitter.finished) {\n\t\t\t\t\t\t\temitter.finished = true;\n\n\t\t\t\t\t\t\tif (emitter.onComplete) {\n\t\t\t\t\t\t\t\temitter.onComplete({ entityId: entity.id });\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tecs.commands.removeComponent(entity.id, 'particleEmitter');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// ==================== Particle Render Sync System ====================\n\t\t\tworld\n\t\t\t\t.addSystem('particle-render-sync')\n\t\t\t\t.setPriority(400)\n\t\t\t\t.inPhase('render')\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setOnInitialize(async (ecs) => {\n\t\t\t\t\t// Dynamic import PixiJS\n\t\t\t\t\tconst pixi = await import('pixi.js');\n\t\t\t\t\tconst ParticleContainerClass = pixi.ParticleContainer;\n\t\t\t\t\tconst ParticleClass = pixi.Particle;\n\n\t\t\t\t\t// Get root container\n\t\t\t\t\tconst rootContainer = ecs.tryGetResource<{ addChild(child: unknown): void }>('rootContainer');\n\n\t\t\t\t\t// Reactive query for particleEmitter component\n\t\t\t\t\tecs.addReactiveQuery('particle-emitters', {\n\t\t\t\t\t\twith: ['particleEmitter'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst emitter = entity.components.particleEmitter;\n\t\t\t\t\t\t\tconst config = emitter.config;\n\n\t\t\t\t\t\t\t// Create PixiJS ParticleContainer\n\t\t\t\t\t\t\tconst pixiContainer = new ParticleContainerClass({\n\t\t\t\t\t\t\t\tdynamicProperties: {\n\t\t\t\t\t\t\t\t\tposition: true,\n\t\t\t\t\t\t\t\t\trotation: true,\n\t\t\t\t\t\t\t\t\tcolor: true,\n\t\t\t\t\t\t\t\t\tvertex: true,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Set blend mode\n\t\t\t\t\t\t\tpixiContainer.blendMode = config.blendMode;\n\n\t\t\t\t\t\t\t// Pre-allocate Particle objects\n\t\t\t\t\t\t\tconst pixiParticles: InstanceType<typeof ParticleClass>[] = [];\n\t\t\t\t\t\t\tfor (let i = 0; i < config.maxParticles; i++) {\n\t\t\t\t\t\t\t\tconst p = new ParticleClass({\n\t\t\t\t\t\t\t\t\ttexture: config.texture,\n\t\t\t\t\t\t\t\t} as ConstructorParameters<typeof ParticleClass>[0]);\n\t\t\t\t\t\t\t\tp.alpha = 0;\n\t\t\t\t\t\t\t\tpixiParticles.push(p);\n\t\t\t\t\t\t\t\tpixiContainer.addParticle(p);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Create pre-allocated pool\n\t\t\t\t\t\t\tconst particles = createParticlePool(config.maxParticles);\n\n\t\t\t\t\t\t\t// Add to scene (cross-plugin structural access for renderLayer)\n\t\t\t\t\t\t\tif (rootContainer) {\n\t\t\t\t\t\t\t\tconst layerName = ecs.getComponent(entity.id, 'renderLayer');\n\t\t\t\t\t\t\t\tif (layerName) {\n\t\t\t\t\t\t\t\t\t(rootContainer as { addChild(child: unknown): void }).addChild(pixiContainer);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t(rootContainer as { addChild(child: unknown): void }).addChild(pixiContainer);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Store in side storage\n\t\t\t\t\t\t\temitterData.set(entity.id, {\n\t\t\t\t\t\t\t\tparticles,\n\t\t\t\t\t\t\t\tpixiContainer,\n\t\t\t\t\t\t\t\tpixiParticles,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tconst data = emitterData.get(entityId);\n\t\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\t\tconst container = data.pixiContainer as { removeFromParent?: () => void; destroy?: () => void } | null;\n\t\t\t\t\t\t\t\tif (container) {\n\t\t\t\t\t\t\t\t\tcontainer.removeFromParent?.();\n\t\t\t\t\t\t\t\t\tcontainer.destroy?.();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\temitterData.delete(entityId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t})\n\t\t\t\t.setProcess(({ ecs }) => {\n\t\t\t\t\t// Sync ParticleState -> PixiJS Particle properties\n\t\t\t\t\tfor (const [entityId, data] of emitterData) {\n\t\t\t\t\t\tconst emitter = ecs.getComponent(entityId, 'particleEmitter');\n\t\t\t\t\t\tif (!emitter) continue;\n\n\t\t\t\t\t\tconst config = emitter.config;\n\n\t\t\t\t\t\t// Local-space: sync container position to emitter's worldTransform\n\t\t\t\t\t\tif (!config.worldSpace) {\n\t\t\t\t\t\t\tconst wt = ecs.getComponent(entityId, 'worldTransform');\n\t\t\t\t\t\t\tif (wt) {\n\t\t\t\t\t\t\t\tconst container = data.pixiContainer as {\n\t\t\t\t\t\t\t\t\tposition: { set(x: number, y: number): void };\n\t\t\t\t\t\t\t\t\trotation: number;\n\t\t\t\t\t\t\t\t\tscale: { set(x: number, y: number): void };\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tcontainer.position.set(wt.x, wt.y);\n\t\t\t\t\t\t\t\tcontainer.rotation = wt.rotation;\n\t\t\t\t\t\t\t\tcontainer.scale.set(wt.scaleX, wt.scaleY);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Sync active particles\n\t\t\t\t\t\tfor (let i = 0; i < emitter.activeCount; i++) {\n\t\t\t\t\t\t\tconst ps = data.particles[i];\n\t\t\t\t\t\t\tconst pp = data.pixiParticles[i] as {\n\t\t\t\t\t\t\t\tx: number;\n\t\t\t\t\t\t\t\ty: number;\n\t\t\t\t\t\t\t\tscaleX: number;\n\t\t\t\t\t\t\t\tscaleY: number;\n\t\t\t\t\t\t\t\trotation: number;\n\t\t\t\t\t\t\t\ttint: number;\n\t\t\t\t\t\t\t\talpha: number;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (!ps || !pp) continue;\n\t\t\t\t\t\t\tpp.x = ps.x;\n\t\t\t\t\t\t\tpp.y = ps.y;\n\t\t\t\t\t\t\tpp.scaleX = ps.size;\n\t\t\t\t\t\t\tpp.scaleY = ps.size;\n\t\t\t\t\t\t\tpp.rotation = ps.rotation;\n\t\t\t\t\t\t\tpp.tint = ps.tint;\n\t\t\t\t\t\t\tpp.alpha = ps.alpha;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Hide inactive particles\n\t\t\t\t\t\tfor (let i = emitter.activeCount; i < config.maxParticles; i++) {\n\t\t\t\t\t\t\tconst pp = data.pixiParticles[i] as { alpha: number } | undefined;\n\t\t\t\t\t\t\tif (pp) {\n\t\t\t\t\t\t\t\tpp.alpha = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t},\n\t});\n}\n\n/**\n * Get the runtime data for an emitter entity.\n * Useful for tests and advanced usage.\n * @internal Exported for testing only.\n */\nexport function getEmitterData(\n\temitterDataMap: Map<number, EmitterRuntimeData>,\n\tentityId: number,\n): EmitterRuntimeData | undefined {\n\treturn emitterDataMap.get(entityId);\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "2C024B5960E7854064756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "4CA5165A66C2506A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,4 @@
1
+ var{defineProperty:N,getOwnPropertyNames:Y,getOwnPropertyDescriptor:w}=Object,I=Object.prototype.hasOwnProperty;function B(z){return this[z]}var b=(z)=>{var E=(q??=new WeakMap).get(z),J;if(E)return E;if(E=N({},"__esModule",{value:!0}),z&&typeof z==="object"||typeof z==="function"){for(var O of Y(z))if(!I.call(E,O))N(E,O,{get:B.bind(z,O),enumerable:!(J=w(z,O))||J.enumerable})}return q.set(z,E),E},q;var f=(z)=>z;function x(z,E){this[z]=f.bind(null,E)}var d=(z,E)=>{for(var J in E)N(z,J,{get:E[J],enumerable:!0,configurable:!0,set:x.bind(E,J)})};var r=(z,E)=>()=>(z&&(E=z(z=0)),E);var l=((z)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(z,{get:(E,J)=>(typeof require<"u"?require:E)[J]}):z)(function(z){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+z+'" is not supported')});import{definePlugin as u}from"ecspresso";function M(z,E,J,O,Z,Q,K){if(!Q&&!K)return null;let k={entityId:z,x:E,y:J,layer:O,collidesWith:Z};if(Q)k.x+=Q.offsetX??0,k.y+=Q.offsetY??0,k.aabb={halfWidth:Q.width/2,halfHeight:Q.height/2};if(K)k.x+=K.offsetX??0,k.y+=K.offsetY??0,k.circle={radius:K.radius};return k}function S(z){return z("spatialIndex")}function m(z,E,J,O,Z,Q,K,k){let _=Z-z,j=Q-E,$=J+K-Math.abs(_),F=O+k-Math.abs(j);if($<=0||F<=0)return null;if($<F)return{normalX:_>=0?1:-1,normalY:0,depth:$};return{normalX:0,normalY:j>=0?1:-1,depth:F}}function g(z,E,J,O,Z,Q){let K=O-z,k=Z-E,_=K*K+k*k,j=J+Q;if(_>=j*j)return null;let $=Math.sqrt(_);if($===0)return{normalX:1,normalY:0,depth:j};return{normalX:K/$,normalY:k/$,depth:j-$}}function C(z,E,J,O,Z,Q,K){let k=Math.max(z-J,Math.min(Z,z+J)),_=Math.max(E-O,Math.min(Q,E+O)),j=Z-k,$=Q-_,F=j*j+$*$;if(F>=K*K)return null;if(F===0){let W=Z-(z-J),L=z+J-Z,R=Q-(E-O),V=E+O-Q,U=Math.min(W,L,R,V);if(U===L)return{normalX:1,normalY:0,depth:L+K};if(U===W)return{normalX:-1,normalY:0,depth:W+K};if(U===V)return{normalX:0,normalY:1,depth:V+K};return{normalX:0,normalY:-1,depth:R+K}}let T=Math.sqrt(F);return{normalX:j/T,normalY:$/T,depth:K-T}}function A(z,E){if(z.aabb&&E.aabb)return m(z.x,z.y,z.aabb.halfWidth,z.aabb.halfHeight,E.x,E.y,E.aabb.halfWidth,E.aabb.halfHeight);if(z.circle&&E.circle)return g(z.x,z.y,z.circle.radius,E.x,E.y,E.circle.radius);if(z.aabb&&E.circle)return C(z.x,z.y,z.aabb.halfWidth,z.aabb.halfHeight,E.x,E.y,E.circle.radius);if(z.circle&&E.aabb){let J=C(E.x,E.y,E.aabb.halfWidth,E.aabb.halfHeight,z.x,z.y,z.circle.radius);if(!J)return null;return{normalX:-J.normalX,normalY:-J.normalY,depth:J.depth}}return null}var P=new Set;function X(z,E,J,O){if(E)h(z,E,J,O);else v(z,J,O)}function v(z,E,J){for(let O=0;O<z.length;O++){let Z=z[O];if(!Z)continue;for(let Q=O+1;Q<z.length;Q++){let K=z[Q];if(!K)continue;if(!Z.collidesWith.includes(K.layer)&&!K.collidesWith.includes(Z.layer))continue;let k=A(Z,K);if(!k)continue;E(Z,K,k,J)}}}function h(z,E,J,O){let Z=new Map;for(let Q=0;Q<z.length;Q++){let K=z[Q];if(!K)continue;Z.set(K.entityId,K)}for(let Q=0;Q<z.length;Q++){let K=z[Q];if(!K)continue;let k=K.aabb?K.aabb.halfWidth:K.circle?K.circle.radius:0,_=K.aabb?K.aabb.halfHeight:K.circle?K.circle.radius:0;P.clear(),E.queryRectInto(K.x-k,K.y-_,K.x+k,K.y+_,P);for(let j of P){if(j<=K.entityId)continue;let $=Z.get(j);if(!$)continue;if(!K.collidesWith.includes($.layer)&&!$.collidesWith.includes(K.layer))continue;let F=A(K,$);if(!F)continue;J(K,$,F,O)}}}function e(z,E){return{rigidBody:{type:z,mass:z==="static"?1/0:E?.mass??1,drag:E?.drag??0,restitution:E?.restitution??0,friction:E?.friction??0,gravityScale:E?.gravityScale??1},force:{x:0,y:0}}}function c(z,E){return{force:{x:z,y:E}}}function t(z,E,J,O){let Z=z.getComponent(E,"force");if(!Z)return;Z.x+=J,Z.y+=O}function a(z,E,J,O){let Z=z.getComponent(E,"velocity"),Q=z.getComponent(E,"rigidBody");if(!Z||!Q)return;if(Q.mass===1/0||Q.mass===0)return;Z.x+=J/Q.mass,Z.y+=O/Q.mass}function zz(z,E,J,O){let Z=z.getComponent(E,"velocity");if(!Z)return;Z.x=J,Z.y=O}function p(z,E,J,O){let Z=z.rigidBody.type==="dynamic"&&z.rigidBody.mass>0&&z.rigidBody.mass!==1/0?1/z.rigidBody.mass:0,Q=E.rigidBody.type==="dynamic"&&E.rigidBody.mass>0&&E.rigidBody.mass!==1/0?1/E.rigidBody.mass:0,K=Z+Q;if(K>0){let k=J.depth/K;if(Z>0){let F=O.getComponent(z.entityId,"localTransform");if(!F)return;F.x-=k*Z*J.normalX,F.y-=k*Z*J.normalY,z.x=F.x,O.markChanged(z.entityId,"localTransform")}if(Q>0){let F=O.getComponent(E.entityId,"localTransform");if(!F)return;F.x+=k*Q*J.normalX,F.y+=k*Q*J.normalY,O.markChanged(E.entityId,"localTransform")}let _=E.velocity.x-z.velocity.x,j=E.velocity.y-z.velocity.y,$=_*J.normalX+j*J.normalY;if($<0){let T=-(1+Math.min(z.rigidBody.restitution,E.rigidBody.restitution))*$/K;z.velocity.x-=T*Z*J.normalX,z.velocity.y-=T*Z*J.normalY,E.velocity.x+=T*Q*J.normalX,E.velocity.y+=T*Q*J.normalY;let W=_-$*J.normalX,L=j-$*J.normalY,R=Math.sqrt(W*W+L*L);if(R>0.000001){let V=W/R,U=L/R,G=Math.sqrt(z.rigidBody.friction*E.rigidBody.friction)*Math.abs(T),H=Math.min(R/K,G);z.velocity.x+=H*Z*V,z.velocity.y+=H*Z*U,E.velocity.x-=H*Q*V,E.velocity.y-=H*Q*U}}O.markChanged(z.entityId,"velocity"),O.markChanged(E.entityId,"velocity")}O.eventBus.publish("physicsCollision",{entityA:z.entityId,entityB:E.entityId,normal:{x:J.normalX,y:J.normalY},depth:J.depth})}function y(z,E,J,O){p(z,E,J,O)}function Ez(z){let{gravity:E={x:0,y:0},systemGroup:J="physics2D",collisionSystemGroup:O,integrationPriority:Z=1000,collisionPriority:Q=900,phase:K="fixedUpdate"}=z??{};return u({id:"physics2D",install(k){k.registerRequired("rigidBody","velocity",()=>({x:0,y:0})),k.registerRequired("rigidBody","force",()=>({x:0,y:0})),k.addResource("physicsConfig",{gravity:{x:E.x,y:E.y}}),k.addSystem("physics2D-integration").setPriority(Z).inPhase(K).inGroup(J).addQuery("bodies",{with:["localTransform","velocity","rigidBody","force"]}).setProcess(({queries:j,dt:$,ecs:F})=>{let T=F.getResource("physicsConfig"),W=T.gravity.x,L=T.gravity.y;for(let R of j.bodies){let{localTransform:V,velocity:U,rigidBody:D,force:G}=R.components;if(D.type==="static")continue;if(D.type==="dynamic"){if(U.x+=W*D.gravityScale*$,U.y+=L*D.gravityScale*$,D.mass>0&&D.mass!==1/0)U.x+=G.x/D.mass*$,U.y+=G.y/D.mass*$;if(D.drag>0){let H=Math.max(0,1-D.drag*$);U.x*=H,U.y*=H}}V.x+=U.x*$,V.y+=U.y*$,G.x=0,G.y=0,F.markChanged(R.id,"localTransform")}});let _=k.addSystem("physics2D-collision").setPriority(Q).inPhase(K).inGroup(J);if(O)_.inGroup(O);_.addQuery("collidables",{with:["localTransform","rigidBody","velocity","collisionLayer"]}).setProcess(({queries:j,ecs:$})=>{let F=[];for(let W of j.collidables){let{localTransform:L,rigidBody:R,velocity:V,collisionLayer:U}=W.components,D=M(W.id,L.x,L.y,U.layer,U.collidesWith,$.getComponent(W.id,"aabbCollider"),$.getComponent(W.id,"circleCollider"));if(!D)continue;F.push(Object.assign(D,{rigidBody:R,velocity:V}))}let T=S($.tryGetResource.bind($));X(F,T,y,$)})}})}export{zz as setVelocity,e as createRigidBody,Ez as createPhysics2DPlugin,c as createForce,a as applyImpulse,t as applyForce};
2
+
3
+ //# debugId=2F6003A2B3E0AA1D64756E2164756E21
4
+ //# sourceMappingURL=physics2D.js.map