@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,114 @@
1
+ // ProjectileSystem - simulates in-flight projectiles + applies hit
2
+ // damage when a projectile contacts a HealthPool entity.
3
+ //
4
+ // Pipeline per tick:
5
+ // 1. For each live projectile:
6
+ // - Decrease life by dt; kill if life <= 0
7
+ // - If homing + target alive: re-aim velocity toward target
8
+ // - Integrate position by velocity * dt
9
+ // - Spatial query: any alive Health-bearing entity within hit
10
+ // radius (size/2 in world units, plus a small fudge)?
11
+ // - On hit: apply damage to target; kill projectile unless
12
+ // PIERCE flag is set
13
+ //
14
+ // Runs in PHASE_PHYSICS, alongside ParticleSimulationSystem. The
15
+ // hit query is O(projectiles * targets); for typical Survivor loads
16
+ // (under 50 of each) that's fine. Phase 8+ may add a spatial hash
17
+ // if profile shows the inner loop dominates.
18
+ import { POOL_PROJECTILE, PROJECTILE_FLAG_ALIVE, PROJECTILE_FLAG_HOMING, PROJECTILE_FLAG_PIERCE } from '../vfx/projectile-pool.js';
19
+ import { POOL_HEALTH } from '../components/health.js';
20
+ import { POOL_TRANSFORM } from '../world.js';
21
+ import { makeEntity } from '../entity.js';
22
+ export class ProjectileSystem {
23
+ name = 'projectile';
24
+ update(world, dt) {
25
+ const pool = world.getPool(POOL_PROJECTILE);
26
+ if (!pool)
27
+ return;
28
+ const transforms = world.getPool(POOL_TRANSFORM);
29
+ const health = world.getPool(POOL_HEALTH);
30
+ if (!transforms || !health)
31
+ return;
32
+ const hwm = pool.getHighWaterMark();
33
+ if (hwm === 0)
34
+ return;
35
+ const now = typeof performance !== 'undefined' ? performance.now() : 0;
36
+ for (let i = 0; i < hwm; i++) {
37
+ const f = pool.flags[i] ?? 0;
38
+ if ((f & PROJECTILE_FLAG_ALIVE) === 0)
39
+ continue;
40
+ // Life decay.
41
+ const remaining = (pool.life[i] ?? 0) - dt;
42
+ if (remaining <= 0) {
43
+ pool.kill(i);
44
+ continue;
45
+ }
46
+ pool.life[i] = remaining;
47
+ // Homing: re-aim velocity toward target each tick.
48
+ if ((f & PROJECTILE_FLAG_HOMING) !== 0) {
49
+ const targetIdx = pool.targetIndex[i] ?? -1;
50
+ if (targetIdx >= 0) {
51
+ const target = makeEntity(targetIdx, 0);
52
+ if (health.isAlive(target)) {
53
+ const tx = transforms.x[targetIdx] ?? 0;
54
+ const ty = transforms.y[targetIdx] ?? 0;
55
+ const tz = transforms.z[targetIdx] ?? 0;
56
+ const dx = tx - (pool.x[i] ?? 0);
57
+ const dy = ty - (pool.y[i] ?? 0);
58
+ const dz = tz - (pool.z[i] ?? 0);
59
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
60
+ if (dist > 1e-6) {
61
+ const speed = Math.sqrt((pool.vx[i] ?? 0) ** 2 +
62
+ (pool.vy[i] ?? 0) ** 2 +
63
+ (pool.vz[i] ?? 0) ** 2);
64
+ pool.vx[i] = (dx / dist) * speed;
65
+ pool.vy[i] = (dy / dist) * speed;
66
+ pool.vz[i] = (dz / dist) * speed;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ // Integrate position.
72
+ pool.x[i] = (pool.x[i] ?? 0) + (pool.vx[i] ?? 0) * dt;
73
+ pool.y[i] = (pool.y[i] ?? 0) + (pool.vy[i] ?? 0) * dt;
74
+ pool.z[i] = (pool.z[i] ?? 0) + (pool.vz[i] ?? 0) * dt;
75
+ // Hit detection: scan HealthPool for alive entities within
76
+ // hit radius. Skip the projectile's owner.
77
+ const px = pool.x[i] ?? 0;
78
+ const py = pool.y[i] ?? 0;
79
+ const owner = pool.ownerIndex[i] ?? -1;
80
+ const hitRadius = (pool.size[i] ?? 4) * 0.05; // size pixels -> world units, approx
81
+ const hitRadiusSq = hitRadius * hitRadius;
82
+ const targetHwm = health.getHighWaterMark();
83
+ let hitIdx = -1;
84
+ for (let j = 1; j < targetHwm; j++) {
85
+ if (j === owner)
86
+ continue;
87
+ const target = makeEntity(j, 0);
88
+ if (!health.isAlive(target))
89
+ continue;
90
+ const tx = transforms.x[j] ?? 0;
91
+ const ty = transforms.y[j] ?? 0;
92
+ const dx = tx - px;
93
+ const dy = ty - py;
94
+ // Use a slightly larger hit radius for tile-space contact -
95
+ // 0.5 world units is roughly half a tile and matches the
96
+ // existing PursueSystem stopDistance defaults.
97
+ const contactSq = 0.5 * 0.5;
98
+ const distSq = dx * dx + dy * dy;
99
+ if (distSq < Math.max(hitRadiusSq, contactSq)) {
100
+ hitIdx = j;
101
+ break;
102
+ }
103
+ }
104
+ if (hitIdx >= 0) {
105
+ const target = makeEntity(hitIdx, 0);
106
+ health.applyDamage(target, pool.damage[i] ?? 0, now);
107
+ if ((f & PROJECTILE_FLAG_PIERCE) === 0) {
108
+ pool.kill(i);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ //# sourceMappingURL=projectile-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectile-system.js","sourceRoot":"","sources":["../../src/systems/projectile-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 PursueSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, dt: number): void;
6
+ }
7
+ //# sourceMappingURL=pursue-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pursue-system.d.ts","sourceRoot":"","sources":["../../src/systems/pursue-system.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAOzC,qBAAa,YAAa,YAAW,MAAM;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAY;IAEjC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;CAmEvC"}
@@ -0,0 +1,83 @@
1
+ // PursueSystem - per-tick enemy AI advance.
2
+ //
3
+ // For each entity with a PursueComponent + Transform, walks the
4
+ // entity toward its target's Transform at the configured speed.
5
+ // When in stop-distance, stops moving and (if contactDamage > 0
6
+ // and cooldown elapsed) applies contact damage to the target.
7
+ //
8
+ // Runs in PHASE_LOGIC, after the input-driven systems (camera,
9
+ // click) so target positions are settled, before DamageSystem
10
+ // (PHASE_LOGIC later in the registration order) so the damage
11
+ // applied this tick is reflected in the cleanup pass.
12
+ import { POOL_TRANSFORM } from '../world.js';
13
+ import { POOL_PURSUE, PURSUE_FLAG_ACTIVE } from '../components/pursue.js';
14
+ import { POOL_HEALTH } from '../components/health.js';
15
+ import { makeEntity } from '../entity.js';
16
+ export class PursueSystem {
17
+ name = 'pursue';
18
+ update(world, dt) {
19
+ const transforms = world.getPool(POOL_TRANSFORM);
20
+ const pursuit = world.getPool(POOL_PURSUE);
21
+ if (!transforms || !pursuit)
22
+ return;
23
+ const health = world.getPool(POOL_HEALTH);
24
+ const hwm = pursuit.getHighWaterMark();
25
+ const now = typeof performance !== 'undefined' ? performance.now() : 0;
26
+ for (let i = 1; i < hwm; i++) {
27
+ const f = pursuit.flags[i] ?? 0;
28
+ if ((f & PURSUE_FLAG_ACTIVE) === 0)
29
+ continue;
30
+ // Skip dead pursuers.
31
+ if (health) {
32
+ const e = makeEntity(i, 0);
33
+ if (health.isDead(e)) {
34
+ pursuit.flags[i] = 0;
35
+ continue;
36
+ }
37
+ }
38
+ const targetIdx = pursuit.targetIndex[i] ?? -1;
39
+ if (targetIdx < 0)
40
+ continue;
41
+ // If the target is dead, stop pursuing.
42
+ if (health && health.isDead(makeEntity(targetIdx, 0))) {
43
+ continue;
44
+ }
45
+ const myX = transforms.x[i] ?? 0;
46
+ const myY = transforms.y[i] ?? 0;
47
+ const tx = transforms.x[targetIdx] ?? 0;
48
+ const ty = transforms.y[targetIdx] ?? 0;
49
+ const dx = tx - myX;
50
+ const dy = ty - myY;
51
+ const dist = Math.sqrt(dx * dx + dy * dy);
52
+ const stopDist = pursuit.stopDistance[i] ?? 0;
53
+ if (dist > stopDist) {
54
+ // Walk toward target. Normalize delta + step by speed * dt.
55
+ const speed = pursuit.speed[i] ?? 0;
56
+ const step = speed * dt;
57
+ const nx = dist > 0 ? dx / dist : 0;
58
+ const ny = dist > 0 ? dy / dist : 0;
59
+ // Don't overshoot.
60
+ const moveX = step >= dist ? dx : nx * step;
61
+ const moveY = step >= dist ? dy : ny * step;
62
+ const e = makeEntity(i, 0);
63
+ transforms.setPosition(e, myX + moveX, myY + moveY, transforms.z[i] ?? 0);
64
+ }
65
+ else {
66
+ // In contact range. Apply contact damage if cooldown elapsed.
67
+ const damage = pursuit.contactDamage[i] ?? 0;
68
+ if (damage > 0 && health) {
69
+ const lastHit = pursuit.lastHitMs[i] ?? -1;
70
+ const cooldown = pursuit.contactCooldownMs[i] ?? 1000;
71
+ if (lastHit < 0 || now - lastHit >= cooldown) {
72
+ const target = makeEntity(targetIdx, 0);
73
+ const applied = health.applyDamage(target, damage, now);
74
+ if (applied > 0) {
75
+ pursuit.lastHitMs[i] = now;
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ //# sourceMappingURL=pursue-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pursue-system.js","sourceRoot":"","sources":["../../src/systems/pursue-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 RangedAttackSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, _dt: number): void;
6
+ }
7
+ //# sourceMappingURL=ranged-attack-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ranged-attack-system.d.ts","sourceRoot":"","sources":["../../src/systems/ranged-attack-system.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAazC,qBAAa,kBAAmB,YAAW,MAAM;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAmB;IAExC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CA6ExC"}
@@ -0,0 +1,98 @@
1
+ // RangedAttackSystem - per-tick ranged-attack scheduler.
2
+ //
3
+ // For each active RangedAttackPool entry:
4
+ // 1. Read the firing entity's transform + the target's transform
5
+ // 2. If target alive AND distance is in [minRange, range] AND
6
+ // cooldown elapsed: spawn a projectile aimed at the target
7
+ // with the configured params
8
+ // 3. Otherwise no-op for this tick
9
+ //
10
+ // Runs in PHASE_LOGIC, after PursueSystem (so positions are up to
11
+ // date) and before DamageSystem cleanup. Spawns projectiles into
12
+ // the shared ProjectilePool which ProjectileSystem (PHASE_PHYSICS)
13
+ // will then advance + collide.
14
+ import { POOL_TRANSFORM } from '../world.js';
15
+ import { POOL_RANGED, RANGED_FLAG_ACTIVE, RANGED_FLAG_HOMING, } from '../components/ranged-attack.js';
16
+ import { POOL_PROJECTILE } from '../vfx/projectile-pool.js';
17
+ import { POOL_HEALTH } from '../components/health.js';
18
+ import { makeEntity } from '../entity.js';
19
+ export class RangedAttackSystem {
20
+ name = 'ranged-attack';
21
+ update(world, _dt) {
22
+ const ranged = world.getPool(POOL_RANGED);
23
+ if (!ranged)
24
+ return;
25
+ const transforms = world.getPool(POOL_TRANSFORM);
26
+ const projectiles = world.getPool(POOL_PROJECTILE);
27
+ const health = world.getPool(POOL_HEALTH);
28
+ if (!transforms || !projectiles || !health)
29
+ return;
30
+ const hwm = ranged.getHighWaterMark();
31
+ const now = typeof performance !== 'undefined' ? performance.now() : 0;
32
+ for (let i = 1; i < hwm; i++) {
33
+ const f = ranged.flags[i] ?? 0;
34
+ if ((f & RANGED_FLAG_ACTIVE) === 0)
35
+ continue;
36
+ // Skip dead firers.
37
+ const firer = makeEntity(i, 0);
38
+ if (!health.isAlive(firer)) {
39
+ ranged.flags[i] = 0;
40
+ continue;
41
+ }
42
+ const targetIdx = ranged.targetIndex[i] ?? -1;
43
+ if (targetIdx < 0)
44
+ continue;
45
+ const target = makeEntity(targetIdx, 0);
46
+ if (!health.isAlive(target))
47
+ continue;
48
+ // Cooldown gate.
49
+ const cooldown = ranged.cooldownMs[i] ?? 1000;
50
+ const lastFire = ranged.lastFireMs[i] ?? -1;
51
+ if (lastFire >= 0 && now - lastFire < cooldown)
52
+ continue;
53
+ // Range check.
54
+ const myX = transforms.x[i] ?? 0;
55
+ const myY = transforms.y[i] ?? 0;
56
+ const tx = transforms.x[targetIdx] ?? 0;
57
+ const ty = transforms.y[targetIdx] ?? 0;
58
+ const dx = tx - myX;
59
+ const dy = ty - myY;
60
+ const dist = Math.sqrt(dx * dx + dy * dy);
61
+ const range = ranged.range[i] ?? 0;
62
+ const minRange = ranged.minRange[i] ?? 0;
63
+ if (dist > range || dist < minRange)
64
+ continue;
65
+ // Fire. Velocity = direction normalized * projectileSpeed.
66
+ const speed = ranged.projectileSpeed[i] ?? 1;
67
+ const norm = dist > 1e-6 ? 1 / dist : 0;
68
+ const vx = dx * norm * speed;
69
+ const vy = dy * norm * speed;
70
+ const homing = (f & RANGED_FLAG_HOMING) !== 0;
71
+ const slot = projectiles.spawn({
72
+ x: myX,
73
+ y: myY,
74
+ z: transforms.z[i] ?? 0.5,
75
+ vx,
76
+ vy,
77
+ vz: 0,
78
+ life: ranged.projectileLife[i] ?? 2.0,
79
+ damage: ranged.damage[i] ?? 1,
80
+ ownerIndex: i,
81
+ targetIndex: homing ? targetIdx : -1,
82
+ size: ranged.projectileSize[i] ?? 5,
83
+ color: {
84
+ r: ranged.r[i] ?? 1,
85
+ g: ranged.g[i] ?? 1,
86
+ b: ranged.b[i] ?? 1,
87
+ a: ranged.a[i] ?? 1,
88
+ },
89
+ homing,
90
+ pierce: false,
91
+ });
92
+ if (slot >= 0) {
93
+ ranged.lastFireMs[i] = now;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ //# sourceMappingURL=ranged-attack-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ranged-attack-system.js","sourceRoot":"","sources":["../../src/systems/ranged-attack-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { System } from '../system.js';
2
+ import type { World } from '../world.js';
3
+ export declare class SpriteRenderSystem implements System {
4
+ readonly name: string;
5
+ private sortBuffer;
6
+ private scratchTint;
7
+ update(world: World, _dt: number): void;
8
+ }
9
+ //# sourceMappingURL=sprite-render-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite-render-system.d.ts","sourceRoot":"","sources":["../../src/systems/sprite-render-system.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAgBzC,qBAAa,kBAAmB,YAAW,MAAM;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAmB;IAIxC,OAAO,CAAC,UAAU,CAAmB;IAOrC,OAAO,CAAC,WAAW,CAEjB;IAEF,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CA0ExC"}
@@ -0,0 +1,108 @@
1
+ // SpriteRenderSystem - iterates entities with both Transform and
2
+ // Sprite components and submits drawSprite calls to the device.
3
+ //
4
+ // This is the first real ECS system in the engine. Everything in
5
+ // the demo that previously called device.drawSprite directly now
6
+ // goes through here.
7
+ //
8
+ // v1 sort: simple back-to-front by iso depth key (x + y), with
9
+ // per-frame allocation of a sort buffer. Phase 2 keeps this naive;
10
+ // optimization (radix sort, persistent buffer) deferred to Phase 4
11
+ // when the VFX framework adds many additive sprites that will
12
+ // expose any sort cost.
13
+ import { POOL_TRANSFORM, POOL_SPRITE } from '../world.js';
14
+ import { TRANSFORM_FLAG_VISIBLE } from '../components/transform.js';
15
+ import { SPRITE_FLAG_ACTIVE, SPRITE_FLAG_TINTED } from '../components/sprite.js';
16
+ import { RESOURCE_DEVICE, RESOURCE_CAMERA, } from '../resources.js';
17
+ export class SpriteRenderSystem {
18
+ name = 'sprite-render';
19
+ // Persistent sort buffer to avoid per-frame allocation. Resized
20
+ // when entity count grows past it.
21
+ sortBuffer = [];
22
+ // Scratch tint object reused across all tinted-sprite draws this
23
+ // frame. Phase 9.1: replaces a per-tinted-sprite allocation with
24
+ // one mutate-and-pass. drawSprite reads ColorRGBA fields by name
25
+ // and does not retain the reference past the call, so reuse is
26
+ // safe and avoids ~N tint-object allocations per frame.
27
+ scratchTint = {
28
+ r: 1, g: 1, b: 1, a: 1,
29
+ };
30
+ update(world, _dt) {
31
+ const transforms = world.getPool(POOL_TRANSFORM);
32
+ const sprites = world.getPool(POOL_SPRITE);
33
+ const device = world.resources.get(RESOURCE_DEVICE);
34
+ const camera = world.resources.get(RESOURCE_CAMERA);
35
+ if (!transforms || !sprites || !device || !camera)
36
+ return;
37
+ device.setCamera(camera);
38
+ const hwm = Math.min(transforms.getHighWaterMark(), sprites.getHighWaterMark());
39
+ // Build the sort list - one entry per active sprite with a
40
+ // visible transform. Reuse the buffer; grow only when needed.
41
+ let count = 0;
42
+ while (this.sortBuffer.length < hwm) {
43
+ this.sortBuffer.push({ index: 0, depth: 0 });
44
+ }
45
+ for (let i = 1; i < hwm; i++) { // index 0 is NULL_ENTITY, skip
46
+ const tFlags = transforms.flags[i] ?? 0;
47
+ const sFlags = sprites.flags[i] ?? 0;
48
+ if ((tFlags & TRANSFORM_FLAG_VISIBLE) === 0)
49
+ continue;
50
+ if ((sFlags & SPRITE_FLAG_ACTIVE) === 0)
51
+ continue;
52
+ const x = transforms.x[i] ?? 0;
53
+ const y = transforms.y[i] ?? 0;
54
+ const z = transforms.z[i] ?? 0;
55
+ const entry = this.sortBuffer[count];
56
+ if (!entry)
57
+ continue;
58
+ entry.index = i;
59
+ // Iso depth key: (x + y) * 1000 + z. Larger = drawn later.
60
+ entry.depth = (x + y) * 1000 + z;
61
+ count++;
62
+ }
63
+ // Insertion sort. For small N (typical Phase 2 demo: dozens of
64
+ // entities), this beats Array.prototype.sort's overhead. When N
65
+ // grows, we'll swap in a radix sort.
66
+ for (let i = 1; i < count; i++) {
67
+ const key = this.sortBuffer[i];
68
+ if (!key)
69
+ continue;
70
+ let j = i - 1;
71
+ while (j >= 0) {
72
+ const prev = this.sortBuffer[j];
73
+ if (!prev || prev.depth <= key.depth)
74
+ break;
75
+ this.sortBuffer[j + 1] = prev;
76
+ j--;
77
+ }
78
+ this.sortBuffer[j + 1] = key;
79
+ }
80
+ // Submit in sort order.
81
+ for (let k = 0; k < count; k++) {
82
+ const entry = this.sortBuffer[k];
83
+ if (!entry)
84
+ continue;
85
+ const i = entry.index;
86
+ const atlas = sprites.atlas[i] ?? -1;
87
+ if (atlas < 0)
88
+ continue;
89
+ const frame = sprites.frame[i] ?? 0;
90
+ const x = transforms.x[i] ?? 0;
91
+ const y = transforms.y[i] ?? 0;
92
+ const z = transforms.z[i] ?? 0;
93
+ const sFlags = sprites.flags[i] ?? 0;
94
+ if ((sFlags & SPRITE_FLAG_TINTED) !== 0) {
95
+ const tint = this.scratchTint;
96
+ tint.r = sprites.tintR[i] ?? 1;
97
+ tint.g = sprites.tintG[i] ?? 1;
98
+ tint.b = sprites.tintB[i] ?? 1;
99
+ tint.a = sprites.tintA[i] ?? 1;
100
+ device.drawSprite(x, y, z, atlas, frame, tint);
101
+ }
102
+ else {
103
+ device.drawSprite(x, y, z, atlas, frame);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=sprite-render-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite-render-system.js","sourceRoot":"","sources":["../../src/systems/sprite-render-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 VeilBudgetSystem implements System {
4
+ readonly name: string;
5
+ update(world: World, _dt: number): void;
6
+ }
7
+ //# sourceMappingURL=veil-budget-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"veil-budget-system.d.ts","sourceRoot":"","sources":["../../src/systems/veil-budget-system.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AASzC,qBAAa,gBAAiB,YAAW,MAAM;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAiB;IAEtC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAYxC"}
@@ -0,0 +1,37 @@
1
+ // VeilBudgetSystem - reads the VeilBudgetResource each tick and
2
+ // propagates its values into the engine subsystems that gate on it.
3
+ //
4
+ // Phase 5 wiring:
5
+ // audioBudget -> AudioBus.setAudioBudget(budget)
6
+ // particleBudget -> ParticlePool.setMaxParticles(budget)
7
+ //
8
+ // This is the cheap-but-explicit propagation step. Subsystems could
9
+ // subscribe to budget changes directly, but a polling system is
10
+ // simpler and runs in O(1) since the bus / pool ignore set-same.
11
+ //
12
+ // Runs in PHASE_INPUT immediately after InputSystem, so by the time
13
+ // gameplay logic / particle emit / audio play* calls happen later in
14
+ // the tick, gates are current.
15
+ //
16
+ // The Director-bridge in Phase 6 mutates budget values directly on
17
+ // the resource; this system is what makes those mutations visible to
18
+ // audio + VFX.
19
+ import { RESOURCE_VEIL_BUDGET, } from '../resources.js';
20
+ import { RESOURCE_AUDIO_BUS } from '../audio/audio-bus.js';
21
+ import { POOL_PARTICLE } from './particle-simulation-system.js';
22
+ export class VeilBudgetSystem {
23
+ name = 'veil-budget';
24
+ update(world, _dt) {
25
+ const budget = world.resources.get(RESOURCE_VEIL_BUDGET);
26
+ if (!budget)
27
+ return;
28
+ const audio = world.resources.get(RESOURCE_AUDIO_BUS);
29
+ if (audio)
30
+ audio.setAudioBudget(budget.audioBudget);
31
+ const particles = world.getPool(POOL_PARTICLE);
32
+ if (particles && particles.getMaxParticles() !== budget.particleBudget) {
33
+ particles.setMaxParticles(budget.particleBudget);
34
+ }
35
+ }
36
+ }
37
+ //# sourceMappingURL=veil-budget-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"veil-budget-system.js","sourceRoot":"","sources":["../../src/systems/veil-budget-system.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,oEAAoE;AACpE,EAAE;AACF,kBAAkB;AAClB,sDAAsD;AACtD,2DAA2D;AAC3D,EAAE;AACF,oEAAoE;AACpE,gEAAgE;AAChE,iEAAiE;AACjE,EAAE;AACF,oEAAoE;AACpE,qEAAqE;AACrE,+BAA+B;AAC/B,EAAE;AACF,mEAAmE;AACnE,qEAAqE;AACrE,eAAe;AAIf,OAAO,EACL,oBAAoB,GAErB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAY,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAW,aAAa,CAAC;IAEtC,MAAM,CAAC,KAAY,EAAE,GAAW;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAqB,oBAAoB,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAW,kBAAkB,CAAC,CAAC;QAChE,IAAI,KAAK;YAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAEpD,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAe,aAAa,CAAC,CAAC;QAC7D,IAAI,SAAS,IAAI,SAAS,CAAC,eAAe,EAAE,KAAK,MAAM,CAAC,cAAc,EAAE,CAAC;YACvE,SAAS,CAAC,eAAe,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ export interface ColorRGBA {
2
+ r: number;
3
+ g: number;
4
+ b: number;
5
+ a: number;
6
+ }
7
+ export declare function rgba(r: number, g: number, b: number, a: number): ColorRGBA;
8
+ export declare const COLOR_WHITE: Readonly<ColorRGBA>;
9
+ export declare const COLOR_BLACK: Readonly<ColorRGBA>;
10
+ export declare const COLOR_TRANSPARENT: Readonly<ColorRGBA>;
11
+ export declare const COLOR_KNOT_STR: Readonly<ColorRGBA>;
12
+ export declare const COLOR_KNOT_DEX: Readonly<ColorRGBA>;
13
+ export declare const COLOR_KNOT_INT: Readonly<ColorRGBA>;
14
+ export declare const COLOR_KNOT_CENTER: Readonly<ColorRGBA>;
15
+ export declare function hexToRgba(hex: number, alpha?: number): ColorRGBA;
16
+ export declare function rgbaToHexString(c: ColorRGBA): string;
17
+ export declare function rgbaToCssString(c: ColorRGBA): string;
18
+ export declare function colorLerp(a: ColorRGBA, b: ColorRGBA, t: number, out: ColorRGBA): ColorRGBA;
19
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/util/color.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAE1E;AAED,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,SAAS,CAA6C,CAAC;AAC1F,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,SAAS,CAA6C,CAAC;AAC1F,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,SAAS,CAA6C,CAAC;AAGhG,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,SAAS,CAAsC,CAAC;AACtF,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,SAAS,CAAsC,CAAC;AACtF,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,SAAS,CAAsC,CAAC;AACtF,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,SAAS,CAAsC,CAAC;AAEzF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,SAAS,CAOnE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAKpD;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAKpD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,SAAS,CAM1F"}
@@ -0,0 +1,45 @@
1
+ // Color types and helpers for the Loom Engine.
2
+ //
3
+ // Two representations:
4
+ // - ColorRGBA: 4 floats in [0,1]. Used in tints, gradients, blending.
5
+ // - Hex24: a packed 0xRRGGBB integer. Used in atlas tints and palette
6
+ // storage where memory matters.
7
+ export function rgba(r, g, b, a) {
8
+ return { r, g, b, a };
9
+ }
10
+ export const COLOR_WHITE = Object.freeze({ r: 1, g: 1, b: 1, a: 1 });
11
+ export const COLOR_BLACK = Object.freeze({ r: 0, g: 0, b: 0, a: 1 });
12
+ export const COLOR_TRANSPARENT = Object.freeze({ r: 0, g: 0, b: 0, a: 0 });
13
+ // Knot palette per LOOM-CLASS-SYSTEM-SPEC Section 4.
14
+ export const COLOR_KNOT_STR = Object.freeze(hexToRgba(0xb04a24));
15
+ export const COLOR_KNOT_DEX = Object.freeze(hexToRgba(0x5ac9d6));
16
+ export const COLOR_KNOT_INT = Object.freeze(hexToRgba(0x9b5de5));
17
+ export const COLOR_KNOT_CENTER = Object.freeze(hexToRgba(0xffd86a));
18
+ export function hexToRgba(hex, alpha = 1) {
19
+ return {
20
+ r: ((hex >> 16) & 0xff) / 255,
21
+ g: ((hex >> 8) & 0xff) / 255,
22
+ b: (hex & 0xff) / 255,
23
+ a: alpha,
24
+ };
25
+ }
26
+ export function rgbaToHexString(c) {
27
+ const r = Math.round(c.r * 255) & 0xff;
28
+ const g = Math.round(c.g * 255) & 0xff;
29
+ const b = Math.round(c.b * 255) & 0xff;
30
+ return '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0');
31
+ }
32
+ export function rgbaToCssString(c) {
33
+ const r = Math.round(c.r * 255) & 0xff;
34
+ const g = Math.round(c.g * 255) & 0xff;
35
+ const b = Math.round(c.b * 255) & 0xff;
36
+ return 'rgba(' + r + ',' + g + ',' + b + ',' + c.a.toFixed(3) + ')';
37
+ }
38
+ export function colorLerp(a, b, t, out) {
39
+ out.r = a.r + (b.r - a.r) * t;
40
+ out.g = a.g + (b.g - a.g) * t;
41
+ out.b = a.b + (b.b - a.b) * t;
42
+ out.a = a.a + (b.a - a.a) * t;
43
+ return out;
44
+ }
45
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/util/color.ts"],"names":[],"mappings":"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"}