@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,36 @@
1
+ import type { ColorRGBA } from '../util/color.js';
2
+ import type { CameraView } from './camera.js';
3
+ export type AtlasHandle = number;
4
+ export interface AtlasDescriptor {
5
+ image: HTMLImageElement | HTMLCanvasElement | ImageBitmap;
6
+ frames: ReadonlyArray<{
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ }>;
12
+ name?: string;
13
+ }
14
+ export interface TextStyle {
15
+ font: string;
16
+ fill: ColorRGBA;
17
+ align?: 'left' | 'center' | 'right';
18
+ baseline?: 'top' | 'middle' | 'bottom' | 'alphabetic';
19
+ }
20
+ export interface IGraphicsDevice {
21
+ readonly canvas: HTMLCanvasElement;
22
+ readonly viewportWidth: number;
23
+ readonly viewportHeight: number;
24
+ beginFrame(): void;
25
+ endFrame(): void;
26
+ setCamera(cam: Readonly<CameraView>): void;
27
+ registerAtlas(desc: AtlasDescriptor): AtlasHandle;
28
+ releaseAtlas(handle: AtlasHandle): void;
29
+ drawSprite(worldX: number, worldY: number, worldZ: number, atlas: AtlasHandle, frame: number, tint?: Readonly<ColorRGBA>): void;
30
+ drawTile(tileX: number, tileY: number, atlas: AtlasHandle, frame: number): void;
31
+ drawText(worldX: number, worldY: number, text: string, style: TextStyle): void;
32
+ drawParticle(worldX: number, worldY: number, worldZ: number, size: number, color: Readonly<ColorRGBA>, additive: boolean): void;
33
+ getDrawCallCount(): number;
34
+ }
35
+ export type DeviceBackend = 'canvas2d' | 'webgl2';
36
+ //# sourceMappingURL=graphics-device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphics-device.d.ts","sourceRoot":"","sources":["../../src/renderer/graphics-device.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAGjC,MAAM,WAAW,eAAe;IAE9B,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,WAAW,CAAC;IAG1D,MAAM,EAAE,aAAa,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEtE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;CACvD;AAID,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAGhC,UAAU,IAAI,IAAI,CAAC;IACnB,QAAQ,IAAI,IAAI,CAAC;IAIjB,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAG3C,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAAC;IAClD,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAMxC,UAAU,CACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,GACzB,IAAI,CAAC;IAER,QAAQ,CACN,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,MAAM,GACZ,IAAI,CAAC;IAER,QAAQ,CACN,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GACf,IAAI,CAAC;IAOR,YAAY,CACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,EAC1B,QAAQ,EAAE,OAAO,GAChB,IAAI,CAAC;IAIR,gBAAgB,IAAI,MAAM,CAAC;CAC5B;AAGD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,12 @@
1
+ // Graphics device abstraction for the Loom Engine.
2
+ //
3
+ // All higher-level systems talk to IGraphicsDevice, never to the
4
+ // concrete backend. This is the Babylon.js ThinEngine split: lean
5
+ // GPU core (the device) + higher-level scene logic (everything
6
+ // else). See PRIOR-ART.md for the citation.
7
+ //
8
+ // Two backends ship over time:
9
+ // Canvas2DDevice - v1 primary, Phase 1
10
+ // WebGL2Device - Phase 2 if profiling demands
11
+ export {};
12
+ //# sourceMappingURL=graphics-device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphics-device.js","sourceRoot":"","sources":["../../src/renderer/graphics-device.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,iEAAiE;AACjE,kEAAkE;AAClE,+DAA+D;AAC/D,4CAA4C;AAC5C,EAAE;AACF,+BAA+B;AAC/B,0CAA0C;AAC1C,mDAAmD"}
@@ -0,0 +1,11 @@
1
+ import type { Vec2, Vec3 } from '../util/math.js';
2
+ export declare const ISO_TILE_WIDTH: number;
3
+ export declare const ISO_TILE_HEIGHT: number;
4
+ export declare const ISO_HALF_W: number;
5
+ export declare const ISO_HALF_H: number;
6
+ export declare const ISO_Z_SCALE: number;
7
+ export declare function tileToIso(tileX: number, tileY: number, out: Vec2): Vec2;
8
+ export declare function worldToIso(world: Vec3, out: Vec2): Vec2;
9
+ export declare function isoToTile(isoX: number, isoY: number, out: Vec2): Vec2;
10
+ export declare function isoDepthKey(world: Vec3): number;
11
+ //# sourceMappingURL=iso-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iso-projection.d.ts","sourceRoot":"","sources":["../../src/renderer/iso-projection.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAKlD,eAAO,MAAM,cAAc,EAAE,MAAW,CAAC;AACzC,eAAO,MAAM,eAAe,EAAE,MAAW,CAAC;AAC1C,eAAO,MAAM,UAAU,EAAE,MAA2B,CAAC;AACrD,eAAO,MAAM,UAAU,EAAE,MAA4B,CAAC;AAGtD,eAAO,MAAM,WAAW,EAAE,MAAW,CAAC;AAKtC,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAIvE;AAID,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAIvD;AAID,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAWrE;AAOD,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAE/C"}
@@ -0,0 +1,59 @@
1
+ // Isometric projection for the Loom Engine.
2
+ //
3
+ // Standard 2:1 dimetric projection. Tile width:height = 2:1. World
4
+ // coords use (x, y) on the tile grid; iso transform produces screen
5
+ // (sx, sy) where sx = (x - y) * (TILE_W/2), sy = (x + y) * (TILE_H/2).
6
+ //
7
+ // This is a public-domain technique - 1980s arcade games (Q*bert),
8
+ // 1990s ARPGs (Diablo, Baldur's Gate). See PRIOR-ART.md for the
9
+ // citation. We add a Z component to each entity (world height for
10
+ // flying / floating sprites + iso-grid depth sort) but Z does not
11
+ // affect the screen Y projection beyond a fixed offset.
12
+ // Iso tile dimensions in pixels at zoom = 1. Pure 2:1 dimetric.
13
+ // These are engine constants for v1; future zoom levels scale them
14
+ // uniformly.
15
+ export const ISO_TILE_WIDTH = 64;
16
+ export const ISO_TILE_HEIGHT = 32;
17
+ export const ISO_HALF_W = ISO_TILE_WIDTH / 2;
18
+ export const ISO_HALF_H = ISO_TILE_HEIGHT / 2;
19
+ // Z (height above ground) -> screen-space Y offset. 1 world unit of
20
+ // height = ISO_Z_SCALE pixels of vertical lift.
21
+ export const ISO_Z_SCALE = 16;
22
+ // World tile coords -> iso screen-space (before camera transform).
23
+ // y increases southward in world space; iso shifts that to lower-
24
+ // right in screen space.
25
+ export function tileToIso(tileX, tileY, out) {
26
+ out.x = (tileX - tileY) * ISO_HALF_W;
27
+ out.y = (tileX + tileY) * ISO_HALF_H;
28
+ return out;
29
+ }
30
+ // Continuous world-space coords (sub-tile precision) -> iso screen-
31
+ // space. Same formula, no rounding.
32
+ export function worldToIso(world, out) {
33
+ out.x = (world.x - world.y) * ISO_HALF_W;
34
+ out.y = (world.x + world.y) * ISO_HALF_H - world.z * ISO_Z_SCALE;
35
+ return out;
36
+ }
37
+ // Iso screen-space -> world tile coords (inverse projection). Used
38
+ // for cursor-to-tile picking.
39
+ export function isoToTile(isoX, isoY, out) {
40
+ // Solve the 2x2 system:
41
+ // isoX = (x - y) * HALF_W
42
+ // isoY = (x + y) * HALF_H
43
+ // -> x = isoX/(2*HALF_W) + isoY/(2*HALF_H)
44
+ // -> y = isoY/(2*HALF_H) - isoX/(2*HALF_W)
45
+ const a = isoX / (2 * ISO_HALF_W);
46
+ const b = isoY / (2 * ISO_HALF_H);
47
+ out.x = a + b;
48
+ out.y = b - a;
49
+ return out;
50
+ }
51
+ // Depth-sort key for iso rendering. Lower = drawn first (back).
52
+ // Uses world (x + y) as the diagonal sort axis. Z (height) breaks
53
+ // ties so taller objects of the same diagonal draw later (front).
54
+ //
55
+ // Inspired by standard iso-game depth sorting; see PRIOR-ART.md.
56
+ export function isoDepthKey(world) {
57
+ return (world.x + world.y) * 1000 + world.z;
58
+ }
59
+ //# sourceMappingURL=iso-projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iso-projection.js","sourceRoot":"","sources":["../../src/renderer/iso-projection.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,uEAAuE;AACvE,EAAE;AACF,mEAAmE;AACnE,gEAAgE;AAChE,kEAAkE;AAClE,kEAAkE;AAClE,wDAAwD;AAIxD,gEAAgE;AAChE,mEAAmE;AACnE,aAAa;AACb,MAAM,CAAC,MAAM,cAAc,GAAW,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,eAAe,GAAW,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAW,cAAc,GAAG,CAAC,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAW,eAAe,GAAG,CAAC,CAAC;AACtD,oEAAoE;AACpE,gDAAgD;AAChD,MAAM,CAAC,MAAM,WAAW,GAAW,EAAE,CAAC;AAEtC,mEAAmE;AACnE,kEAAkE;AAClE,yBAAyB;AACzB,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,KAAa,EAAE,GAAS;IAC/D,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,UAAU,CAAC;IACrC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,UAAU,CAAC;IACrC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oEAAoE;AACpE,oCAAoC;AACpC,MAAM,UAAU,UAAU,CAAC,KAAW,EAAE,GAAS;IAC/C,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;IACzC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,KAAK,CAAC,CAAC,GAAG,WAAW,CAAC;IACjE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AACnE,8BAA8B;AAC9B,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY,EAAE,GAAS;IAC7D,wBAAwB;IACxB,4BAA4B;IAC5B,4BAA4B;IAC5B,2CAA2C;IAC3C,2CAA2C;IAC3C,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;IAClC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACd,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gEAAgE;AAChE,kEAAkE;AAClE,kEAAkE;AAClE,EAAE;AACF,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,KAAW;IACrC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,28 @@
1
+ export declare class ResourceRegistry {
2
+ private resources;
3
+ set<T>(key: string, value: T): void;
4
+ get<T>(key: string): T | undefined;
5
+ require<T>(key: string): T;
6
+ has(key: string): boolean;
7
+ remove(key: string): boolean;
8
+ keys(): IterableIterator<string>;
9
+ }
10
+ export interface TimeResource {
11
+ elapsed: number;
12
+ delta: number;
13
+ frame: number;
14
+ }
15
+ export declare function createTimeResource(): TimeResource;
16
+ export declare const RESOURCE_TIME = "time";
17
+ export declare const RESOURCE_CAMERA = "camera";
18
+ export declare const RESOURCE_DEVICE = "device";
19
+ export interface VeilBudgetResource {
20
+ particleBudget: number;
21
+ shaderBudget: number;
22
+ eventBudget: number;
23
+ audioBudget: number;
24
+ lastUpdatedFrame: number;
25
+ }
26
+ export declare function createVeilBudgetResource(): VeilBudgetResource;
27
+ export declare const RESOURCE_VEIL_BUDGET = "veil_budget";
28
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAUA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAmC;IAEpD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAInC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAMlC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC;IAQ1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;CAGjC;AAID,MAAM,WAAW,YAAY;IAE3B,OAAO,EAAE,MAAM,CAAC;IAGhB,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAED,eAAO,MAAM,aAAa,SAAS,CAAC;AACpC,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,eAAe,WAAW,CAAC;AAexC,MAAM,WAAW,kBAAkB;IAKjC,cAAc,EAAE,MAAM,CAAC;IAEvB,YAAY,EAAE,MAAM,CAAC;IAErB,WAAW,EAAE,MAAM,CAAC;IAKpB,WAAW,EAAE,MAAM,CAAC;IAGpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,wBAAwB,IAAI,kBAAkB,CAQ7D;AAED,eAAO,MAAM,oBAAoB,gBAAgB,CAAC"}
@@ -0,0 +1,53 @@
1
+ // Resource registry for the Loom Engine ECS.
2
+ //
3
+ // Resources are singleton state injected into systems by string key.
4
+ // Time, Input, Camera, RNG, etc. all live here. The registry is just
5
+ // a typed Map.
6
+ //
7
+ // Inspired by Bevy's Resource pattern (see PRIOR-ART.md). Bevy uses
8
+ // type-as-key via Rust's TypeId; in TS we use string keys with a
9
+ // generic accessor.
10
+ export class ResourceRegistry {
11
+ resources = new Map();
12
+ set(key, value) {
13
+ this.resources.set(key, value);
14
+ }
15
+ get(key) {
16
+ return this.resources.get(key);
17
+ }
18
+ // Throws if missing. Useful for resources that must always exist
19
+ // (Time, Camera) so the call site stays terse.
20
+ require(key) {
21
+ const v = this.resources.get(key);
22
+ if (v === undefined) {
23
+ throw new Error('ResourceRegistry: required resource "' + key + '" not registered');
24
+ }
25
+ return v;
26
+ }
27
+ has(key) {
28
+ return this.resources.has(key);
29
+ }
30
+ remove(key) {
31
+ return this.resources.delete(key);
32
+ }
33
+ keys() {
34
+ return this.resources.keys();
35
+ }
36
+ }
37
+ export function createTimeResource() {
38
+ return { elapsed: 0, delta: 0, frame: 0 };
39
+ }
40
+ export const RESOURCE_TIME = 'time';
41
+ export const RESOURCE_CAMERA = 'camera';
42
+ export const RESOURCE_DEVICE = 'device';
43
+ export function createVeilBudgetResource() {
44
+ return {
45
+ particleBudget: 4096,
46
+ shaderBudget: 8,
47
+ eventBudget: 256,
48
+ audioBudget: 1.0,
49
+ lastUpdatedFrame: -1,
50
+ };
51
+ }
52
+ export const RESOURCE_VEIL_BUDGET = 'veil_budget';
53
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,eAAe;AACf,EAAE;AACF,oEAAoE;AACpE,iEAAiE;AACjE,oBAAoB;AAEpB,MAAM,OAAO,gBAAgB;IACnB,SAAS,GAAyB,IAAI,GAAG,EAAE,CAAC;IAEpD,GAAG,CAAI,GAAW,EAAE,KAAQ;QAC1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,GAAG,CAAI,GAAW;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAkB,CAAC;IAClD,CAAC;IAED,iEAAiE;IACjE,+CAA+C;IAC/C,OAAO,CAAI,GAAW;QACpB,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,uCAAuC,GAAG,GAAG,GAAG,kBAAkB,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,CAAM,CAAC;IAChB,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;CACF;AAcD,MAAM,UAAU,kBAAkB;IAChC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AACpC,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AACxC,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AAmCxC,MAAM,UAAU,wBAAwB;IACtC,OAAO;QACL,cAAc,EAAE,IAAI;QACpB,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,GAAG;QAChB,WAAW,EAAE,GAAG;QAChB,gBAAgB,EAAE,CAAC,CAAC;KACrB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { World } from './world.js';
2
+ export interface System {
3
+ readonly name: string;
4
+ update(world: World, dt: number): void;
5
+ }
6
+ export declare const SYSTEM_PHASE_INPUT = 0;
7
+ export declare const SYSTEM_PHASE_LOGIC = 1;
8
+ export declare const SYSTEM_PHASE_PHYSICS = 2;
9
+ export declare const SYSTEM_PHASE_ANIMATION = 3;
10
+ export declare const SYSTEM_PHASE_RENDER = 4;
11
+ export declare const SYSTEM_PHASE_POST_RENDER = 5;
12
+ export type SystemPhase = typeof SYSTEM_PHASE_INPUT | typeof SYSTEM_PHASE_LOGIC | typeof SYSTEM_PHASE_PHYSICS | typeof SYSTEM_PHASE_ANIMATION | typeof SYSTEM_PHASE_RENDER | typeof SYSTEM_PHASE_POST_RENDER;
13
+ export declare const SYSTEM_PHASES_IN_ORDER: ReadonlyArray<SystemPhase>;
14
+ //# sourceMappingURL=system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,MAAM;IAGrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAID,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,MAAM,MAAM,WAAW,GACnB,OAAO,kBAAkB,GACzB,OAAO,kBAAkB,GACzB,OAAO,oBAAoB,GAC3B,OAAO,sBAAsB,GAC7B,OAAO,mBAAmB,GAC1B,OAAO,wBAAwB,CAAC;AAEpC,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,WAAW,CAO7D,CAAC"}
package/dist/system.js ADDED
@@ -0,0 +1,25 @@
1
+ // System interface for the Loom Engine ECS.
2
+ //
3
+ // A System is just a function-with-name. Systems run in registration
4
+ // order, single-threaded, deterministic. No parallel scheduler in v1
5
+ // (Bevy-style work-stealing is post-funding).
6
+ //
7
+ // Systems read/write component pools and resources. They're registered
8
+ // with World.addSystem and run via World.update(dt).
9
+ // System lifecycle phase. The scheduler runs phases in this order
10
+ // every tick. Within a phase, systems run in registration order.
11
+ export const SYSTEM_PHASE_INPUT = 0;
12
+ export const SYSTEM_PHASE_LOGIC = 1;
13
+ export const SYSTEM_PHASE_PHYSICS = 2;
14
+ export const SYSTEM_PHASE_ANIMATION = 3;
15
+ export const SYSTEM_PHASE_RENDER = 4;
16
+ export const SYSTEM_PHASE_POST_RENDER = 5;
17
+ export const SYSTEM_PHASES_IN_ORDER = [
18
+ SYSTEM_PHASE_INPUT,
19
+ SYSTEM_PHASE_LOGIC,
20
+ SYSTEM_PHASE_PHYSICS,
21
+ SYSTEM_PHASE_ANIMATION,
22
+ SYSTEM_PHASE_RENDER,
23
+ SYSTEM_PHASE_POST_RENDER,
24
+ ];
25
+ //# sourceMappingURL=system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,8CAA8C;AAC9C,EAAE;AACF,uEAAuE;AACvE,qDAAqD;AAYrD,kEAAkE;AAClE,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAS1C,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IAChE,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,sBAAsB;IACtB,mBAAmB;IACnB,wBAAwB;CACzB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare const POOL_ANIMATION = "animation";
4
+ export declare class AnimationSystem implements System {
5
+ readonly name: string;
6
+ update(world: World, dt: number): void;
7
+ }
8
+ //# sourceMappingURL=animation-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-system.d.ts","sourceRoot":"","sources":["../../src/systems/animation-system.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAkBzC,eAAO,MAAM,cAAc,cAAc,CAAC;AAc1C,qBAAa,eAAgB,YAAW,MAAM;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAe;IAEpC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;CAiDvC"}
@@ -0,0 +1,77 @@
1
+ // AnimationSystem - advances each active AnimationState's elapsedMs
2
+ // and writes the resolved frame index back to SpritePool.
3
+ //
4
+ // Runs in SYSTEM_PHASE_ANIMATION. By the time SpriteRenderSystem
5
+ // runs in SYSTEM_PHASE_RENDER, every entity's frame is up-to-date.
6
+ //
7
+ // Lookup cost per entity: O(clipCount) to find the named clip, then
8
+ // O(1) for uniform-fps clips or O(framesInClip) for per-frame
9
+ // duration clips. For Phase 3 / typical clips of <16 frames, no
10
+ // optimization beyond a linear scan. Phase 4+ may swap in a clip-
11
+ // name-to-index hash if profiling exposes hotspots.
12
+ import { POOL_SPRITE } from '../world.js';
13
+ import { ANIMATION_FLAG_ACTIVE, ANIMATION_FLAG_FINISHED, } from '../animation/animation-state-pool.js';
14
+ import { frameInClipAt, manifestFrameIndex, clipDurationMs, } from '../animation/animation-clip.js';
15
+ // Conventional pool key. Engine.create registers AnimationStatePool
16
+ // under this name; Phase 3 demo + ARPG systems look it up by it.
17
+ export const POOL_ANIMATION = 'animation';
18
+ // Find a clip by name on a manifest. Linear scan is fine for the
19
+ // typical clip count (idle / walk / run / attack / hit / death = ~6).
20
+ function findClip(clips, name) {
21
+ for (let i = 0; i < clips.length; i++) {
22
+ if (clips[i]?.name === name)
23
+ return clips[i];
24
+ }
25
+ return undefined;
26
+ }
27
+ export class AnimationSystem {
28
+ name = 'animation';
29
+ update(world, dt) {
30
+ const animations = world.getPool(POOL_ANIMATION);
31
+ const sprites = world.getPool(POOL_SPRITE);
32
+ if (!animations || !sprites)
33
+ return;
34
+ // dt is seconds; manifests work in milliseconds.
35
+ const dtMs = dt * 1000;
36
+ const hwm = animations.getHighWaterMark();
37
+ for (let i = 1; i < hwm; i++) { // index 0 reserved for NULL
38
+ const flags = animations.flags[i] ?? 0;
39
+ if ((flags & ANIMATION_FLAG_ACTIVE) === 0)
40
+ continue;
41
+ const manifest = animations.manifest[i];
42
+ const clipName = animations.clipName[i] ?? '';
43
+ if (!manifest || !clipName)
44
+ continue;
45
+ const clip = findClip(manifest.clips, clipName);
46
+ if (!clip)
47
+ continue;
48
+ const prevElapsed = animations.elapsedMs[i] ?? 0;
49
+ const nextElapsed = prevElapsed + dtMs;
50
+ if (clip.loop) {
51
+ animations.elapsedMs[i] = nextElapsed;
52
+ }
53
+ else {
54
+ // Non-looping: clamp at total duration so subsequent ticks
55
+ // keep returning the last frame, and set FINISHED once we
56
+ // cross the boundary.
57
+ const total = clipDurationMs(clip, manifest.fps);
58
+ if (nextElapsed >= total && total > 0) {
59
+ animations.elapsedMs[i] = total;
60
+ animations.flags[i] = (flags | ANIMATION_FLAG_FINISHED);
61
+ }
62
+ else {
63
+ animations.elapsedMs[i] = nextElapsed;
64
+ }
65
+ }
66
+ const frameInClip = frameInClipAt(clip, animations.elapsedMs[i] ?? 0, manifest.fps);
67
+ const sheetFrame = manifestFrameIndex(clip, frameInClip);
68
+ // SpritePool indexes per entity index, same shape; we can
69
+ // write directly without going through setFrame for fewer
70
+ // function calls in the hot loop.
71
+ if (i < sprites.frame.length) {
72
+ sprites.frame[i] = sheetFrame;
73
+ }
74
+ }
75
+ }
76
+ }
77
+ //# sourceMappingURL=animation-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-system.js","sourceRoot":"","sources":["../../src/systems/animation-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ import { type EntityId } from '../entity.js';
4
+ export interface AttackSystemOptions {
5
+ damage: number;
6
+ range: number;
7
+ player: EntityId;
8
+ }
9
+ export declare class AttackSystem implements System {
10
+ private opts;
11
+ readonly name: string;
12
+ lastTargetIndex: number;
13
+ lastDamageApplied: number;
14
+ constructor(opts: AttackSystemOptions);
15
+ update(world: World, _dt: number): void;
16
+ }
17
+ //# sourceMappingURL=attack-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attack-system.d.ts","sourceRoot":"","sources":["../../src/systems/attack-system.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAazC,OAAO,EAAE,KAAK,QAAQ,EAAc,MAAM,cAAc,CAAC;AAIzD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IAGd,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,qBAAa,YAAa,YAAW,MAAM;IAM7B,OAAO,CAAC,IAAI;IALxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAY;IAEjC,eAAe,EAAE,MAAM,CAAM;IAC7B,iBAAiB,EAAE,MAAM,CAAK;gBAEV,IAAI,EAAE,mBAAmB;IAE7C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAsDxC"}
@@ -0,0 +1,94 @@
1
+ // AttackSystem - reads input + camera + transforms and applies
2
+ // damage to the entity nearest the click point. Phase 7's minimum
3
+ // player attack.
4
+ //
5
+ // For Phase 7's combat slice, this is intentionally simple:
6
+ // - Click anywhere -> damage the nearest pursuing entity
7
+ // - Damage amount is a constructor parameter
8
+ // - Range is a constructor parameter (max world distance from
9
+ // click)
10
+ //
11
+ // Real Survivor combat will replace this with a per-skill router:
12
+ // - Different click positions = different skills
13
+ // - Skills have cooldowns, projectiles, AoE patterns
14
+ // - Knot-context modifies damage type
15
+ // All of which fit the engine's existing surface (TimeResource for
16
+ // cooldowns, ParticleEmitterPool for projectiles, KnotContextResource
17
+ // for damage tinting). For Phase 7's first slice we ship the simplest
18
+ // working attack.
19
+ import { POOL_TRANSFORM } from '../world.js';
20
+ import { POOL_HEALTH } from '../components/health.js';
21
+ import { POOL_PURSUE } from '../components/pursue.js';
22
+ import { RESOURCE_INPUT, } from '../input/input-manager.js';
23
+ import { RESOURCE_CAMERA } from '../resources.js';
24
+ import { isoToTile } from '../renderer/iso-projection.js';
25
+ import { vec2 } from '../util/math.js';
26
+ import { makeEntity } from '../entity.js';
27
+ const SCRATCH_TILE = vec2(0, 0);
28
+ export class AttackSystem {
29
+ opts;
30
+ name = 'attack';
31
+ // Last entity damaged this tick (for VFX hooks). -1 if no hit.
32
+ lastTargetIndex = -1;
33
+ lastDamageApplied = 0;
34
+ constructor(opts) {
35
+ this.opts = opts;
36
+ }
37
+ update(world, _dt) {
38
+ const input = world.resources.get(RESOURCE_INPUT);
39
+ const camera = world.resources.get(RESOURCE_CAMERA);
40
+ if (!input || !camera)
41
+ return;
42
+ const transforms = world.getPool(POOL_TRANSFORM);
43
+ const health = world.getPool(POOL_HEALTH);
44
+ const pursuit = world.getPool(POOL_PURSUE);
45
+ if (!transforms || !health || !pursuit)
46
+ return;
47
+ this.lastTargetIndex = -1;
48
+ this.lastDamageApplied = 0;
49
+ // Only react to a fresh primary-button click.
50
+ const leftClicked = (input.pointerPressedThisFrame & 1) !== 0;
51
+ if (!leftClicked)
52
+ return;
53
+ // Convert click pixel coords -> world iso -> tile.
54
+ const worldIsoX = (input.pointer.x - camera.viewportWidth / 2) / camera.zoom + camera.centerX;
55
+ const worldIsoY = (input.pointer.y - camera.viewportHeight / 2) / camera.zoom + camera.centerY;
56
+ isoToTile(worldIsoX, worldIsoY, SCRATCH_TILE);
57
+ const clickTileX = SCRATCH_TILE.x;
58
+ const clickTileY = SCRATCH_TILE.y;
59
+ // Find the nearest pursuer (i.e. enemy) within range.
60
+ const playerIdx = this.opts.player & 0x00ffffff;
61
+ const hwm = pursuit.getHighWaterMark();
62
+ let bestIdx = -1;
63
+ let bestDist = this.opts.range;
64
+ for (let i = 1; i < hwm; i++) {
65
+ if (i === playerIdx)
66
+ continue; // never damage the player via this attack
67
+ const f = pursuit.flags[i] ?? 0;
68
+ if ((f & 1) === 0)
69
+ continue; // PURSUE_FLAG_ACTIVE
70
+ // Only attack live entities.
71
+ if (health.isDead(makeEntity(i, 0)))
72
+ continue;
73
+ const tx = transforms.x[i] ?? 0;
74
+ const ty = transforms.y[i] ?? 0;
75
+ const dx = tx - clickTileX;
76
+ const dy = ty - clickTileY;
77
+ const d = Math.sqrt(dx * dx + dy * dy);
78
+ if (d < bestDist) {
79
+ bestDist = d;
80
+ bestIdx = i;
81
+ }
82
+ }
83
+ if (bestIdx < 0)
84
+ return;
85
+ const target = makeEntity(bestIdx, 0);
86
+ const now = typeof performance !== 'undefined' ? performance.now() : 0;
87
+ const applied = health.applyDamage(target, this.opts.damage, now);
88
+ if (applied > 0) {
89
+ this.lastTargetIndex = bestIdx;
90
+ this.lastDamageApplied = applied;
91
+ }
92
+ }
93
+ }
94
+ //# sourceMappingURL=attack-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attack-system.js","sourceRoot":"","sources":["../../src/systems/attack-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export interface KillEvent {
4
+ entityIndex: number;
5
+ killerIndex: number | null;
6
+ atMs: number;
7
+ }
8
+ export declare class DeathLog {
9
+ recent: KillEvent[];
10
+ totalKills: number;
11
+ static readonly MAX_KILLS = 64;
12
+ }
13
+ export declare const RESOURCE_DEATH_LOG = "death_log";
14
+ export declare class DamageSystem implements System {
15
+ readonly name: string;
16
+ update(world: World, _dt: number): void;
17
+ }
18
+ //# sourceMappingURL=damage-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"damage-system.d.ts","sourceRoot":"","sources":["../../src/systems/damage-system.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAazC,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,QAAQ;IAEnB,MAAM,EAAE,SAAS,EAAE,CAAM;IACzB,UAAU,EAAE,MAAM,CAAK;IACvB,MAAM,CAAC,QAAQ,CAAC,SAAS,MAAM;CAChC;AAED,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C,qBAAa,YAAa,YAAW,MAAM;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAY;IAEjC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAiDxC"}
@@ -0,0 +1,77 @@
1
+ // DamageSystem - sweeps the HealthPool each tick, removes dead
2
+ // entities (clears their Sprite + Transform components and destroys
3
+ // the entity handle).
4
+ //
5
+ // Death pipeline:
6
+ // 1. AttackSystem (or any gameplay code) calls health.applyDamage
7
+ // 2. If HP drops to 0, HEALTH_FLAG_DEAD is set on the pool
8
+ // 3. DamageSystem next tick walks the pool, sees DEAD flag,
9
+ // detaches Sprite + Transform + Health + Animation +
10
+ // Emitter components, destroys the entity
11
+ // 4. Optional consumers can listen to the event log for kill
12
+ // notifications (Phase 7+ adds a kill-event ring buffer)
13
+ //
14
+ // Runs in PHASE_LOGIC, after AttackSystem + PursueSystem so this
15
+ // frame's damage events have been resolved before cleanup.
16
+ import { POOL_HEALTH, HEALTH_FLAG_ACTIVE, HEALTH_FLAG_DEAD } from '../components/health.js';
17
+ import { POOL_TRANSFORM, POOL_SPRITE } from '../world.js';
18
+ import { POOL_EMITTER } from './particle-emitter-system.js';
19
+ import { POOL_ANIMATION } from './animation-system.js';
20
+ import { makeEntity } from '../entity.js';
21
+ export class DeathLog {
22
+ // Newest first. Capped at MAX_KILLS to bound memory.
23
+ recent = [];
24
+ totalKills = 0;
25
+ static MAX_KILLS = 64;
26
+ }
27
+ export const RESOURCE_DEATH_LOG = 'death_log';
28
+ export class DamageSystem {
29
+ name = 'damage';
30
+ update(world, _dt) {
31
+ const health = world.getPool(POOL_HEALTH);
32
+ if (!health)
33
+ return;
34
+ const transforms = world.getPool(POOL_TRANSFORM);
35
+ const sprites = world.getPool(POOL_SPRITE);
36
+ const emitters = world.getPool(POOL_EMITTER);
37
+ const animations = world.getPool(POOL_ANIMATION);
38
+ const deathLog = world.resources.get(RESOURCE_DEATH_LOG);
39
+ const hwm = health.getHighWaterMark();
40
+ const now = typeof performance !== 'undefined' ? performance.now() : 0;
41
+ for (let i = 1; i < hwm; i++) {
42
+ const f = health.flags[i] ?? 0;
43
+ if ((f & HEALTH_FLAG_ACTIVE) === 0)
44
+ continue;
45
+ if ((f & HEALTH_FLAG_DEAD) === 0)
46
+ continue;
47
+ // Reconstruct an EntityId for the index. Generation isn't
48
+ // material here because the pools all store by index and the
49
+ // entity allocator's destroy will bump the generation.
50
+ const e = makeEntity(i, 0);
51
+ // Detach everything we know about. Pool detach is idempotent
52
+ // and bounds-checked, so unknown components are no-ops.
53
+ health.detach(e);
54
+ transforms?.detach(e);
55
+ sprites?.detach(e);
56
+ emitters?.detach(e);
57
+ animations?.stop(e);
58
+ // Destroy the entity allocator slot. The slot returns to the
59
+ // free list and a future create() will recycle it. The
60
+ // generation bump invalidates any stale handles still held
61
+ // by gameplay code.
62
+ world.destroyEntity(e);
63
+ // Record the kill in the log. Killer attribution is gameplay-
64
+ // specific and not tracked here; AttackSystem can extend the
65
+ // log with killer info when it deals lethal damage.
66
+ if (deathLog) {
67
+ const ev = { entityIndex: i, killerIndex: null, atMs: now };
68
+ deathLog.recent.unshift(ev);
69
+ if (deathLog.recent.length > DeathLog.MAX_KILLS) {
70
+ deathLog.recent.length = DeathLog.MAX_KILLS;
71
+ }
72
+ deathLog.totalKills++;
73
+ }
74
+ }
75
+ }
76
+ }
77
+ //# sourceMappingURL=damage-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"damage-system.js","sourceRoot":"","sources":["../../src/systems/damage-system.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,oEAAoE;AACpE,sBAAsB;AACtB,EAAE;AACF,kBAAkB;AAClB,oEAAoE;AACpE,6DAA6D;AAC7D,8DAA8D;AAC9D,0DAA0D;AAC1D,+CAA+C;AAC/C,+DAA+D;AAC/D,8DAA8D;AAC9D,EAAE;AACF,iEAAiE;AACjE,2DAA2D;AAI3D,OAAO,EAAc,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAKxG,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU1C,MAAM,OAAO,QAAQ;IACnB,qDAAqD;IACrD,MAAM,GAAgB,EAAE,CAAC;IACzB,UAAU,GAAW,CAAC,CAAC;IACvB,MAAM,CAAU,SAAS,GAAG,EAAE,CAAC;;AAGjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE9C,MAAM,OAAO,YAAY;IACd,IAAI,GAAW,QAAQ,CAAC;IAEjC,MAAM,CAAC,KAAY,EAAE,GAAW;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAa,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAgB,cAAc,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAa,WAAW,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAsB,YAAY,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAqB,cAAc,CAAC,CAAC;QACrE,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAW,kBAAkB,CAAC,CAAC;QAEnE,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC7C,IAAI,CAAC,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC;gBAAE,SAAS;YAE3C,0DAA0D;YAC1D,6DAA6D;YAC7D,uDAAuD;YACvD,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAE3B,6DAA6D;YAC7D,wDAAwD;YACxD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;YACtB,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;YACnB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;YAEpB,6DAA6D;YAC7D,uDAAuD;YACvD,2DAA2D;YAC3D,oBAAoB;YACpB,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YAEvB,8DAA8D;YAC9D,6DAA6D;YAC7D,oDAAoD;YACpD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,EAAE,GAAc,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;gBACvE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC5B,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;oBAChD,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC;gBAC9C,CAAC;gBACD,QAAQ,CAAC,UAAU,EAAE,CAAC;YACxB,CAAC;QACH,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 InputSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, _dt: number): void;
6
+ }
7
+ //# sourceMappingURL=input-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-system.d.ts","sourceRoot":"","sources":["../../src/systems/input-system.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAOzC,qBAAa,WAAY,YAAW,MAAM;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAW;IAEhC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAMxC"}