@sadhaka/loom-engine 0.10.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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +344 -0
  3. package/dist/animation/animation-clip.d.ts +12 -0
  4. package/dist/animation/animation-clip.d.ts.map +1 -0
  5. package/dist/animation/animation-clip.js +85 -0
  6. package/dist/animation/animation-clip.js.map +1 -0
  7. package/dist/animation/animation-state-pool.d.ts +25 -0
  8. package/dist/animation/animation-state-pool.d.ts.map +1 -0
  9. package/dist/animation/animation-state-pool.js +113 -0
  10. package/dist/animation/animation-state-pool.js.map +1 -0
  11. package/dist/asset/sprite-sheet-loader.d.ts +41 -0
  12. package/dist/asset/sprite-sheet-loader.d.ts.map +1 -0
  13. package/dist/asset/sprite-sheet-loader.js +313 -0
  14. package/dist/asset/sprite-sheet-loader.js.map +1 -0
  15. package/dist/audio/audio-bus.d.ts +43 -0
  16. package/dist/audio/audio-bus.d.ts.map +1 -0
  17. package/dist/audio/audio-bus.js +258 -0
  18. package/dist/audio/audio-bus.js.map +1 -0
  19. package/dist/combat/mob-catalog.d.ts +29 -0
  20. package/dist/combat/mob-catalog.d.ts.map +1 -0
  21. package/dist/combat/mob-catalog.js +104 -0
  22. package/dist/combat/mob-catalog.js.map +1 -0
  23. package/dist/components/health.d.ts +28 -0
  24. package/dist/components/health.d.ts.map +1 -0
  25. package/dist/components/health.js +150 -0
  26. package/dist/components/health.js.map +1 -0
  27. package/dist/components/interactable.d.ts +30 -0
  28. package/dist/components/interactable.d.ts.map +1 -0
  29. package/dist/components/interactable.js +94 -0
  30. package/dist/components/interactable.js.map +1 -0
  31. package/dist/components/particle-emitter.d.ts +62 -0
  32. package/dist/components/particle-emitter.d.ts.map +1 -0
  33. package/dist/components/particle-emitter.js +193 -0
  34. package/dist/components/particle-emitter.js.map +1 -0
  35. package/dist/components/pursue.d.ts +23 -0
  36. package/dist/components/pursue.d.ts.map +1 -0
  37. package/dist/components/pursue.js +96 -0
  38. package/dist/components/pursue.js.map +1 -0
  39. package/dist/components/ranged-attack.d.ts +44 -0
  40. package/dist/components/ranged-attack.d.ts.map +1 -0
  41. package/dist/components/ranged-attack.js +120 -0
  42. package/dist/components/ranged-attack.js.map +1 -0
  43. package/dist/components/sprite.d.ts +27 -0
  44. package/dist/components/sprite.d.ts.map +1 -0
  45. package/dist/components/sprite.js +122 -0
  46. package/dist/components/sprite.js.map +1 -0
  47. package/dist/components/transform.d.ts +30 -0
  48. package/dist/components/transform.d.ts.map +1 -0
  49. package/dist/components/transform.js +150 -0
  50. package/dist/components/transform.js.map +1 -0
  51. package/dist/director/director-bridge.d.ts +22 -0
  52. package/dist/director/director-bridge.d.ts.map +1 -0
  53. package/dist/director/director-bridge.js +23 -0
  54. package/dist/director/director-bridge.js.map +1 -0
  55. package/dist/director/director-encounter-system.d.ts +21 -0
  56. package/dist/director/director-encounter-system.d.ts.map +1 -0
  57. package/dist/director/director-encounter-system.js +128 -0
  58. package/dist/director/director-encounter-system.js.map +1 -0
  59. package/dist/director/director-system.d.ts +19 -0
  60. package/dist/director/director-system.d.ts.map +1 -0
  61. package/dist/director/director-system.js +179 -0
  62. package/dist/director/director-system.js.map +1 -0
  63. package/dist/director/event-envelope.d.ts +144 -0
  64. package/dist/director/event-envelope.d.ts.map +1 -0
  65. package/dist/director/event-envelope.js +108 -0
  66. package/dist/director/event-envelope.js.map +1 -0
  67. package/dist/director/knot-context-resource.d.ts +25 -0
  68. package/dist/director/knot-context-resource.d.ts.map +1 -0
  69. package/dist/director/knot-context-resource.js +152 -0
  70. package/dist/director/knot-context-resource.js.map +1 -0
  71. package/dist/director/mock-director-bridge.d.ts +18 -0
  72. package/dist/director/mock-director-bridge.d.ts.map +1 -0
  73. package/dist/director/mock-director-bridge.js +75 -0
  74. package/dist/director/mock-director-bridge.js.map +1 -0
  75. package/dist/director/snapshot-recovery.d.ts +37 -0
  76. package/dist/director/snapshot-recovery.d.ts.map +1 -0
  77. package/dist/director/snapshot-recovery.js +180 -0
  78. package/dist/director/snapshot-recovery.js.map +1 -0
  79. package/dist/director/sse-director-bridge.d.ts +42 -0
  80. package/dist/director/sse-director-bridge.d.ts.map +1 -0
  81. package/dist/director/sse-director-bridge.js +280 -0
  82. package/dist/director/sse-director-bridge.js.map +1 -0
  83. package/dist/engine.d.ts +25 -0
  84. package/dist/engine.d.ts.map +1 -0
  85. package/dist/engine.js +166 -0
  86. package/dist/engine.js.map +1 -0
  87. package/dist/entity.d.ts +17 -0
  88. package/dist/entity.d.ts.map +1 -0
  89. package/dist/entity.js +77 -0
  90. package/dist/entity.js.map +1 -0
  91. package/dist/index.d.ts +85 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +64 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/input/input-manager.d.ts +91 -0
  96. package/dist/input/input-manager.d.ts.map +1 -0
  97. package/dist/input/input-manager.js +349 -0
  98. package/dist/input/input-manager.js.map +1 -0
  99. package/dist/input/tap-to-walk.d.ts +27 -0
  100. package/dist/input/tap-to-walk.d.ts.map +1 -0
  101. package/dist/input/tap-to-walk.js +118 -0
  102. package/dist/input/tap-to-walk.js.map +1 -0
  103. package/dist/input/virtual-dpad.d.ts +34 -0
  104. package/dist/input/virtual-dpad.d.ts.map +1 -0
  105. package/dist/input/virtual-dpad.js +267 -0
  106. package/dist/input/virtual-dpad.js.map +1 -0
  107. package/dist/renderer/camera.d.ts +26 -0
  108. package/dist/renderer/camera.d.ts.map +1 -0
  109. package/dist/renderer/camera.js +39 -0
  110. package/dist/renderer/camera.js.map +1 -0
  111. package/dist/renderer/canvas2d-device.d.ts +26 -0
  112. package/dist/renderer/canvas2d-device.d.ts.map +1 -0
  113. package/dist/renderer/canvas2d-device.js +252 -0
  114. package/dist/renderer/canvas2d-device.js.map +1 -0
  115. package/dist/renderer/graphics-device.d.ts +36 -0
  116. package/dist/renderer/graphics-device.d.ts.map +1 -0
  117. package/dist/renderer/graphics-device.js +12 -0
  118. package/dist/renderer/graphics-device.js.map +1 -0
  119. package/dist/renderer/iso-projection.d.ts +11 -0
  120. package/dist/renderer/iso-projection.d.ts.map +1 -0
  121. package/dist/renderer/iso-projection.js +59 -0
  122. package/dist/renderer/iso-projection.js.map +1 -0
  123. package/dist/resources.d.ts +28 -0
  124. package/dist/resources.d.ts.map +1 -0
  125. package/dist/resources.js +53 -0
  126. package/dist/resources.js.map +1 -0
  127. package/dist/system.d.ts +14 -0
  128. package/dist/system.d.ts.map +1 -0
  129. package/dist/system.js +25 -0
  130. package/dist/system.js.map +1 -0
  131. package/dist/systems/animation-system.d.ts +8 -0
  132. package/dist/systems/animation-system.d.ts.map +1 -0
  133. package/dist/systems/animation-system.js +77 -0
  134. package/dist/systems/animation-system.js.map +1 -0
  135. package/dist/systems/attack-system.d.ts +17 -0
  136. package/dist/systems/attack-system.d.ts.map +1 -0
  137. package/dist/systems/attack-system.js +94 -0
  138. package/dist/systems/attack-system.js.map +1 -0
  139. package/dist/systems/damage-system.d.ts +18 -0
  140. package/dist/systems/damage-system.d.ts.map +1 -0
  141. package/dist/systems/damage-system.js +77 -0
  142. package/dist/systems/damage-system.js.map +1 -0
  143. package/dist/systems/input-system.d.ts +7 -0
  144. package/dist/systems/input-system.d.ts.map +1 -0
  145. package/dist/systems/input-system.js +27 -0
  146. package/dist/systems/input-system.js.map +1 -0
  147. package/dist/systems/interaction-system.d.ts +23 -0
  148. package/dist/systems/interaction-system.d.ts.map +1 -0
  149. package/dist/systems/interaction-system.js +120 -0
  150. package/dist/systems/interaction-system.js.map +1 -0
  151. package/dist/systems/particle-emitter-system.d.ts +8 -0
  152. package/dist/systems/particle-emitter-system.d.ts.map +1 -0
  153. package/dist/systems/particle-emitter-system.js +161 -0
  154. package/dist/systems/particle-emitter-system.js.map +1 -0
  155. package/dist/systems/particle-render-system.d.ts +7 -0
  156. package/dist/systems/particle-render-system.d.ts.map +1 -0
  157. package/dist/systems/particle-render-system.js +53 -0
  158. package/dist/systems/particle-render-system.js.map +1 -0
  159. package/dist/systems/particle-simulation-system.d.ts +8 -0
  160. package/dist/systems/particle-simulation-system.d.ts.map +1 -0
  161. package/dist/systems/particle-simulation-system.js +45 -0
  162. package/dist/systems/particle-simulation-system.js.map +1 -0
  163. package/dist/systems/projectile-render-system.d.ts +7 -0
  164. package/dist/systems/projectile-render-system.d.ts.map +1 -0
  165. package/dist/systems/projectile-render-system.js +35 -0
  166. package/dist/systems/projectile-render-system.js.map +1 -0
  167. package/dist/systems/projectile-system.d.ts +7 -0
  168. package/dist/systems/projectile-system.d.ts.map +1 -0
  169. package/dist/systems/projectile-system.js +114 -0
  170. package/dist/systems/projectile-system.js.map +1 -0
  171. package/dist/systems/pursue-system.d.ts +7 -0
  172. package/dist/systems/pursue-system.d.ts.map +1 -0
  173. package/dist/systems/pursue-system.js +83 -0
  174. package/dist/systems/pursue-system.js.map +1 -0
  175. package/dist/systems/ranged-attack-system.d.ts +7 -0
  176. package/dist/systems/ranged-attack-system.d.ts.map +1 -0
  177. package/dist/systems/ranged-attack-system.js +98 -0
  178. package/dist/systems/ranged-attack-system.js.map +1 -0
  179. package/dist/systems/sprite-render-system.d.ts +9 -0
  180. package/dist/systems/sprite-render-system.d.ts.map +1 -0
  181. package/dist/systems/sprite-render-system.js +108 -0
  182. package/dist/systems/sprite-render-system.js.map +1 -0
  183. package/dist/systems/veil-budget-system.d.ts +7 -0
  184. package/dist/systems/veil-budget-system.d.ts.map +1 -0
  185. package/dist/systems/veil-budget-system.js +37 -0
  186. package/dist/systems/veil-budget-system.js.map +1 -0
  187. package/dist/util/color.d.ts +19 -0
  188. package/dist/util/color.d.ts.map +1 -0
  189. package/dist/util/color.js +45 -0
  190. package/dist/util/color.js.map +1 -0
  191. package/dist/util/math.d.ts +26 -0
  192. package/dist/util/math.d.ts.map +1 -0
  193. package/dist/util/math.js +47 -0
  194. package/dist/util/math.js.map +1 -0
  195. package/dist/util/typed-arrays.d.ts +7 -0
  196. package/dist/util/typed-arrays.d.ts.map +1 -0
  197. package/dist/util/typed-arrays.js +42 -0
  198. package/dist/util/typed-arrays.js.map +1 -0
  199. package/dist/vfx/particle-pool.d.ts +61 -0
  200. package/dist/vfx/particle-pool.d.ts.map +1 -0
  201. package/dist/vfx/particle-pool.js +204 -0
  202. package/dist/vfx/particle-pool.js.map +1 -0
  203. package/dist/vfx/projectile-pool.d.ts +56 -0
  204. package/dist/vfx/projectile-pool.d.ts.map +1 -0
  205. package/dist/vfx/projectile-pool.js +157 -0
  206. package/dist/vfx/projectile-pool.js.map +1 -0
  207. package/dist/world.d.ts +23 -0
  208. package/dist/world.d.ts.map +1 -0
  209. package/dist/world.js +101 -0
  210. package/dist/world.js.map +1 -0
  211. package/dist/zone/zone-catalog.d.ts +17 -0
  212. package/dist/zone/zone-catalog.d.ts.map +1 -0
  213. package/dist/zone/zone-catalog.js +116 -0
  214. package/dist/zone/zone-catalog.js.map +1 -0
  215. package/dist/zone/zone-state.d.ts +18 -0
  216. package/dist/zone/zone-state.d.ts.map +1 -0
  217. package/dist/zone/zone-state.js +52 -0
  218. package/dist/zone/zone-state.js.map +1 -0
  219. package/package.json +56 -0
