ecspresso 0.10.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +256 -148
  2. package/dist/asset-manager.d.ts +16 -16
  3. package/dist/asset-types.d.ts +18 -16
  4. package/dist/command-buffer.d.ts +30 -20
  5. package/dist/ecspresso-builder.d.ts +193 -0
  6. package/dist/ecspresso.d.ts +323 -209
  7. package/dist/entity-manager.d.ts +76 -30
  8. package/dist/event-bus.d.ts +6 -1
  9. package/dist/index.d.ts +6 -13
  10. package/dist/plugin.d.ts +61 -0
  11. package/dist/plugins/audio.d.ts +273 -0
  12. package/dist/{bundles/utils → plugins}/bounds.d.ts +20 -26
  13. package/dist/plugins/camera.d.ts +88 -0
  14. package/dist/plugins/collision.d.ts +285 -0
  15. package/dist/plugins/coroutine.d.ts +126 -0
  16. package/dist/plugins/diagnostics.d.ts +49 -0
  17. package/dist/{bundles/utils → plugins}/input.d.ts +22 -29
  18. package/dist/plugins/particles.d.ts +225 -0
  19. package/dist/plugins/physics2D.d.ts +163 -0
  20. package/dist/plugins/renderers/renderer2D.d.ts +262 -0
  21. package/dist/plugins/spatial-index.d.ts +58 -0
  22. package/dist/plugins/sprite-animation.d.ts +150 -0
  23. package/dist/plugins/state-machine.d.ts +244 -0
  24. package/dist/plugins/timers.d.ts +151 -0
  25. package/dist/{bundles/utils → plugins}/transform.d.ts +21 -22
  26. package/dist/plugins/tween.d.ts +162 -0
  27. package/dist/reactive-query-manager.d.ts +14 -3
  28. package/dist/resource-manager.d.ts +64 -23
  29. package/dist/screen-manager.d.ts +21 -15
  30. package/dist/screen-types.d.ts +15 -11
  31. package/dist/src/index.js +4 -0
  32. package/dist/src/index.js.map +25 -0
  33. package/dist/src/plugins/audio.js +4 -0
  34. package/dist/src/plugins/audio.js.map +10 -0
  35. package/dist/src/plugins/bounds.js +4 -0
  36. package/dist/src/plugins/bounds.js.map +10 -0
  37. package/dist/src/plugins/camera.js +4 -0
  38. package/dist/src/plugins/camera.js.map +10 -0
  39. package/dist/src/plugins/collision.js +4 -0
  40. package/dist/src/plugins/collision.js.map +11 -0
  41. package/dist/src/plugins/coroutine.js +4 -0
  42. package/dist/src/plugins/coroutine.js.map +10 -0
  43. package/dist/src/plugins/diagnostics.js +5 -0
  44. package/dist/src/plugins/diagnostics.js.map +10 -0
  45. package/dist/src/plugins/input.js +4 -0
  46. package/dist/src/plugins/input.js.map +10 -0
  47. package/dist/src/plugins/particles.js +4 -0
  48. package/dist/src/plugins/particles.js.map +10 -0
  49. package/dist/src/plugins/physics2D.js +4 -0
  50. package/dist/src/plugins/physics2D.js.map +11 -0
  51. package/dist/src/plugins/renderers/renderer2D.js +4 -0
  52. package/dist/src/plugins/renderers/renderer2D.js.map +10 -0
  53. package/dist/src/plugins/spatial-index.js +4 -0
  54. package/dist/src/plugins/spatial-index.js.map +11 -0
  55. package/dist/src/plugins/sprite-animation.js +4 -0
  56. package/dist/src/plugins/sprite-animation.js.map +10 -0
  57. package/dist/src/plugins/state-machine.js +4 -0
  58. package/dist/src/plugins/state-machine.js.map +10 -0
  59. package/dist/src/plugins/timers.js +4 -0
  60. package/dist/src/plugins/timers.js.map +10 -0
  61. package/dist/src/plugins/transform.js +4 -0
  62. package/dist/src/plugins/transform.js.map +10 -0
  63. package/dist/src/plugins/tween.js +4 -0
  64. package/dist/src/plugins/tween.js.map +11 -0
  65. package/dist/system-builder.d.ts +75 -112
  66. package/dist/type-utils.d.ts +247 -7
  67. package/dist/types.d.ts +58 -39
  68. package/dist/utils/check-required-cycle.d.ts +12 -0
  69. package/dist/utils/easing.d.ts +71 -0
  70. package/dist/utils/math.d.ts +67 -0
  71. package/dist/utils/narrowphase.d.ts +63 -0
  72. package/dist/utils/spatial-hash.d.ts +53 -0
  73. package/package.json +65 -27
  74. package/dist/bundle.d.ts +0 -123
  75. package/dist/bundles/renderers/renderer2D.d.ts +0 -220
  76. package/dist/bundles/renderers/renderer2D.js +0 -4
  77. package/dist/bundles/renderers/renderer2D.js.map +0 -10
  78. package/dist/bundles/utils/bounds.js +0 -4
  79. package/dist/bundles/utils/bounds.js.map +0 -10
  80. package/dist/bundles/utils/collision.d.ts +0 -204
  81. package/dist/bundles/utils/collision.js +0 -4
  82. package/dist/bundles/utils/collision.js.map +0 -10
  83. package/dist/bundles/utils/input.js +0 -4
  84. package/dist/bundles/utils/input.js.map +0 -10
  85. package/dist/bundles/utils/movement.d.ts +0 -86
  86. package/dist/bundles/utils/movement.js +0 -4
  87. package/dist/bundles/utils/movement.js.map +0 -10
  88. package/dist/bundles/utils/timers.d.ts +0 -172
  89. package/dist/bundles/utils/timers.js +0 -4
  90. package/dist/bundles/utils/timers.js.map +0 -10
  91. package/dist/bundles/utils/transform.js +0 -4
  92. package/dist/bundles/utils/transform.js.map +0 -10
  93. package/dist/index.js +0 -4
  94. package/dist/index.js.map +0 -22
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/utils/bounds.ts"],
4
- "sourcesContent": [
5
- "/**\n * Bounds Bundle for ECSpresso\n *\n * Provides screen bounds enforcement for entities with transforms.\n * Reads worldTransform for position checking; modifies localTransform for corrections.\n * Supports destroy, clamp, and wrap behaviors.\n */\n\nimport { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\nimport type { TransformComponentTypes } from './transform';\n\n// ==================== Component Types ====================\n\n/**\n * Component that marks an entity for destruction when outside bounds.\n */\nexport interface DestroyOutOfBounds {\n\t/** Extra padding beyond bounds before destruction (default: 0) */\n\tpadding?: number;\n}\n\n/**\n * Component that clamps an entity's position to stay within bounds.\n */\nexport interface ClampToBounds {\n\t/** Margin to shrink the valid area (default: 0) */\n\tmargin?: number;\n}\n\n/**\n * Component that wraps an entity's position to the opposite edge.\n */\nexport interface WrapAtBounds {\n\t/** Padding beyond bounds before wrapping (default: 0) */\n\tpadding?: number;\n}\n\n/**\n * Component types provided by the bounds bundle.\n * Extend your component types with this interface.\n *\n * @example\n * ```typescript\n * interface GameComponents extends TransformComponentTypes, BoundsComponentTypes {\n * sprite: Sprite;\n * }\n * ```\n */\nexport interface BoundsComponentTypes {\n\tdestroyOutOfBounds: DestroyOutOfBounds;\n\tclampToBounds: ClampToBounds;\n\twrapAtBounds: WrapAtBounds;\n}\n\n// ==================== Resource Types ====================\n\n/**\n * Bounds rectangle definition.\n */\nexport interface BoundsRect {\n\t/** Left edge x coordinate (default: 0) */\n\tx?: number;\n\t/** Top edge y coordinate (default: 0) */\n\ty?: number;\n\t/** Width of the bounds area */\n\twidth: number;\n\t/** Height of the bounds area */\n\theight: number;\n}\n\n/**\n * Resource types provided by the bounds bundle.\n */\nexport interface BoundsResourceTypes {\n\tbounds: BoundsRect;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Event fired when an entity exits bounds.\n */\nexport interface EntityOutOfBoundsEvent {\n\t/** The entity that exited bounds */\n\tentityId: number;\n\t/** The edge the entity exited through */\n\texitEdge: 'top' | 'bottom' | 'left' | 'right';\n}\n\n/**\n * Event types provided by the bounds bundle.\n */\nexport interface BoundsEventTypes {\n\tentityOutOfBounds: EntityOutOfBoundsEvent;\n}\n\n// ==================== Bundle Options ====================\n\n/**\n * Configuration options for the bounds bundle.\n */\nexport interface BoundsBundleOptions {\n\t/** System group name (default: 'physics') */\n\tsystemGroup?: string;\n\t/** Priority for bounds systems (default: 50) */\n\tpriority?: number;\n\t/** Resource key for bounds rectangle (default: 'bounds') */\n\tboundsResourceKey?: string;\n\t/** Whether to auto-remove entities when out of bounds (default: true) */\n\tautoRemove?: boolean;\n\t/** Execution phase (default: 'postUpdate') */\n\tphase?: SystemPhase;\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Create a bounds rectangle resource.\n *\n * @param width The width of the bounds area\n * @param height The height of the bounds area\n * @param x The left edge x coordinate (default: 0)\n * @param y The top edge y coordinate (default: 0)\n * @returns Bounds rectangle suitable for use as a resource\n *\n * @example\n * ```typescript\n * ECSpresso.create()\n * .withResource('bounds', createBounds(800, 600))\n * .build();\n * ```\n */\nexport function createBounds(width: number, height: number, x?: number, y?: number): BoundsRect {\n\tconst bounds: BoundsRect = { width, height };\n\tif (x !== undefined) bounds.x = x;\n\tif (y !== undefined) bounds.y = y;\n\treturn bounds;\n}\n\n/**\n * Create a destroyOutOfBounds component.\n *\n * @param padding Extra padding beyond bounds before destruction\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createDestroyOutOfBounds(20),\n * });\n * ```\n */\nexport function createDestroyOutOfBounds(padding?: number): Pick<BoundsComponentTypes, 'destroyOutOfBounds'> {\n\treturn {\n\t\tdestroyOutOfBounds: padding !== undefined ? { padding } : {},\n\t};\n}\n\n/**\n * Create a clampToBounds component.\n *\n * @param margin Margin to shrink the valid area\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createClampToBounds(30),\n * });\n * ```\n */\nexport function createClampToBounds(margin?: number): Pick<BoundsComponentTypes, 'clampToBounds'> {\n\treturn {\n\t\tclampToBounds: margin !== undefined ? { margin } : {},\n\t};\n}\n\n/**\n * Create a wrapAtBounds component.\n *\n * @param padding Padding beyond bounds before wrapping\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createWrapAtBounds(10),\n * });\n * ```\n */\nexport function createWrapAtBounds(padding?: number): Pick<BoundsComponentTypes, 'wrapAtBounds'> {\n\treturn {\n\t\twrapAtBounds: padding !== undefined ? { padding } : {},\n\t};\n}\n\n// ==================== Internal Types ====================\n\ntype CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a bounds bundle for ECSpresso.\n *\n * This bundle provides:\n * - Destroy out of bounds system - removes entities that exit bounds\n * - Clamp to bounds system - constrains entities within bounds\n * - Wrap at bounds system - wraps entities to opposite edge\n *\n * Uses worldTransform for position checking (world-space) and modifies\n * localTransform for corrections. Works best with entities that don't\n * have parent transforms (orphan entities).\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso\n * .create<Components, Events, Resources>()\n * .withResource('bounds', createBounds(800, 600))\n * .withBundle(createTransformBundle())\n * .withBundle(createBoundsBundle())\n * .build();\n *\n * // Entity that gets destroyed when leaving screen\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createDestroyOutOfBounds(),\n * });\n * ```\n */\nexport function createBoundsBundle<ResourceTypes extends BoundsResourceTypes = BoundsResourceTypes>(\n\toptions?: BoundsBundleOptions\n): Bundle<CombinedComponentTypes, BoundsEventTypes, ResourceTypes> {\n\tconst {\n\t\tsystemGroup = 'physics',\n\t\tpriority = 50,\n\t\tboundsResourceKey = 'bounds',\n\t\tautoRemove = true,\n\t\tphase = 'postUpdate',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<CombinedComponentTypes, BoundsEventTypes, ResourceTypes>('bounds');\n\n\t// Destroy out of bounds system\n\tbundle\n\t\t.addSystem('bounds-destroy')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('entities', {\n\t\t\twith: ['worldTransform', 'destroyOutOfBounds'],\n\t\t})\n\t\t.setProcess((queries, _deltaTime, ecs) => {\n\t\t\tconst bounds = ecs.getResource(boundsResourceKey as keyof ResourceTypes) as BoundsRect;\n\t\t\tconst minX = bounds.x ?? 0;\n\t\t\tconst minY = bounds.y ?? 0;\n\t\t\tconst maxX = minX + bounds.width;\n\t\t\tconst maxY = minY + bounds.height;\n\n\t\t\tfor (const entity of queries.entities) {\n\t\t\t\tconst { worldTransform, destroyOutOfBounds } = entity.components;\n\t\t\t\tconst padding = destroyOutOfBounds.padding ?? 0;\n\n\t\t\t\tconst exitEdge = getExitEdge(worldTransform, minX, minY, maxX, maxY, padding);\n\t\t\t\tif (!exitEdge) continue;\n\n\t\t\t\tecs.eventBus.publish('entityOutOfBounds', {\n\t\t\t\t\tentityId: entity.id,\n\t\t\t\t\texitEdge,\n\t\t\t\t});\n\n\t\t\t\tif (autoRemove) {\n\t\t\t\t\tecs.commands.removeEntity(entity.id);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\t// Clamp to bounds system\n\tbundle\n\t\t.addSystem('bounds-clamp')\n\t\t.setPriority(priority - 1)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('entities', {\n\t\t\twith: ['localTransform', 'worldTransform', 'clampToBounds'],\n\t\t})\n\t\t.setProcess((queries, _deltaTime, ecs) => {\n\t\t\tconst bounds = ecs.getResource(boundsResourceKey as keyof ResourceTypes) as BoundsRect;\n\t\t\tconst minX = bounds.x ?? 0;\n\t\t\tconst minY = bounds.y ?? 0;\n\t\t\tconst maxX = minX + bounds.width;\n\t\t\tconst maxY = minY + bounds.height;\n\n\t\t\tfor (const entity of queries.entities) {\n\t\t\t\tconst { localTransform, worldTransform, clampToBounds } = entity.components;\n\t\t\t\tconst margin = clampToBounds.margin ?? 0;\n\n\t\t\t\tconst clampedMinX = minX + margin;\n\t\t\t\tconst clampedMinY = minY + margin;\n\t\t\t\tconst clampedMaxX = maxX - margin;\n\t\t\t\tconst clampedMaxY = maxY - margin;\n\n\t\t\t\t// Calculate world-space correction and apply to local transform\n\t\t\t\t// For entities without parents, this is equivalent to direct position clamping\n\t\t\t\tlet deltaX = 0;\n\t\t\t\tlet deltaY = 0;\n\n\t\t\t\tif (worldTransform.x < clampedMinX) deltaX = clampedMinX - worldTransform.x;\n\t\t\t\tif (worldTransform.x > clampedMaxX) deltaX = clampedMaxX - worldTransform.x;\n\t\t\t\tif (worldTransform.y < clampedMinY) deltaY = clampedMinY - worldTransform.y;\n\t\t\t\tif (worldTransform.y > clampedMaxY) deltaY = clampedMaxY - worldTransform.y;\n\n\t\t\t\tif (deltaX !== 0 || deltaY !== 0) {\n\t\t\t\t\tlocalTransform.x += deltaX;\n\t\t\t\t\tlocalTransform.y += deltaY;\n\t\t\t\t\tecs.markChanged(entity.id, 'localTransform');\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\t// Wrap at bounds system\n\tbundle\n\t\t.addSystem('bounds-wrap')\n\t\t.setPriority(priority - 2)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('entities', {\n\t\t\twith: ['localTransform', 'worldTransform', 'wrapAtBounds'],\n\t\t})\n\t\t.setProcess((queries, _deltaTime, ecs) => {\n\t\t\tconst bounds = ecs.getResource(boundsResourceKey as keyof ResourceTypes) as BoundsRect;\n\t\t\tconst minX = bounds.x ?? 0;\n\t\t\tconst minY = bounds.y ?? 0;\n\t\t\tconst maxX = minX + bounds.width;\n\t\t\tconst maxY = minY + bounds.height;\n\n\t\t\tfor (const entity of queries.entities) {\n\t\t\t\tconst { localTransform, worldTransform, wrapAtBounds } = entity.components;\n\t\t\t\tconst padding = wrapAtBounds.padding ?? 0;\n\n\t\t\t\tlet deltaX = 0;\n\t\t\t\tlet deltaY = 0;\n\t\t\t\tconst boundsWidth = maxX - minX;\n\t\t\t\tconst boundsHeight = maxY - minY;\n\n\t\t\t\t// Wrap horizontally\n\t\t\t\tif (worldTransform.x > maxX + padding) {\n\t\t\t\t\tdeltaX = -(boundsWidth + 2 * padding);\n\t\t\t\t} else if (worldTransform.x < minX - padding) {\n\t\t\t\t\tdeltaX = boundsWidth + 2 * padding;\n\t\t\t\t}\n\n\t\t\t\t// Wrap vertically\n\t\t\t\tif (worldTransform.y > maxY + padding) {\n\t\t\t\t\tdeltaY = -(boundsHeight + 2 * padding);\n\t\t\t\t} else if (worldTransform.y < minY - padding) {\n\t\t\t\t\tdeltaY = boundsHeight + 2 * padding;\n\t\t\t\t}\n\n\t\t\t\tif (deltaX !== 0 || deltaY !== 0) {\n\t\t\t\t\tlocalTransform.x += deltaX;\n\t\t\t\t\tlocalTransform.y += deltaY;\n\t\t\t\t\tecs.markChanged(entity.id, 'localTransform');\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\treturn bundle;\n}\n\n/**\n * Determine which edge an entity has exited through, if any.\n */\nfunction getExitEdge(\n\ttransform: { x: number; y: number },\n\tminX: number,\n\tminY: number,\n\tmaxX: number,\n\tmaxY: number,\n\tpadding: number\n): 'top' | 'bottom' | 'left' | 'right' | null {\n\tif (transform.x > maxX + padding) return 'right';\n\tif (transform.x < minX - padding) return 'left';\n\tif (transform.y > maxY + padding) return 'bottom';\n\tif (transform.y < minY - padding) return 'top';\n\treturn null;\n}\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "8FF1EB3AF1C6181164756E2164756E21",
9
- "names": []
10
- }
@@ -1,204 +0,0 @@
1
- /**
2
- * Collision Bundle for ECSpresso
3
- *
4
- * Provides layer-based collision detection with events.
5
- * Uses worldTransform for position (world-space collision).
6
- * Supports AABB and circle colliders.
7
- */
8
- import { Bundle } from 'ecspresso';
9
- import type { SystemPhase } from 'ecspresso';
10
- import type { TransformComponentTypes } from './transform';
11
- /**
12
- * Axis-Aligned Bounding Box collider.
13
- */
14
- export interface AABBCollider {
15
- /** Width of the bounding box */
16
- width: number;
17
- /** Height of the bounding box */
18
- height: number;
19
- /** X offset from entity position (default: 0) */
20
- offsetX?: number;
21
- /** Y offset from entity position (default: 0) */
22
- offsetY?: number;
23
- }
24
- /**
25
- * Circle collider.
26
- */
27
- export interface CircleCollider {
28
- /** Radius of the circle */
29
- radius: number;
30
- /** X offset from entity position (default: 0) */
31
- offsetX?: number;
32
- /** Y offset from entity position (default: 0) */
33
- offsetY?: number;
34
- }
35
- /**
36
- * Collision layer configuration.
37
- */
38
- export interface CollisionLayer {
39
- /** The layer this entity belongs to */
40
- layer: string;
41
- /** Layers this entity can collide with */
42
- collidesWith: readonly string[];
43
- }
44
- /**
45
- * Component types provided by the collision bundle.
46
- * Extend your component types with this interface.
47
- *
48
- * @example
49
- * ```typescript
50
- * interface GameComponents extends TransformComponentTypes, CollisionComponentTypes {
51
- * sprite: Sprite;
52
- * enemy: boolean;
53
- * }
54
- * ```
55
- */
56
- export interface CollisionComponentTypes {
57
- aabbCollider: AABBCollider;
58
- circleCollider: CircleCollider;
59
- collisionLayer: CollisionLayer;
60
- }
61
- /**
62
- * Event fired when two entities collide.
63
- */
64
- export interface CollisionEvent {
65
- /** First entity in the collision */
66
- entityA: number;
67
- /** Second entity in the collision */
68
- entityB: number;
69
- /** Layer of the first entity */
70
- layerA: string;
71
- /** Layer of the second entity */
72
- layerB: string;
73
- }
74
- /**
75
- * Event types provided by the collision bundle.
76
- */
77
- export interface CollisionEventTypes {
78
- collision: CollisionEvent;
79
- }
80
- /**
81
- * Configuration options for the collision bundle.
82
- */
83
- export interface CollisionBundleOptions {
84
- /** System group name (default: 'physics') */
85
- systemGroup?: string;
86
- /** Priority for collision system (default: 0) */
87
- priority?: number;
88
- /** Name of the collision event (default: 'collision') */
89
- collisionEventName?: string;
90
- /** Execution phase (default: 'postUpdate') */
91
- phase?: SystemPhase;
92
- }
93
- /**
94
- * Create an AABB collider component.
95
- *
96
- * @param width Width of the bounding box
97
- * @param height Height of the bounding box
98
- * @param offsetX X offset from entity position
99
- * @param offsetY Y offset from entity position
100
- * @returns Component object suitable for spreading into spawn()
101
- *
102
- * @example
103
- * ```typescript
104
- * ecs.spawn({
105
- * ...createTransform(100, 200),
106
- * ...createAABBCollider(50, 30),
107
- * });
108
- * ```
109
- */
110
- export declare function createAABBCollider(width: number, height: number, offsetX?: number, offsetY?: number): Pick<CollisionComponentTypes, 'aabbCollider'>;
111
- /**
112
- * Create a circle collider component.
113
- *
114
- * @param radius Radius of the circle
115
- * @param offsetX X offset from entity position
116
- * @param offsetY Y offset from entity position
117
- * @returns Component object suitable for spreading into spawn()
118
- *
119
- * @example
120
- * ```typescript
121
- * ecs.spawn({
122
- * ...createTransform(100, 200),
123
- * ...createCircleCollider(25),
124
- * });
125
- * ```
126
- */
127
- export declare function createCircleCollider(radius: number, offsetX?: number, offsetY?: number): Pick<CollisionComponentTypes, 'circleCollider'>;
128
- /**
129
- * Create a collision layer component.
130
- *
131
- * @param layer The layer this entity belongs to
132
- * @param collidesWith Layers this entity can collide with
133
- * @returns Component object suitable for spreading into spawn()
134
- *
135
- * @example
136
- * ```typescript
137
- * ecs.spawn({
138
- * ...createTransform(100, 200),
139
- * ...createAABBCollider(50, 30),
140
- * ...createCollisionLayer('player', ['enemy', 'obstacle']),
141
- * });
142
- * ```
143
- */
144
- export declare function createCollisionLayer(layer: string, collidesWith: readonly string[]): Pick<CollisionComponentTypes, 'collisionLayer'>;
145
- /**
146
- * Layer factory result from defineCollisionLayers.
147
- */
148
- export type LayerFactories<T extends Record<string, readonly string[]>> = {
149
- [K in keyof T]: () => Pick<CollisionComponentTypes, 'collisionLayer'>;
150
- };
151
- /**
152
- * Define collision layer relationships and get factory functions.
153
- *
154
- * @param rules Object mapping layer names to arrays of layers they collide with
155
- * @returns Object with factory functions for each layer
156
- *
157
- * @example
158
- * ```typescript
159
- * const layers = defineCollisionLayers({
160
- * player: ['enemy', 'enemyProjectile'],
161
- * playerProjectile: ['enemy'],
162
- * enemy: ['playerProjectile'],
163
- * enemyProjectile: ['player'],
164
- * });
165
- *
166
- * // Usage
167
- * ecs.spawn({
168
- * ...createTransform(100, 200),
169
- * ...createAABBCollider(50, 30),
170
- * ...layers.player(),
171
- * });
172
- * ```
173
- */
174
- export declare function defineCollisionLayers<T extends Record<string, readonly string[]>>(rules: T): LayerFactories<T>;
175
- type CombinedComponentTypes = CollisionComponentTypes & TransformComponentTypes;
176
- /**
177
- * Create a collision bundle for ECSpresso.
178
- *
179
- * This bundle provides:
180
- * - O(n²) collision detection between entities with colliders
181
- * - AABB-AABB, circle-circle, and AABB-circle collision
182
- * - Layer-based filtering for collision pairs
183
- * - Deduplication of A-B / B-A collisions
184
- *
185
- * Uses worldTransform for position (world-space collision detection).
186
- *
187
- * @example
188
- * ```typescript
189
- * const ecs = ECSpresso
190
- * .create<Components, Events, Resources>()
191
- * .withBundle(createTransformBundle())
192
- * .withBundle(createCollisionBundle())
193
- * .build();
194
- *
195
- * // Entity with collision
196
- * ecs.spawn({
197
- * ...createTransform(100, 200),
198
- * ...createAABBCollider(50, 30),
199
- * ...createCollisionLayer('player', ['enemy']),
200
- * });
201
- * ```
202
- */
203
- export declare function createCollisionBundle(options?: CollisionBundleOptions): Bundle<CombinedComponentTypes, CollisionEventTypes, {}>;
204
- export {};
@@ -1,4 +0,0 @@
1
- var L=((v)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(v,{get:(z,D)=>(typeof require<"u"?require:z)[D]}):v)(function(v){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+v+'" is not supported')});import{Bundle as B}from"ecspresso";function R(v,z,D,E){let I={width:v,height:z};if(D!==void 0)I.offsetX=D;if(E!==void 0)I.offsetY=E;return{aabbCollider:I}}function V(v,z,D){let E={radius:v};if(z!==void 0)E.offsetX=z;if(D!==void 0)E.offsetY=D;return{circleCollider:E}}function K(v,z){return{collisionLayer:{layer:v,collidesWith:z}}}function m(v){let z={};for(let D of Object.keys(v)){let E=v[D];z[D]=()=>K(D,E)}return z}function C(v){let{systemGroup:z="physics",priority:D=0,phase:E="postUpdate"}=v??{},I=new B("collision");return I.addSystem("collision-detection").setPriority(D).inPhase(E).inGroup(z).addQuery("collidables",{with:["worldTransform","collisionLayer"]}).setProcess((Z,_,O)=>{let N=[];for(let M of Z.collidables){let{worldTransform:J,collisionLayer:k}=M.components,F=O.entityManager.getComponent(M.id,"aabbCollider"),$=O.entityManager.getComponent(M.id,"circleCollider");if(!F&&!$)continue;let U={entityId:M.id,x:J.x,y:J.y,layer:k.layer,collidesWith:k.collidesWith};if(F)U.x+=F.offsetX??0,U.y+=F.offsetY??0,U.aabb={halfWidth:F.width/2,halfHeight:F.height/2};if($)U.x+=$.offsetX??0,U.y+=$.offsetY??0,U.circle={radius:$.radius};N.push(U)}let Q=new Set;for(let M=0;M<N.length;M++){let J=N[M];if(!J)continue;for(let k=M+1;k<N.length;k++){let F=N[k];if(!F)continue;let $=J.collidesWith.includes(F.layer),U=F.collidesWith.includes(J.layer);if(!$&&!U)continue;let G=J.entityId<F.entityId?`${J.entityId}:${F.entityId}`:`${F.entityId}:${J.entityId}`;if(Q.has(G))continue;if(P(J,F))Q.add(G),O.eventBus.publish("collision",{entityA:J.entityId,entityB:F.entityId,layerA:J.layer,layerB:F.layer})}}}).and(),I}function P(v,z){if(v.aabb&&z.aabb)return T(v.x,v.y,v.aabb.halfWidth,v.aabb.halfHeight,z.x,z.y,z.aabb.halfWidth,z.aabb.halfHeight);if(v.circle&&z.circle)return j(v.x,v.y,v.circle.radius,z.x,z.y,z.circle.radius);if(v.aabb&&z.circle)return A(v.x,v.y,v.aabb.halfWidth,v.aabb.halfHeight,z.x,z.y,z.circle.radius);if(v.circle&&z.aabb)return A(z.x,z.y,z.aabb.halfWidth,z.aabb.halfHeight,v.x,v.y,v.circle.radius);return!1}function T(v,z,D,E,I,Z,_,O){let N=Math.abs(v-I),Q=Math.abs(z-Z);return N<D+_&&Q<E+O}function j(v,z,D,E,I,Z){let _=v-E,O=z-I,N=_*_+O*O,Q=D+Z;return N<Q*Q}function A(v,z,D,E,I,Z,_){let O=Math.max(v-D,Math.min(I,v+D)),N=Math.max(z-E,Math.min(Z,z+E)),Q=I-O,M=Z-N;return Q*Q+M*M<_*_}export{m as defineCollisionLayers,K as createCollisionLayer,C as createCollisionBundle,V as createCircleCollider,R as createAABBCollider};
2
-
3
- //# debugId=87C006826612A65764756E2164756E21
4
- //# sourceMappingURL=collision.js.map
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/utils/collision.ts"],
4
- "sourcesContent": [
5
- "/**\n * Collision Bundle 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 { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\nimport type { TransformComponentTypes } from './transform';\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 {\n\t/** The layer this entity belongs to */\n\tlayer: string;\n\t/** Layers this entity can collide with */\n\tcollidesWith: readonly string[];\n}\n\n/**\n * Component types provided by the collision bundle.\n * Extend your component types with this interface.\n *\n * @example\n * ```typescript\n * interface GameComponents extends TransformComponentTypes, CollisionComponentTypes {\n * sprite: Sprite;\n * enemy: boolean;\n * }\n * ```\n */\nexport interface CollisionComponentTypes {\n\taabbCollider: AABBCollider;\n\tcircleCollider: CircleCollider;\n\tcollisionLayer: CollisionLayer;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Event fired when two entities collide.\n */\nexport interface CollisionEvent {\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: string;\n\t/** Layer of the second entity */\n\tlayerB: string;\n}\n\n/**\n * Event types provided by the collision bundle.\n */\nexport interface CollisionEventTypes {\n\tcollision: CollisionEvent;\n}\n\n// ==================== Bundle Options ====================\n\n/**\n * Configuration options for the collision bundle.\n */\nexport interface CollisionBundleOptions {\n\t/** System group name (default: 'physics') */\n\tsystemGroup?: string;\n\t/** Priority for collision system (default: 0) */\n\tpriority?: number;\n\t/** Name of the collision event (default: 'collision') */\n\tcollisionEventName?: string;\n\t/** Execution phase (default: 'postUpdate') */\n\tphase?: SystemPhase;\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): Pick<CollisionComponentTypes, '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): Pick<CollisionComponentTypes, '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(\n\tlayer: string,\n\tcollidesWith: readonly string[]\n): Pick<CollisionComponentTypes, '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, 'collisionLayer'>;\n};\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 */\nexport function defineCollisionLayers<T extends Record<string, readonly string[]>>(\n\trules: T\n): LayerFactories<T> {\n\tconst factories = {} as LayerFactories<T>;\n\n\tfor (const layer of Object.keys(rules) as Array<keyof T & string>) {\n\t\tconst collidesWith = rules[layer] as readonly string[];\n\t\tfactories[layer] = () => createCollisionLayer(layer, collidesWith);\n\t}\n\n\treturn factories;\n}\n\n// ==================== Internal Types ====================\n\ntype CombinedComponentTypes = CollisionComponentTypes & TransformComponentTypes;\n\ninterface ColliderInfo {\n\tentityId: number;\n\tx: number;\n\ty: number;\n\tlayer: string;\n\tcollidesWith: readonly string[];\n\taabb?: { halfWidth: number; halfHeight: number };\n\tcircle?: { radius: number };\n}\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a collision bundle for ECSpresso.\n *\n * This bundle provides:\n * - O(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 *\n * Uses worldTransform for position (world-space collision detection).\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso\n * .create<Components, Events, Resources>()\n * .withBundle(createTransformBundle())\n * .withBundle(createCollisionBundle())\n * .build();\n *\n * // Entity with collision\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createAABBCollider(50, 30),\n * ...createCollisionLayer('player', ['enemy']),\n * });\n * ```\n */\nexport function createCollisionBundle(\n\toptions?: CollisionBundleOptions\n): Bundle<CombinedComponentTypes, CollisionEventTypes, {}> {\n\tconst {\n\t\tsystemGroup = 'physics',\n\t\tpriority = 0,\n\t\tphase = 'postUpdate',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<CombinedComponentTypes, CollisionEventTypes, {}>('collision');\n\n\tbundle\n\t\t.addSystem('collision-detection')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('collidables', {\n\t\t\twith: ['worldTransform', 'collisionLayer'],\n\t\t})\n\t\t.setProcess((queries, _deltaTime, ecs) => {\n\t\t\t// Build list of collidable entities with their computed positions\n\t\t\tconst colliders: ColliderInfo[] = [];\n\n\t\t\tfor (const entity of queries.collidables) {\n\t\t\t\tconst { worldTransform, collisionLayer } = entity.components;\n\n\t\t\t\t// Get collider info\n\t\t\t\tconst aabb = ecs.entityManager.getComponent(entity.id, 'aabbCollider') as AABBCollider | null;\n\t\t\t\tconst circle = ecs.entityManager.getComponent(entity.id, 'circleCollider') as CircleCollider | null;\n\n\t\t\t\t// Must have at least one collider\n\t\t\t\tif (!aabb && !circle) continue;\n\n\t\t\t\tconst info: ColliderInfo = {\n\t\t\t\t\tentityId: entity.id,\n\t\t\t\t\tx: worldTransform.x,\n\t\t\t\t\ty: worldTransform.y,\n\t\t\t\t\tlayer: collisionLayer.layer,\n\t\t\t\t\tcollidesWith: collisionLayer.collidesWith,\n\t\t\t\t};\n\n\t\t\t\tif (aabb) {\n\t\t\t\t\tinfo.x += aabb.offsetX ?? 0;\n\t\t\t\t\tinfo.y += aabb.offsetY ?? 0;\n\t\t\t\t\tinfo.aabb = {\n\t\t\t\t\t\thalfWidth: aabb.width / 2,\n\t\t\t\t\t\thalfHeight: aabb.height / 2,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (circle) {\n\t\t\t\t\tinfo.x += circle.offsetX ?? 0;\n\t\t\t\t\tinfo.y += circle.offsetY ?? 0;\n\t\t\t\t\tinfo.circle = { radius: circle.radius };\n\t\t\t\t}\n\n\t\t\t\tcolliders.push(info);\n\t\t\t}\n\n\t\t\t// Track processed pairs to avoid duplicates\n\t\t\tconst processedPairs = new Set<string>();\n\n\t\t\t// O(n²) collision detection\n\t\t\tfor (let i = 0; i < colliders.length; i++) {\n\t\t\t\tconst a = colliders[i];\n\t\t\t\tif (!a) continue;\n\n\t\t\t\tfor (let j = i + 1; j < colliders.length; j++) {\n\t\t\t\t\tconst b = colliders[j];\n\t\t\t\t\tif (!b) continue;\n\n\t\t\t\t\t// Check layer compatibility (A→B or B→A)\n\t\t\t\t\tconst aCollidesWithB = a.collidesWith.includes(b.layer);\n\t\t\t\t\tconst bCollidesWithA = b.collidesWith.includes(a.layer);\n\n\t\t\t\t\tif (!aCollidesWithB && !bCollidesWithA) continue;\n\n\t\t\t\t\t// Create unique pair key\n\t\t\t\t\tconst pairKey = a.entityId < b.entityId\n\t\t\t\t\t\t? `${a.entityId}:${b.entityId}`\n\t\t\t\t\t\t: `${b.entityId}:${a.entityId}`;\n\n\t\t\t\t\tif (processedPairs.has(pairKey)) continue;\n\n\t\t\t\t\t// Check collision based on collider types\n\t\t\t\t\tconst colliding = checkCollision(a, b);\n\n\t\t\t\t\tif (colliding) {\n\t\t\t\t\t\tprocessedPairs.add(pairKey);\n\t\t\t\t\t\tecs.eventBus.publish('collision', {\n\t\t\t\t\t\t\tentityA: a.entityId,\n\t\t\t\t\t\t\tentityB: b.entityId,\n\t\t\t\t\t\t\tlayerA: a.layer,\n\t\t\t\t\t\t\tlayerB: b.layer,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\treturn bundle;\n}\n\n/**\n * Check if two colliders are overlapping.\n */\nfunction checkCollision(a: ColliderInfo, b: ColliderInfo): boolean {\n\t// AABB vs AABB\n\tif (a.aabb && b.aabb) {\n\t\treturn aabbVsAabb(\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\t// Circle vs Circle\n\tif (a.circle && b.circle) {\n\t\treturn circleVsCircle(\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\t// AABB vs Circle\n\tif (a.aabb && b.circle) {\n\t\treturn aabbVsCircle(\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\treturn aabbVsCircle(\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}\n\n\treturn false;\n}\n\n/**\n * AABB vs AABB collision test.\n */\nfunction aabbVsAabb(\n\tax: number, ay: number, aHalfWidth: number, aHalfHeight: number,\n\tbx: number, by: number, bHalfWidth: number, bHalfHeight: number\n): boolean {\n\tconst dx = Math.abs(ax - bx);\n\tconst dy = Math.abs(ay - by);\n\treturn dx < (aHalfWidth + bHalfWidth) && dy < (aHalfHeight + bHalfHeight);\n}\n\n/**\n * Circle vs Circle collision test.\n */\nfunction circleVsCircle(\n\tax: number, ay: number, aRadius: number,\n\tbx: number, by: number, bRadius: number\n): boolean {\n\tconst dx = ax - bx;\n\tconst dy = ay - by;\n\tconst distSq = dx * dx + dy * dy;\n\tconst radiusSum = aRadius + bRadius;\n\treturn distSq < radiusSum * radiusSum;\n}\n\n/**\n * AABB vs Circle collision test.\n */\nfunction aabbVsCircle(\n\taabbX: number, aabbY: number, halfWidth: number, halfHeight: number,\n\tcircleX: number, circleY: number, radius: number\n): boolean {\n\t// Find the closest point on the AABB to the circle center\n\tconst closestX = Math.max(aabbX - halfWidth, Math.min(circleX, aabbX + halfWidth));\n\tconst closestY = Math.max(aabbY - halfHeight, Math.min(circleY, aabbY + halfHeight));\n\n\t// Calculate distance from closest point to circle center\n\tconst dx = circleX - closestX;\n\tconst dy = circleY - closestY;\n\tconst distSq = dx * dx + dy * dy;\n\n\treturn distSq < radius * radius;\n}\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "87C006826612A65764756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- var _=((q)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(q,{get:(O,Q)=>(typeof require<"u"?require:O)[Q]}):q)(function(q){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+q+'" is not supported')});import{Bundle as Y}from"ecspresso";function M(q){return q}function A(){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,m=new Set;function D(){return{keysDown:K,keysPressed:K,keysReleased:K,buttonsDown:m,buttonsPressed:m,buttonsReleased:m,pointerX:0,pointerY:0,pointerDeltaX:0,pointerDeltaY:0,actionsActive:K,prevActionsActive:K}}function R(q,O,Q){let V=new Set;for(let[Z,W]of Object.entries(q)){let H=W.keys?.some((J)=>O.has(J))??!1,z=W.buttons?.some((J)=>Q.has(J))??!1;if(H||z)V.add(Z)}return V}function v(q,O,Q){let V=new Set(q.keysDown),Z=new Set(q.keysPressed),W=new Set(q.keysReleased),H=new Set(q.buttonsDown),z=new Set(q.buttonsPressed),J=new Set(q.buttonsReleased),$=q.pointerMoved?q.pointerX-q.lastPointerX:0,j=q.pointerMoved?q.pointerY-q.lastPointerY:0,x=R(Q,V,H),B={keysDown:V,keysPressed:Z,keysReleased:W,buttonsDown:H,buttonsPressed:z,buttonsReleased:J,pointerX:q.pointerX,pointerY:q.pointerY,pointerDeltaX:$,pointerDeltaY:j,actionsActive:x,prevActionsActive:O};return q.keysPressed=[],q.keysReleased=[],q.buttonsPressed=[],q.buttonsReleased=[],q.lastPointerX=q.pointerX,q.lastPointerY=q.pointerY,q.pointerMoved=!1,B}function T(q){let{systemGroup:O="input",priority:Q=100,phase:V="preUpdate",actions:Z={},target:W=globalThis}=q??{},H=A(),z=D(),J={...Z},$=[],j={x:0,y:0},x={x:0,y:0},L={keyboard:{isDown:(f)=>z.keysDown.has(f),justPressed:(f)=>z.keysPressed.has(f),justReleased:(f)=>z.keysReleased.has(f)},pointer:{position:j,delta:x,isDown:(f)=>z.buttonsDown.has(f),justPressed:(f)=>z.buttonsPressed.has(f),justReleased:(f)=>z.buttonsReleased.has(f)},actions:{isActive:(f)=>z.actionsActive.has(f),justActivated:(f)=>z.actionsActive.has(f)&&!z.prevActionsActive.has(f),justDeactivated:(f)=>!z.actionsActive.has(f)&&z.prevActionsActive.has(f)},setActionMap(f){J={...f}},getActionMap(){return{...J}}};function g(f){let C=f;if(C.repeat)return;H.keysDown.add(C.key),H.keysPressed.push(C.key)}function G(f){let C=f;H.keysDown.delete(C.key),H.keysReleased.push(C.key)}function I(f){let C=f;H.buttonsDown.add(C.button),H.buttonsPressed.push(C.button)}function U(f){let C=f;H.pointerX=C.clientX,H.pointerY=C.clientY,H.pointerMoved=!0}function N(f){let C=f;H.buttonsDown.delete(C.button),H.buttonsReleased.push(C.button)}function X(f,C){W.addEventListener(f,C),$.push(()=>W.removeEventListener(f,C))}let F=new Y("input");return F.addResource("inputState",L),F.addSystem("input-state").setPriority(Q).inPhase(V).inGroup(O).setOnInitialize(()=>{X("keydown",g),X("keyup",G),X("pointerdown",I),X("pointermove",U),X("pointerup",N)}).setOnDetach(()=>{for(let f of $)f();$.length=0}).setProcess(()=>{let f=z.actionsActive;z=v(H,f,J),j.x=z.pointerX,j.y=z.pointerY,x.x=z.pointerDeltaX,x.y=z.pointerDeltaY}).and(),F}export{T as createInputBundle,M as createActionBinding};
2
-
3
- //# debugId=1A29264AD47B9F8264756E2164756E21
4
- //# sourceMappingURL=input.js.map
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/utils/input.ts"],
4
- "sourcesContent": [
5
- "/**\n * Input Bundle for ECSpresso\n *\n * Provides frame-accurate keyboard, pointer (mouse + touch via PointerEvent),\n * and action mapping input. Resource-only bundle — 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 { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\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 {\n\tisActive(action: string): boolean;\n\tjustActivated(action: string): boolean;\n\tjustDeactivated(action: string): boolean;\n}\n\nexport interface InputState {\n\treadonly keyboard: KeyboardState;\n\treadonly pointer: PointerState;\n\treadonly actions: ActionState;\n\tsetActionMap(actions: ActionMap): void;\n\tgetActionMap(): Readonly<ActionMap>;\n}\n\nexport interface ActionBinding {\n\tkeys?: KeyCode[];\n\tbuttons?: number[];\n}\n\nexport type ActionMap = Record<string, ActionBinding>;\n\nexport interface InputResourceTypes {\n\tinputState: InputState;\n}\n\nexport interface InputBundleOptions {\n\t/** System group name (default: 'input') */\n\tsystemGroup?: string;\n\t/** Priority for input system (default: 100) */\n\tpriority?: number;\n\t/** Execution phase (default: 'preUpdate') */\n\tphase?: SystemPhase;\n\t/** Initial action mappings */\n\tactions?: ActionMap;\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// ==================== Bundle 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 bundle for ECSpresso.\n *\n * This bundle 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\n * .create<Components, Events, Resources>()\n * .withBundle(createInputBundle({\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 createInputBundle(\n\toptions?: InputBundleOptions\n): Bundle<{}, {}, InputResourceTypes> {\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 = {\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 = {\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 };\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 unknown 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 unknown 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 unknown 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\t// Build bundle\n\tconst bundle = new Bundle<{}, {}, InputResourceTypes>('input');\n\n\tbundle.addResource('inputState', inputState);\n\n\tbundle\n\t\t.addSystem('input-state')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.setOnInitialize(() => {\n\t\t\taddListener('keydown', onKeyDown);\n\t\t\taddListener('keyup', onKeyUp);\n\t\t\taddListener('pointerdown', onPointerDown);\n\t\t\taddListener('pointermove', onPointerMove);\n\t\t\taddListener('pointerup', onPointerUp);\n\t\t})\n\t\t.setOnDetach(() => {\n\t\t\tfor (const cleanup of cleanupFns) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t\tcleanupFns.length = 0;\n\t\t})\n\t\t.setProcess(() => {\n\t\t\tconst prevActionsActive = snapshot.actionsActive;\n\t\t\tsnapshot = snapshotRaw(raw, prevActionsActive, actionMap);\n\n\t\t\t// Update the exposed position/delta objects in-place\n\t\t\tposition.x = snapshot.pointerX;\n\t\t\tposition.y = snapshot.pointerY;\n\t\t\tdelta.x = snapshot.pointerDeltaX;\n\t\t\tdelta.y = snapshot.pointerDeltaY;\n\t\t})\n\t\t.and();\n\n\treturn bundle;\n}\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "1A29264AD47B9F8264756E2164756E21",
9
- "names": []
10
- }
@@ -1,86 +0,0 @@
1
- /**
2
- * Movement Bundle for ECSpresso
3
- *
4
- * Provides velocity → localTransform integration for entities.
5
- * Works with the transform bundle's localTransform/worldTransform system.
6
- */
7
- import { Bundle } from 'ecspresso';
8
- import type { SystemPhase } from 'ecspresso';
9
- import type { TransformComponentTypes } from './transform';
10
- /**
11
- * Velocity component data structure.
12
- */
13
- export interface Velocity {
14
- x: number;
15
- y: number;
16
- }
17
- /**
18
- * Component types provided by the movement bundle.
19
- * Extend your component types with this interface.
20
- *
21
- * @example
22
- * ```typescript
23
- * interface GameComponents extends TransformComponentTypes, MovementComponentTypes {
24
- * sprite: Sprite;
25
- * player: boolean;
26
- * }
27
- * ```
28
- */
29
- export interface MovementComponentTypes extends TransformComponentTypes {
30
- velocity: Velocity;
31
- }
32
- /**
33
- * Configuration options for the movement bundle.
34
- */
35
- export interface MovementBundleOptions {
36
- /** System group name (default: 'physics') */
37
- systemGroup?: string;
38
- /** Priority for movement update system (default: 1000, runs early before transform propagation) */
39
- priority?: number;
40
- /** Execution phase (default: 'fixedUpdate') */
41
- phase?: SystemPhase;
42
- }
43
- /**
44
- * Create a velocity component.
45
- *
46
- * @param x The x velocity
47
- * @param y The y velocity
48
- * @returns Component object suitable for spreading into spawn()
49
- *
50
- * @example
51
- * ```typescript
52
- * ecs.spawn({
53
- * ...createTransform(100, 200),
54
- * ...createVelocity(50, -25),
55
- * projectile: true,
56
- * });
57
- * ```
58
- */
59
- export declare function createVelocity(x: number, y: number): Pick<MovementComponentTypes, 'velocity'>;
60
- /**
61
- * Create a movement bundle for ECSpresso.
62
- *
63
- * This bundle provides:
64
- * - Movement update system that integrates velocity into localTransform
65
- * - Processes all entities with both localTransform and velocity components
66
- *
67
- * Note: This bundle modifies localTransform. The transform bundle's propagation
68
- * system will then compute worldTransform for use by other systems.
69
- *
70
- * @example
71
- * ```typescript
72
- * const ecs = ECSpresso
73
- * .create<Components, Events, Resources>()
74
- * .withBundle(createTransformBundle())
75
- * .withBundle(createMovementBundle())
76
- * .build();
77
- *
78
- * // Spawn entity with movement
79
- * ecs.spawn({
80
- * ...createTransform(100, 200),
81
- * ...createVelocity(50, -25),
82
- * sprite,
83
- * });
84
- * ```
85
- */
86
- export declare function createMovementBundle(options?: MovementBundleOptions): Bundle<MovementComponentTypes, {}, {}>;
@@ -1,4 +0,0 @@
1
- var Q=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(k,z)=>(typeof require<"u"?require:k)[z]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});import{Bundle as O}from"ecspresso";function X(j,k){return{velocity:{x:j,y:k}}}function Y(j){let{systemGroup:k="physics",priority:z=1000,phase:J="fixedUpdate"}=j??{},A=new O("movement");return A.addSystem("movement").setPriority(z).inPhase(J).inGroup(k).addQuery("movingEntities",{with:["localTransform","velocity"]}).setProcess((K,D,L)=>{for(let F of K.movingEntities){let{localTransform:H,velocity:I}=F.components;H.x+=I.x*D,H.y+=I.y*D,L.markChanged(F.id,"localTransform")}}).and(),A}export{X as createVelocity,Y as createMovementBundle};
2
-
3
- //# debugId=3E601B940224B76564756E2164756E21
4
- //# sourceMappingURL=movement.js.map
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/utils/movement.ts"],
4
- "sourcesContent": [
5
- "/**\n * Movement Bundle for ECSpresso\n *\n * Provides velocity → localTransform integration for entities.\n * Works with the transform bundle's localTransform/worldTransform system.\n */\n\nimport { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\nimport type { TransformComponentTypes } from './transform';\n\n// ==================== Component Types ====================\n\n/**\n * Velocity component data structure.\n */\nexport interface Velocity {\n\tx: number;\n\ty: number;\n}\n\n/**\n * Component types provided by the movement bundle.\n * Extend your component types with this interface.\n *\n * @example\n * ```typescript\n * interface GameComponents extends TransformComponentTypes, MovementComponentTypes {\n * sprite: Sprite;\n * player: boolean;\n * }\n * ```\n */\nexport interface MovementComponentTypes extends TransformComponentTypes {\n\tvelocity: Velocity;\n}\n\n// ==================== Bundle Options ====================\n\n/**\n * Configuration options for the movement bundle.\n */\nexport interface MovementBundleOptions {\n\t/** System group name (default: 'physics') */\n\tsystemGroup?: string;\n\t/** Priority for movement update system (default: 1000, runs early before transform propagation) */\n\tpriority?: number;\n\t/** Execution phase (default: 'fixedUpdate') */\n\tphase?: SystemPhase;\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Create a velocity component.\n *\n * @param x The x velocity\n * @param y The y velocity\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createVelocity(50, -25),\n * projectile: true,\n * });\n * ```\n */\nexport function createVelocity(x: number, y: number): Pick<MovementComponentTypes, 'velocity'> {\n\treturn {\n\t\tvelocity: { x, y },\n\t};\n}\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a movement bundle for ECSpresso.\n *\n * This bundle provides:\n * - Movement update system that integrates velocity into localTransform\n * - Processes all entities with both localTransform and velocity components\n *\n * Note: This bundle modifies localTransform. The transform bundle's propagation\n * system will then compute worldTransform for use by other systems.\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso\n * .create<Components, Events, Resources>()\n * .withBundle(createTransformBundle())\n * .withBundle(createMovementBundle())\n * .build();\n *\n * // Spawn entity with movement\n * ecs.spawn({\n * ...createTransform(100, 200),\n * ...createVelocity(50, -25),\n * sprite,\n * });\n * ```\n */\nexport function createMovementBundle(\n\toptions?: MovementBundleOptions\n): Bundle<MovementComponentTypes, {}, {}> {\n\tconst {\n\t\tsystemGroup = 'physics',\n\t\tpriority = 1000,\n\t\tphase = 'fixedUpdate',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<MovementComponentTypes, {}, {}>('movement');\n\n\tbundle\n\t\t.addSystem('movement')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('movingEntities', {\n\t\t\twith: ['localTransform', 'velocity'],\n\t\t})\n\t\t.setProcess((queries, deltaTime, ecs) => {\n\t\t\tfor (const entity of queries.movingEntities) {\n\t\t\t\tconst { localTransform, velocity } = entity.components;\n\t\t\t\tlocalTransform.x += velocity.x * deltaTime;\n\t\t\t\tlocalTransform.y += velocity.y * deltaTime;\n\t\t\t\tecs.markChanged(entity.id, 'localTransform');\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\treturn bundle;\n}\n"
6
- ],
7
- "mappings": "2PAOA,iBAAS,kBA8DF,SAAS,CAAc,CAAC,EAAW,EAAqD,CAC9F,MAAO,CACN,SAAU,CAAE,IAAG,GAAE,CAClB,EA+BM,SAAS,CAAoB,CACnC,EACyC,CACzC,IACC,cAAc,UACd,WAAW,KACX,QAAQ,eACL,GAAW,CAAC,EAEV,EAAS,IAAI,EAAuC,UAAU,EAoBpE,OAlBA,EACE,UAAU,UAAU,EACpB,YAAY,CAAQ,EACpB,QAAQ,CAAK,EACb,QAAQ,CAAW,EACnB,SAAS,iBAAkB,CAC3B,KAAM,CAAC,iBAAkB,UAAU,CACpC,CAAC,EACA,WAAW,CAAC,EAAS,EAAW,IAAQ,CACxC,QAAW,KAAU,EAAQ,eAAgB,CAC5C,IAAQ,iBAAgB,YAAa,EAAO,WAC5C,EAAe,GAAK,EAAS,EAAI,EACjC,EAAe,GAAK,EAAS,EAAI,EACjC,EAAI,YAAY,EAAO,GAAI,gBAAgB,GAE5C,EACA,IAAI,EAEC",
8
- "debugId": "3E601B940224B76564756E2164756E21",
9
- "names": []
10
- }