@@ -0,0 +1,27 @@
1
+ // InputSystem - bridges DOM input accumulator into a frame-coherent
2
+ // resource. Runs as the FIRST registered system in PHASE_INPUT, so
3
+ // every subsequent phase reads the same snapshot.
4
+ //
5
+ // The system does two things each tick:
6
+ // 1. Calls inputManager.beginFrame() so per-frame transient state
7
+ // (pressed-this-frame / released-this-frame / wheel delta /
8
+ // touchesStarted / touchesEnded) snapshots from the accumulator
9
+ // 2. Writes the resulting InputSnapshot into the world's resource
10
+ // registry under RESOURCE_INPUT
11
+ //
12
+ // Other systems (camera pan, click-to-spawn, etc.) read from
13
+ // world.resources.get<InputSnapshot>(RESOURCE_INPUT). The snapshot is
14
+ // replaced each tick so consumers should not hold a reference across
15
+ // frames.
16
+ import { RESOURCE_INPUT_MANAGER, RESOURCE_INPUT, } from '../input/input-manager.js';
17
+ export class InputSystem {
18
+ name = 'input';
19
+ update(world, _dt) {
20
+ const manager = world.resources.get(RESOURCE_INPUT_MANAGER);
21
+ if (!manager)
22
+ return;
23
+ manager.beginFrame();
24
+ world.resources.set(RESOURCE_INPUT, manager.snapshot());
25
+ }
26
+ }
27
+ //# sourceMappingURL=input-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-system.js","sourceRoot":"","sources":["../../src/systems/input-system.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,mEAAmE;AACnE,kDAAkD;AAClD,EAAE;AACF,wCAAwC;AACxC,oEAAoE;AACpE,iEAAiE;AACjE,qEAAqE;AACrE,oEAAoE;AACpE,qCAAqC;AACrC,EAAE;AACF,6DAA6D;AAC7D,sEAAsE;AACtE,qEAAqE;AACrE,UAAU;AAIV,OAAO,EAEL,sBAAsB,EACtB,cAAc,GACf,MAAM,2BAA2B,CAAC;AAEnC,MAAM,OAAO,WAAW;IACb,IAAI,GAAW,OAAO,CAAC;IAEhC,MAAM,CAAC,KAAY,EAAE,GAAW;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAe,sBAAsB,CAAC,CAAC;QAC1E,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,OAAO,CAAC,UAAU,EAAE,CAAC;QACrB,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ import { type InteractableKind } from '../components/interactable.js';
4
+ import { type EntityId } from '../entity.js';
5
+ export interface LastInteractionResource {
6
+ entityIndex: number;
7
+ atFrame: number;
8
+ kind: InteractableKind;
9
+ payload: string;
10
+ prompt: string;
11
+ }
12
+ export declare function createLastInteraction(): LastInteractionResource;
13
+ export declare const RESOURCE_LAST_INTERACTION = "last_interaction";
14
+ export interface InteractionSystemOptions {
15
+ player: EntityId;
16
+ }
17
+ export declare class InteractionSystem implements System {
18
+ private opts;
19
+ readonly name: string;
20
+ constructor(opts: InteractionSystemOptions);
21
+ update(world: World, _dt: number): void;
22
+ }
23
+ //# sourceMappingURL=interaction-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction-system.d.ts","sourceRoot":"","sources":["../../src/systems/interaction-system.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAC;AASvC,OAAO,EAAE,KAAK,QAAQ,EAA2B,MAAM,cAAc,CAAC;AAItE,MAAM,WAAW,uBAAuB;IAGtC,WAAW,EAAE,MAAM,CAAC;IAGpB,OAAO,EAAE,MAAM,CAAC;IAEhB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,qBAAqB,IAAI,uBAAuB,CAQ/D;AAED,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IAGvC,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,qBAAa,iBAAkB,YAAW,MAAM;IAGlC,OAAO,CAAC,IAAI;IAFxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAiB;gBAElB,IAAI,EAAE,wBAAwB;IAElD,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAgFxC"}
@@ -0,0 +1,120 @@
1
+ // InteractionSystem - detects player interactions with Interactable
2
+ // entities (NPCs, portals, lore stones).
3
+ //
4
+ // Two trigger paths:
5
+ // 1. Click on or near an interactable entity (within its radius)
6
+ // 2. Press 'KeyE' or 'Enter' while in range of any interactable
7
+ // (per CLAUDE.md hotkey lock list: E + Enter open NPC dialog)
8
+ //
9
+ // Output: writes the most recently triggered interaction to the
10
+ // LastInteractionResource. Gameplay code reads this to show dialog,
11
+ // trigger zone transitions, etc. Cleared after one tick of being
12
+ // read so each interaction fires exactly once.
13
+ import { POOL_TRANSFORM } from '../world.js';
14
+ import { POOL_INTERACTABLE, INTERACTABLE_FLAG_ACTIVE, } from '../components/interactable.js';
15
+ import { RESOURCE_INPUT, } from '../input/input-manager.js';
16
+ import { RESOURCE_CAMERA } from '../resources.js';
17
+ import { isoToTile } from '../renderer/iso-projection.js';
18
+ import { vec2 } from '../util/math.js';
19
+ import { makeEntity, entityIndex } from '../entity.js';
20
+ const SCRATCH_TILE = vec2(0, 0);
21
+ export function createLastInteraction() {
22
+ return {
23
+ entityIndex: -1,
24
+ atFrame: -1,
25
+ kind: 'npc',
26
+ payload: '',
27
+ prompt: '',
28
+ };
29
+ }
30
+ export const RESOURCE_LAST_INTERACTION = 'last_interaction';
31
+ export class InteractionSystem {
32
+ opts;
33
+ name = 'interaction';
34
+ constructor(opts) {
35
+ this.opts = opts;
36
+ }
37
+ update(world, _dt) {
38
+ const input = world.resources.get(RESOURCE_INPUT);
39
+ if (!input)
40
+ return;
41
+ const camera = world.resources.get(RESOURCE_CAMERA);
42
+ const transforms = world.getPool(POOL_TRANSFORM);
43
+ const interactables = world.getPool(POOL_INTERACTABLE);
44
+ const last = world.resources.get(RESOURCE_LAST_INTERACTION);
45
+ if (!camera || !transforms || !interactables || !last)
46
+ return;
47
+ const playerIdx = entityIndex(this.opts.player);
48
+ const px = transforms.x[playerIdx] ?? 0;
49
+ const py = transforms.y[playerIdx] ?? 0;
50
+ // Trigger paths:
51
+ // - Left click anywhere -> find interactable nearest the click
52
+ // point that is within its own radius
53
+ // - 'KeyE' or 'Enter' pressedThisFrame -> find nearest
54
+ // interactable to the player that is within its own radius
55
+ const leftClicked = (input.pointerPressedThisFrame & 1) !== 0;
56
+ const eKey = input.keysPressedThisFrame.has('KeyE') || input.keysPressedThisFrame.has('Enter');
57
+ if (!leftClicked && !eKey)
58
+ return;
59
+ let probeX;
60
+ let probeY;
61
+ if (leftClicked) {
62
+ // Convert click pixel coords -> world iso -> tile.
63
+ const worldIsoX = (input.pointer.x - camera.viewportWidth / 2) / camera.zoom + camera.centerX;
64
+ const worldIsoY = (input.pointer.y - camera.viewportHeight / 2) / camera.zoom + camera.centerY;
65
+ isoToTile(worldIsoX, worldIsoY, SCRATCH_TILE);
66
+ probeX = SCRATCH_TILE.x;
67
+ probeY = SCRATCH_TILE.y;
68
+ }
69
+ else {
70
+ // E / Enter: probe at player's position.
71
+ probeX = px;
72
+ probeY = py;
73
+ }
74
+ // Find the nearest interactable whose radius contains the probe.
75
+ const hwm = interactables.getHighWaterMark();
76
+ let bestIdx = -1;
77
+ let bestDist = Infinity;
78
+ for (let i = 1; i < hwm; i++) {
79
+ if (i === playerIdx)
80
+ continue;
81
+ const f = interactables.flags[i] ?? 0;
82
+ if ((f & INTERACTABLE_FLAG_ACTIVE) === 0)
83
+ continue;
84
+ const tx = transforms.x[i] ?? 0;
85
+ const ty = transforms.y[i] ?? 0;
86
+ const dx = tx - probeX;
87
+ const dy = ty - probeY;
88
+ const dist = Math.sqrt(dx * dx + dy * dy);
89
+ const radius = interactables.radius[i] ?? 0;
90
+ if (dist > radius)
91
+ continue;
92
+ // For E/Enter: also require the player itself be within radius
93
+ // (so the player can't trigger an NPC across the map by pressing
94
+ // E while standing right next to a different one).
95
+ if (eKey) {
96
+ const playerDx = tx - px;
97
+ const playerDy = ty - py;
98
+ if (Math.sqrt(playerDx * playerDx + playerDy * playerDy) > radius)
99
+ continue;
100
+ }
101
+ if (dist < bestDist) {
102
+ bestDist = dist;
103
+ bestIdx = i;
104
+ }
105
+ }
106
+ if (bestIdx < 0)
107
+ return;
108
+ last.entityIndex = bestIdx;
109
+ last.kind = interactables.kind[bestIdx] ?? 'npc';
110
+ last.payload = interactables.payload[bestIdx] ?? '';
111
+ last.prompt = interactables.prompt[bestIdx] ?? '';
112
+ // atFrame is read by the gameplay handler to detect "new" - we
113
+ // bump it to the entity index plus a tick salt so successive
114
+ // identical-target reads still register as new. Simpler: any
115
+ // monotonic increment works.
116
+ last.atFrame = (last.atFrame ?? 0) + 1;
117
+ void makeEntity; // imported but only used by callers via index
118
+ }
119
+ }
120
+ //# sourceMappingURL=interaction-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction-system.js","sourceRoot":"","sources":["../../src/systems/interaction-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare const POOL_EMITTER = "emitter";
4
+ export declare class ParticleEmitterSystem implements System {
5
+ readonly name: string;
6
+ update(world: World, dt: number): void;
7
+ }
8
+ //# sourceMappingURL=particle-emitter-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-emitter-system.d.ts","sourceRoot":"","sources":["../../src/systems/particle-emitter-system.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAOzC,eAAO,MAAM,YAAY,YAAY,CAAC;AAkEtC,qBAAa,qBAAsB,YAAW,MAAM;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAsB;IAE3C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;CA0EvC"}
@@ -0,0 +1,161 @@
1
+ // ParticleEmitterSystem - reads ECS entities that have BOTH a
2
+ // Transform AND a ParticleEmitter, computes how many particles to
3
+ // spawn this tick (continuous rate + pending burst), and pushes
4
+ // each spawn into the shared ParticlePool.
5
+ //
6
+ // Runs in PHASE_LOGIC. Simulation happens in PHASE_PHYSICS afterward,
7
+ // so a particle spawned this tick gets its first physics step in the
8
+ // same frame.
9
+ //
10
+ // Spawn math:
11
+ // - Continuous: emitter.rate * dt + emitter.spawnCarry, floor that
12
+ // for the count this tick, save the fractional part as new carry
13
+ // - Burst: emit min(burstRemaining, available-budget) all at once
14
+ // - Direction: sample a random direction inside the cone defined
15
+ // by (dirX, dirY, dirZ) and coneRadians half-angle
16
+ // - Speed: uniform random in [speedMin, speedMax]
17
+ //
18
+ // The pool's maxParticles cap is enforced inside ParticlePool.spawn;
19
+ // when it returns -1 we stop emitting from this entity for this tick
20
+ // and the missed spawns are NOT carried over (a budget under-fire is
21
+ // a render-budget signal, not a debt).
22
+ import { POOL_TRANSFORM } from '../world.js';
23
+ import { EMITTER_FLAG_ACTIVE } from '../components/particle-emitter.js';
24
+ import { POOL_PARTICLE } from './particle-simulation-system.js';
25
+ export const POOL_EMITTER = 'emitter';
26
+ // Sample a random unit direction inside a cone of half-angle
27
+ // `coneHalf` around the axis (ax, ay, az). For coneHalf = 0 the
28
+ // returned vector equals the axis. For coneHalf = PI it's a random
29
+ // direction on the full sphere.
30
+ //
31
+ // Algorithm: pick a random angle phi in [0, coneHalf], a random
32
+ // azimuth theta in [0, 2*PI], and rotate that into the axis-aligned
33
+ // frame. We use a small-angle approximation that's exact for our
34
+ // 2.5D usage (the axis is mostly axis-aligned in iso space).
35
+ function sampleConeDirection(ax, ay, az, coneHalf, out) {
36
+ if (coneHalf <= 0) {
37
+ out.x = ax;
38
+ out.y = ay;
39
+ out.z = az;
40
+ return;
41
+ }
42
+ // Random angle from axis (cosine-weighted for uniform sphere
43
+ // coverage as cone widens).
44
+ const cosLimit = Math.cos(coneHalf);
45
+ const cosAngle = cosLimit + (1 - cosLimit) * Math.random();
46
+ const sinAngle = Math.sqrt(Math.max(0, 1 - cosAngle * cosAngle));
47
+ const azimuth = Math.random() * Math.PI * 2;
48
+ // Build a local frame around the axis.
49
+ const len = Math.sqrt(ax * ax + ay * ay + az * az) || 1;
50
+ const aux = ax / len;
51
+ const auy = ay / len;
52
+ const auz = az / len;
53
+ // Find a perpendicular to (aux, auy, auz). Use the smaller axis
54
+ // as the helper to avoid degenerate cross products.
55
+ let hx, hy, hz;
56
+ if (Math.abs(aux) < 0.9) {
57
+ hx = 1;
58
+ hy = 0;
59
+ hz = 0;
60
+ }
61
+ else {
62
+ hx = 0;
63
+ hy = 1;
64
+ hz = 0;
65
+ }
66
+ // First perpendicular = axis x helper
67
+ let p1x = auy * hz - auz * hy;
68
+ let p1y = auz * hx - aux * hz;
69
+ let p1z = aux * hy - auy * hx;
70
+ const p1Len = Math.sqrt(p1x * p1x + p1y * p1y + p1z * p1z) || 1;
71
+ p1x /= p1Len;
72
+ p1y /= p1Len;
73
+ p1z /= p1Len;
74
+ // Second perpendicular = axis x p1
75
+ const p2x = auy * p1z - auz * p1y;
76
+ const p2y = auz * p1x - aux * p1z;
77
+ const p2z = aux * p1y - auy * p1x;
78
+ const cosA = Math.cos(azimuth);
79
+ const sinA = Math.sin(azimuth);
80
+ // Combine: result = cosAngle*axis + sinAngle*(cosA*p1 + sinA*p2)
81
+ out.x = cosAngle * aux + sinAngle * (cosA * p1x + sinA * p2x);
82
+ out.y = cosAngle * auy + sinAngle * (cosA * p1y + sinA * p2y);
83
+ out.z = cosAngle * auz + sinAngle * (cosA * p1z + sinA * p2z);
84
+ }
85
+ const SCRATCH_DIR = { x: 0, y: 0, z: 0 };
86
+ export class ParticleEmitterSystem {
87
+ name = 'particle-emitter';
88
+ update(world, dt) {
89
+ const transforms = world.getPool(POOL_TRANSFORM);
90
+ const emitters = world.getPool(POOL_EMITTER);
91
+ const particles = world.getPool(POOL_PARTICLE);
92
+ if (!transforms || !emitters || !particles)
93
+ return;
94
+ const hwm = Math.min(transforms.getHighWaterMark(), emitters.getHighWaterMark());
95
+ for (let i = 1; i < hwm; i++) { // index 0 is NULL_ENTITY
96
+ const flags = emitters.flags[i] ?? 0;
97
+ if ((flags & EMITTER_FLAG_ACTIVE) === 0)
98
+ continue;
99
+ const x = transforms.x[i] ?? 0;
100
+ const y = transforms.y[i] ?? 0;
101
+ const z = transforms.z[i] ?? 0;
102
+ // Total spawns this tick = continuous fractional accumulator
103
+ // + pending burst.
104
+ const carry = (emitters.spawnCarry[i] ?? 0) + (emitters.rate[i] ?? 0) * dt;
105
+ const continuousCount = Math.floor(carry);
106
+ emitters.spawnCarry[i] = carry - continuousCount;
107
+ const burstCount = emitters.burstRemaining[i] ?? 0;
108
+ const total = continuousCount + burstCount;
109
+ if (total === 0)
110
+ continue;
111
+ let spawnedFromBurst = 0;
112
+ const additive = (flags & 0x02) !== 0;
113
+ const dirX = emitters.dirX[i] ?? 0;
114
+ const dirY = emitters.dirY[i] ?? -1;
115
+ const dirZ = emitters.dirZ[i] ?? 0;
116
+ const coneHalf = emitters.coneRadians[i] ?? 0;
117
+ const speedMin = emitters.speedMin[i] ?? 0;
118
+ const speedMax = emitters.speedMax[i] ?? speedMin;
119
+ const ax = emitters.ax[i] ?? 0;
120
+ const ay = emitters.ay[i] ?? 0;
121
+ const az = emitters.az[i] ?? 0;
122
+ const life = emitters.particleLife[i] ?? 1;
123
+ const startSize = emitters.startSize[i] ?? 4;
124
+ const endSize = emitters.endSize[i] ?? startSize;
125
+ for (let k = 0; k < total; k++) {
126
+ sampleConeDirection(dirX, dirY, dirZ, coneHalf, SCRATCH_DIR);
127
+ const speed = speedMin + Math.random() * (speedMax - speedMin);
128
+ const slot = particles.spawn({
129
+ x, y, z,
130
+ vx: SCRATCH_DIR.x * speed,
131
+ vy: SCRATCH_DIR.y * speed,
132
+ vz: SCRATCH_DIR.z * speed,
133
+ ax, ay, az,
134
+ life,
135
+ size: startSize,
136
+ endSize,
137
+ color: {
138
+ r: emitters.startR[i] ?? 1,
139
+ g: emitters.startG[i] ?? 1,
140
+ b: emitters.startB[i] ?? 1,
141
+ a: emitters.startA[i] ?? 1,
142
+ },
143
+ endColor: {
144
+ r: emitters.endR[i] ?? 1,
145
+ g: emitters.endG[i] ?? 1,
146
+ b: emitters.endB[i] ?? 1,
147
+ a: emitters.endA[i] ?? 0,
148
+ },
149
+ additive,
150
+ });
151
+ if (slot < 0)
152
+ break; // budget hit
153
+ if (k < burstCount)
154
+ spawnedFromBurst++;
155
+ }
156
+ // Reduce burstRemaining only by what we actually spawned.
157
+ emitters.burstRemaining[i] = burstCount - spawnedFromBurst;
158
+ }
159
+ }
160
+ }
161
+ //# sourceMappingURL=particle-emitter-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-emitter-system.js","sourceRoot":"","sources":["../../src/systems/particle-emitter-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare class ParticleRenderSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, _dt: number): void;
6
+ }
7
+ //# sourceMappingURL=particle-render-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-render-system.d.ts","sourceRoot":"","sources":["../../src/systems/particle-render-system.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAYzC,qBAAa,oBAAqB,YAAW,MAAM;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAqB;IAE1C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAwCxC"}
@@ -0,0 +1,53 @@
1
+ // ParticleRenderSystem - iterates the ParticlePool and submits
2
+ // drawParticle calls to the device.
3
+ //
4
+ // Runs in PHASE_RENDER. Registration order matters: this system
5
+ // should be registered AFTER SpriteRenderSystem so particles draw
6
+ // on top of sprites (typical "VFX above" layering). Caller can
7
+ // reverse the order to put particles UNDER sprites for, say, ground-
8
+ // level smoke trails.
9
+ //
10
+ // Particle draw order is insertion order; the pool's free-list
11
+ // recycles slots so the order isn't strictly back-to-front. For
12
+ // additive-blended particles this doesn't matter; for alpha-blended
13
+ // the visual difference is rarely noticeable at typical particle
14
+ // sizes. Phase 4 ships without sort to keep the hot loop tight;
15
+ // Phase 5+ may add a sorted-render variant if profiling demands.
16
+ import { PARTICLE_FLAG_ALIVE, PARTICLE_FLAG_ADDITIVE } from '../vfx/particle-pool.js';
17
+ import { RESOURCE_DEVICE, RESOURCE_CAMERA, } from '../resources.js';
18
+ import { POOL_PARTICLE } from './particle-simulation-system.js';
19
+ const SCRATCH_COLOR = { r: 1, g: 1, b: 1, a: 1 };
20
+ export class ParticleRenderSystem {
21
+ name = 'particle-render';
22
+ update(world, _dt) {
23
+ const pool = world.getPool(POOL_PARTICLE);
24
+ const device = world.resources.get(RESOURCE_DEVICE);
25
+ const camera = world.resources.get(RESOURCE_CAMERA);
26
+ if (!pool || !device || !camera)
27
+ return;
28
+ if (pool.getLiveCount() === 0)
29
+ return;
30
+ device.setCamera(camera);
31
+ const hwm = pool.getHighWaterMark();
32
+ for (let i = 0; i < hwm; i++) {
33
+ const f = pool.flags[i] ?? 0;
34
+ if ((f & PARTICLE_FLAG_ALIVE) === 0)
35
+ continue;
36
+ const life = pool.life[i] ?? 0;
37
+ const maxLife = pool.maxLife[i] ?? 1;
38
+ // t = 0 at spawn, t = 1 at death.
39
+ const t = maxLife > 0 ? 1 - life / maxLife : 1;
40
+ const r = (pool.r0[i] ?? 1) + ((pool.r1[i] ?? 1) - (pool.r0[i] ?? 1)) * t;
41
+ const g = (pool.g0[i] ?? 1) + ((pool.g1[i] ?? 1) - (pool.g0[i] ?? 1)) * t;
42
+ const b = (pool.b0[i] ?? 1) + ((pool.b1[i] ?? 1) - (pool.b0[i] ?? 1)) * t;
43
+ const a = (pool.a0[i] ?? 1) + ((pool.a1[i] ?? 0) - (pool.a0[i] ?? 1)) * t;
44
+ const size = (pool.size[i] ?? 4) + ((pool.endSize[i] ?? 4) - (pool.size[i] ?? 4)) * t;
45
+ SCRATCH_COLOR.r = r;
46
+ SCRATCH_COLOR.g = g;
47
+ SCRATCH_COLOR.b = b;
48
+ SCRATCH_COLOR.a = a;
49
+ device.drawParticle(pool.x[i] ?? 0, pool.y[i] ?? 0, pool.z[i] ?? 0, size, SCRATCH_COLOR, (f & PARTICLE_FLAG_ADDITIVE) !== 0);
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=particle-render-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-render-system.js","sourceRoot":"","sources":["../../src/systems/particle-render-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare const POOL_PARTICLE = "particle";
4
+ export declare class ParticleSimulationSystem implements System {
5
+ readonly name: string;
6
+ update(world: World, dt: number): void;
7
+ }
8
+ //# sourceMappingURL=particle-simulation-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-simulation-system.d.ts","sourceRoot":"","sources":["../../src/systems/particle-simulation-system.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,eAAO,MAAM,aAAa,aAAa,CAAC;AAExC,qBAAa,wBAAyB,YAAW,MAAM;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAyB;IAE9C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;CA8BvC"}
@@ -0,0 +1,45 @@
1
+ // ParticleSimulationSystem - advances every live particle's state
2
+ // each tick. Position, velocity, life. Particles whose life drops
3
+ // below zero are killed and their slots returned to the free list.
4
+ //
5
+ // Runs in PHASE_PHYSICS, after the emitter system has spawned this
6
+ // frame's new particles (emitter runs in PHASE_LOGIC).
7
+ //
8
+ // Hot loop: walks [0, highWaterMark) of the pool, branchless on the
9
+ // alive flag (skip via continue), in-place mutation. No allocations
10
+ // per particle.
11
+ import { PARTICLE_FLAG_ALIVE } from '../vfx/particle-pool.js';
12
+ export const POOL_PARTICLE = 'particle';
13
+ export class ParticleSimulationSystem {
14
+ name = 'particle-simulation';
15
+ update(world, dt) {
16
+ const pool = world.getPool(POOL_PARTICLE);
17
+ if (!pool)
18
+ return;
19
+ const hwm = pool.getHighWaterMark();
20
+ if (hwm === 0)
21
+ return;
22
+ for (let i = 0; i < hwm; i++) {
23
+ const f = pool.flags[i] ?? 0;
24
+ if ((f & PARTICLE_FLAG_ALIVE) === 0)
25
+ continue;
26
+ const remaining = (pool.life[i] ?? 0) - dt;
27
+ if (remaining <= 0) {
28
+ pool.kill(i);
29
+ continue;
30
+ }
31
+ pool.life[i] = remaining;
32
+ // Velocity integration with constant acceleration.
33
+ const vx = (pool.vx[i] ?? 0) + (pool.ax[i] ?? 0) * dt;
34
+ const vy = (pool.vy[i] ?? 0) + (pool.ay[i] ?? 0) * dt;
35
+ const vz = (pool.vz[i] ?? 0) + (pool.az[i] ?? 0) * dt;
36
+ pool.vx[i] = vx;
37
+ pool.vy[i] = vy;
38
+ pool.vz[i] = vz;
39
+ pool.x[i] = (pool.x[i] ?? 0) + vx * dt;
40
+ pool.y[i] = (pool.y[i] ?? 0) + vy * dt;
41
+ pool.z[i] = (pool.z[i] ?? 0) + vz * dt;
42
+ }
43
+ }
44
+ }
45
+ //# sourceMappingURL=particle-simulation-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle-simulation-system.js","sourceRoot":"","sources":["../../src/systems/particle-simulation-system.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,kEAAkE;AAClE,mEAAmE;AACnE,EAAE;AACF,mEAAmE;AACnE,uDAAuD;AACvD,EAAE;AACF,oEAAoE;AACpE,oEAAoE;AACpE,gBAAgB;AAIhB,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE5E,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;AAExC,MAAM,OAAO,wBAAwB;IAC1B,IAAI,GAAW,qBAAqB,CAAC;IAE9C,MAAM,CAAC,KAAY,EAAE,EAAU;QAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAe,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpC,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO;QAEtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC;gBAAE,SAAS;YAE9C,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YAC3C,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACb,SAAS;YACX,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;YAEzB,mDAAmD;YACnD,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YACtD,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YACtD,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YACtD,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YAEhB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare class ProjectileRenderSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, _dt: number): void;
6
+ }
7
+ //# sourceMappingURL=projectile-render-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectile-render-system.d.ts","sourceRoot":"","sources":["../../src/systems/projectile-render-system.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAWzC,qBAAa,sBAAuB,YAAW,MAAM;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAuB;IAE5C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CA0BxC"}
@@ -0,0 +1,35 @@
1
+ // ProjectileRenderSystem - draws live projectiles each frame.
2
+ //
3
+ // Reuses Canvas2DDevice.drawParticle for rendering since projectiles
4
+ // are visually similar to particles (small bright dots / lines). For
5
+ // a dedicated arrow/bolt sprite, future work can extend with a
6
+ // drawProjectile primitive that renders an oriented sprite. For v1
7
+ // of Phase 7 deeper, code-painted dots are sufficient.
8
+ import { POOL_PROJECTILE, PROJECTILE_FLAG_ALIVE } from '../vfx/projectile-pool.js';
9
+ import { RESOURCE_DEVICE, RESOURCE_CAMERA, } from '../resources.js';
10
+ const SCRATCH_COLOR = { r: 1, g: 1, b: 1, a: 1 };
11
+ export class ProjectileRenderSystem {
12
+ name = 'projectile-render';
13
+ update(world, _dt) {
14
+ const pool = world.getPool(POOL_PROJECTILE);
15
+ const device = world.resources.get(RESOURCE_DEVICE);
16
+ const camera = world.resources.get(RESOURCE_CAMERA);
17
+ if (!pool || !device || !camera)
18
+ return;
19
+ if (pool.getLiveCount() === 0)
20
+ return;
21
+ device.setCamera(camera);
22
+ const hwm = pool.getHighWaterMark();
23
+ for (let i = 0; i < hwm; i++) {
24
+ const f = pool.flags[i] ?? 0;
25
+ if ((f & PROJECTILE_FLAG_ALIVE) === 0)
26
+ continue;
27
+ SCRATCH_COLOR.r = pool.r[i] ?? 1;
28
+ SCRATCH_COLOR.g = pool.g[i] ?? 1;
29
+ SCRATCH_COLOR.b = pool.b[i] ?? 1;
30
+ SCRATCH_COLOR.a = pool.a[i] ?? 1;
31
+ device.drawParticle(pool.x[i] ?? 0, pool.y[i] ?? 0, pool.z[i] ?? 0, pool.size[i] ?? 4, SCRATCH_COLOR, true);
32
+ }
33
+ }
34
+ }
35
+ //# sourceMappingURL=projectile-render-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectile-render-system.js","sourceRoot":"","sources":["../../src/systems/projectile-render-system.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,+DAA+D;AAC/D,mEAAmE;AACnE,uDAAuD;AAIvD,OAAO,EAAkB,eAAe,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,EACL,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAIzB,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAEjD,MAAM,OAAO,sBAAsB;IACxB,IAAI,GAAW,mBAAmB,CAAC;IAE5C,MAAM,CAAC,KAAY,EAAE,GAAW;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAiB,eAAe,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAkB,eAAe,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAa,eAAe,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;YAAE,OAAO;QACxC,IAAI,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;YAAE,OAAO;QAEtC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,CAAC,GAAG,qBAAqB,CAAC,KAAK,CAAC;gBAAE,SAAS;YAChD,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACjC,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACjC,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACjC,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,CAAC,YAAY,CACjB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EACd,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EACd,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EACd,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EACjB,aAAa,EACb,IAAI,CACL,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare class ProjectileSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, dt: number): void;
6
+ }
7
+ //# sourceMappingURL=projectile-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectile-system.d.ts","sourceRoot":"","sources":["../../src/systems/projectile-system.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAOzC,qBAAa,gBAAiB,YAAW,MAAM;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAgB;IAErC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;CA4FvC"}