@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,313 @@
1
+ // Loom Engine - sprite-sheet loader.
2
+ //
3
+ // Loads a PNG + JSON manifest pair from a URL and returns the
4
+ // {image, frames} shape that IGraphicsDevice.registerAtlas accepts
5
+ // directly. The engine never decides where assets come from; the
6
+ // caller passes a manifest URL and the loader fetches both halves.
7
+ //
8
+ // Manifest schema (see assets/knight/walk.json for an example):
9
+ // {
10
+ // "name": "knight-walk",
11
+ // "image": "walk.png", // sibling-relative URL of the PNG
12
+ // "frames": [
13
+ // { "x":0, "y":0, "w":16, "h":32, "name":"walk_pass_a", "duration_ms":140 },
14
+ // ...
15
+ // ],
16
+ // "anchor": { "x": 8, "y": 32 },
17
+ // "fps": 8
18
+ // }
19
+ //
20
+ // PRIOR-ART.md cites the Aseprite "JSON-Array" export and the
21
+ // TexturePacker "JSON-array" format as inspiration for the shape.
22
+ // We do not parse either format directly; we use a small superset
23
+ // (per-frame name + duration_ms + sheet anchor + fps) tailored to
24
+ // the engine's animation needs.
25
+ // Errors thrown by the loader. All carry a `kind` so callers can
26
+ // branch on the failure mode without parsing message strings.
27
+ export class SpriteSheetLoadError extends Error {
28
+ kind;
29
+ url;
30
+ constructor(kind, url, message, options) {
31
+ super(`SpriteSheetLoadError[${kind}] ${url}: ${message}`, options);
32
+ this.name = 'SpriteSheetLoadError';
33
+ this.kind = kind;
34
+ this.url = url;
35
+ }
36
+ }
37
+ // Default browser-side image decoder. Creates an <img>, sets src to
38
+ // a blob: URL, and resolves on load. Throws SpriteSheetLoadError
39
+ // with kind='decode-image' on failure.
40
+ function defaultDecodeImage(bytes, url) {
41
+ return new Promise((resolve, reject) => {
42
+ const blob = new Blob([bytes], { type: 'image/png' });
43
+ const objectUrl = URL.createObjectURL(blob);
44
+ const img = new Image();
45
+ img.onload = () => {
46
+ URL.revokeObjectURL(objectUrl);
47
+ resolve(img);
48
+ };
49
+ img.onerror = (err) => {
50
+ URL.revokeObjectURL(objectUrl);
51
+ reject(new SpriteSheetLoadError('decode-image', url, 'Image element failed to decode the PNG bytes', { cause: err }));
52
+ };
53
+ img.src = objectUrl;
54
+ });
55
+ }
56
+ // Resolve a sibling-relative image path against the manifest URL.
57
+ // "walk.png" alongside ".../knight/walk.json" -> ".../knight/walk.png".
58
+ // Absolute URLs and "/abs/path" pass through unchanged.
59
+ function resolveImageUrl(manifestUrl, imagePath) {
60
+ // URL constructor handles both absolute and relative inputs as long
61
+ // as we have a base. In Node (no DOM, no document) the manifestUrl
62
+ // must be absolute (file://, http(s)://); in the browser the
63
+ // Document base does the work.
64
+ try {
65
+ return new URL(imagePath, manifestUrl).toString();
66
+ }
67
+ catch {
68
+ // Fallback: simple sibling-replace. Strip the manifest filename
69
+ // and append the image filename. This handles bare relative paths
70
+ // when no URL constructor base is reachable.
71
+ const slash = manifestUrl.lastIndexOf('/');
72
+ if (slash < 0)
73
+ return imagePath;
74
+ return manifestUrl.slice(0, slash + 1) + imagePath;
75
+ }
76
+ }
77
+ // Validate a parsed JSON object against the manifest schema. Returns
78
+ // the typed manifest or throws SpriteSheetLoadError. We do not pull
79
+ // in a schema library; the engine ships zero runtime deps.
80
+ function validateManifest(raw, url) {
81
+ if (!raw || typeof raw !== 'object') {
82
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'manifest is not an object');
83
+ }
84
+ const m = raw;
85
+ if (typeof m['name'] !== 'string' || m['name'].length === 0) {
86
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'name must be a non-empty string');
87
+ }
88
+ if (typeof m['image'] !== 'string' || m['image'].length === 0) {
89
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'image must be a non-empty string');
90
+ }
91
+ if (!Array.isArray(m['frames']) || m['frames'].length === 0) {
92
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'frames must be a non-empty array');
93
+ }
94
+ const frames = [];
95
+ for (let i = 0; i < m['frames'].length; i++) {
96
+ const fRaw = m['frames'][i];
97
+ if (!fRaw || typeof fRaw !== 'object') {
98
+ throw new SpriteSheetLoadError('invalid-manifest', url, `frame[${i}] is not an object`);
99
+ }
100
+ const f = fRaw;
101
+ const x = f['x'];
102
+ const y = f['y'];
103
+ const w = f['w'];
104
+ const h = f['h'];
105
+ if (typeof x !== 'number' ||
106
+ typeof y !== 'number' ||
107
+ typeof w !== 'number' ||
108
+ typeof h !== 'number') {
109
+ throw new SpriteSheetLoadError('invalid-manifest', url, `frame[${i}] must have numeric x, y, w, h`);
110
+ }
111
+ if (w <= 0 || h <= 0) {
112
+ throw new SpriteSheetLoadError('invalid-manifest', url, `frame[${i}] w and h must be positive`);
113
+ }
114
+ const frame = { x, y, w, h };
115
+ if (typeof f['name'] === 'string')
116
+ frame.name = f['name'];
117
+ if (typeof f['duration_ms'] === 'number')
118
+ frame.duration_ms = f['duration_ms'];
119
+ frames.push(frame);
120
+ }
121
+ // Anchor defaults to bottom-center of the first frame if not given.
122
+ let anchor;
123
+ const aRaw = m['anchor'];
124
+ if (aRaw && typeof aRaw === 'object') {
125
+ const a = aRaw;
126
+ if (typeof a['x'] !== 'number' || typeof a['y'] !== 'number') {
127
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'anchor.x and anchor.y must be numeric when anchor is present');
128
+ }
129
+ anchor = { x: a['x'], y: a['y'] };
130
+ }
131
+ else {
132
+ const f0 = frames[0];
133
+ anchor = { x: f0.w / 2, y: f0.h };
134
+ }
135
+ // fps optional, defaults to 8 (matches the v1 walk-cycle cadence).
136
+ let fps = 8;
137
+ if (m['fps'] !== undefined) {
138
+ if (typeof m['fps'] !== 'number' || m['fps'] <= 0) {
139
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'fps must be a positive number');
140
+ }
141
+ fps = m['fps'];
142
+ }
143
+ // Clips optional. When absent, synthesize a 'default' clip that
144
+ // walks all frames in order, looping. When present, validate that
145
+ // each clip has a non-empty frames[] of integer indices in range
146
+ // and a boolean loop flag.
147
+ let clips;
148
+ if (m['clips'] === undefined) {
149
+ const defaultFrames = [];
150
+ for (let i = 0; i < frames.length; i++)
151
+ defaultFrames.push(i);
152
+ clips = [{ name: 'default', frames: defaultFrames, loop: true }];
153
+ }
154
+ else {
155
+ if (!Array.isArray(m['clips'])) {
156
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'clips must be an array when present');
157
+ }
158
+ clips = [];
159
+ for (let ci = 0; ci < m['clips'].length; ci++) {
160
+ const cRaw = m['clips'][ci];
161
+ if (!cRaw || typeof cRaw !== 'object') {
162
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}] is not an object`);
163
+ }
164
+ const c = cRaw;
165
+ if (typeof c['name'] !== 'string' || c['name'].length === 0) {
166
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].name must be a non-empty string`);
167
+ }
168
+ if (!Array.isArray(c['frames']) || c['frames'].length === 0) {
169
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].frames must be a non-empty array`);
170
+ }
171
+ const clipFrames = [];
172
+ for (let fi = 0; fi < c['frames'].length; fi++) {
173
+ const v = c['frames'][fi];
174
+ if (typeof v !== 'number' || !Number.isInteger(v) || v < 0 || v >= frames.length) {
175
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].frames[${fi}] must be an integer in [0, ${frames.length})`);
176
+ }
177
+ clipFrames.push(v);
178
+ }
179
+ if (typeof c['loop'] !== 'boolean') {
180
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].loop must be boolean`);
181
+ }
182
+ const clip = { name: c['name'], frames: clipFrames, loop: c['loop'] };
183
+ if (c['fps'] !== undefined) {
184
+ if (typeof c['fps'] !== 'number' || c['fps'] <= 0) {
185
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].fps must be a positive number when present`);
186
+ }
187
+ clip.fps = c['fps'];
188
+ }
189
+ if (c['durations_ms'] !== undefined) {
190
+ if (!Array.isArray(c['durations_ms']) || c['durations_ms'].length !== clipFrames.length) {
191
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].durations_ms must be an array of length ${clipFrames.length} when present`);
192
+ }
193
+ const durs = [];
194
+ for (let di = 0; di < c['durations_ms'].length; di++) {
195
+ const d = c['durations_ms'][di];
196
+ if (typeof d !== 'number' || d <= 0) {
197
+ throw new SpriteSheetLoadError('invalid-manifest', url, `clips[${ci}].durations_ms[${di}] must be a positive number`);
198
+ }
199
+ durs.push(d);
200
+ }
201
+ clip.durations_ms = durs;
202
+ }
203
+ clips.push(clip);
204
+ }
205
+ if (clips.length === 0) {
206
+ throw new SpriteSheetLoadError('invalid-manifest', url, 'clips must be non-empty when present');
207
+ }
208
+ }
209
+ return {
210
+ name: m['name'],
211
+ image: m['image'],
212
+ frames,
213
+ anchor,
214
+ fps,
215
+ clips,
216
+ };
217
+ }
218
+ // Load a sprite sheet from a manifest URL. Fetches the JSON, then
219
+ // the PNG referenced by the manifest's "image" field (resolved
220
+ // sibling-relative to the manifest URL), then decodes the image and
221
+ // returns a LoadedSpriteSheet.
222
+ //
223
+ // The returned `atlas` is shaped exactly for IGraphicsDevice.registerAtlas:
224
+ // const sheet = await loadSpriteSheet('/assets/knight/walk.json');
225
+ // const handle = device.registerAtlas(sheet.atlas);
226
+ export async function loadSpriteSheet(manifestUrl, options = {}) {
227
+ const fetchImpl = options.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);
228
+ if (!fetchImpl) {
229
+ throw new SpriteSheetLoadError('fetch-manifest', manifestUrl, 'no fetch implementation available; pass options.fetchImpl');
230
+ }
231
+ const decodeImage = options.decodeImage ?? defaultDecodeImage;
232
+ // 1. Fetch + parse manifest.
233
+ let manifestResp;
234
+ try {
235
+ manifestResp = await fetchImpl(manifestUrl);
236
+ }
237
+ catch (err) {
238
+ throw new SpriteSheetLoadError('fetch-manifest', manifestUrl, 'network error', { cause: err });
239
+ }
240
+ if (!manifestResp.ok) {
241
+ throw new SpriteSheetLoadError('fetch-manifest', manifestUrl, `HTTP ${manifestResp.status} ${manifestResp.statusText}`);
242
+ }
243
+ let raw;
244
+ try {
245
+ raw = await manifestResp.json();
246
+ }
247
+ catch (err) {
248
+ throw new SpriteSheetLoadError('parse-manifest', manifestUrl, 'response is not valid JSON', { cause: err });
249
+ }
250
+ const manifest = validateManifest(raw, manifestUrl);
251
+ // 2. Fetch + decode image.
252
+ const imageUrl = resolveImageUrl(manifestUrl, manifest.image);
253
+ let imageResp;
254
+ try {
255
+ imageResp = await fetchImpl(imageUrl);
256
+ }
257
+ catch (err) {
258
+ throw new SpriteSheetLoadError('fetch-image', imageUrl, 'network error', { cause: err });
259
+ }
260
+ if (!imageResp.ok) {
261
+ throw new SpriteSheetLoadError('fetch-image', imageUrl, `HTTP ${imageResp.status} ${imageResp.statusText}`);
262
+ }
263
+ const bytes = await imageResp.arrayBuffer();
264
+ const image = await decodeImage(bytes, imageUrl);
265
+ // 3. Compose AtlasDescriptor (only fields the device reads).
266
+ const atlas = {
267
+ image,
268
+ frames: manifest.frames.map((f) => ({ x: f.x, y: f.y, w: f.w, h: f.h })),
269
+ name: manifest.name,
270
+ };
271
+ return { manifest, image, atlas };
272
+ }
273
+ // Compute the active frame index for a time-driven walk cycle. Use
274
+ // per-frame duration_ms if every frame has it, otherwise fall back
275
+ // to manifest.fps. Returns an integer in [0, frames.length).
276
+ //
277
+ // `now` is a monotonic millisecond clock (typically performance.now()
278
+ // in the browser or process.hrtime() bigint -> ms in Node). `start`
279
+ // is the t0 the caller stored when the animation began.
280
+ export function computeFrameIndex(manifest, now, start) {
281
+ const n = manifest.frames.length;
282
+ if (n <= 1)
283
+ return 0;
284
+ // Sum per-frame durations if available. Mixed manifests (some
285
+ // frames with duration_ms, some without) fall back to fps.
286
+ let totalDuration = 0;
287
+ let allHaveDuration = true;
288
+ for (let i = 0; i < n; i++) {
289
+ const d = manifest.frames[i]?.duration_ms;
290
+ if (typeof d === 'number' && d > 0) {
291
+ totalDuration += d;
292
+ }
293
+ else {
294
+ allHaveDuration = false;
295
+ break;
296
+ }
297
+ }
298
+ const elapsed = Math.max(0, now - start);
299
+ if (allHaveDuration) {
300
+ const t = elapsed % totalDuration;
301
+ let acc = 0;
302
+ for (let i = 0; i < n; i++) {
303
+ acc += manifest.frames[i].duration_ms;
304
+ if (t < acc)
305
+ return i;
306
+ }
307
+ return n - 1; // safety
308
+ }
309
+ // Uniform-fps fallback.
310
+ const frameMs = 1000 / manifest.fps;
311
+ return Math.floor(elapsed / frameMs) % n;
312
+ }
313
+ //# sourceMappingURL=sprite-sheet-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite-sheet-loader.js","sourceRoot":"","sources":["../../src/asset/sprite-sheet-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ export type BusPriority = 'essential' | 'ambient';
2
+ export interface BusOptions {
3
+ initialGain?: number;
4
+ priority?: BusPriority;
5
+ }
6
+ export declare const AUDIO_BUDGET_AMBIENT_FLOOR = 0.25;
7
+ export declare const AUDIO_BUDGET_ESSENTIAL_FLOOR = 0.05;
8
+ export declare class AudioBus {
9
+ readonly ctx: AudioContext;
10
+ private master;
11
+ private buses;
12
+ private masterGain;
13
+ private currentBudget;
14
+ private suspended;
15
+ private constructor();
16
+ static create(ctx?: AudioContext): AudioBus;
17
+ unlock(): Promise<void>;
18
+ isUnlocked(): boolean;
19
+ input(name: string): AudioNode;
20
+ hasBus(name: string): boolean;
21
+ addBus(name: string, opts?: BusOptions): void;
22
+ removeBus(name: string): void;
23
+ setMasterGain(gain: number): void;
24
+ getMasterGain(): number;
25
+ setBusGain(name: string, gain: number): void;
26
+ getBusGain(name: string): number;
27
+ setBusMuted(name: string, muted: boolean): void;
28
+ isBusMuted(name: string): boolean;
29
+ setAudioBudget(budget: number): void;
30
+ getAudioBudget(): number;
31
+ private applyBudgetToBus;
32
+ playOneShot(busName: string, buffer: AudioBuffer, options?: {
33
+ rate?: number;
34
+ gain?: number;
35
+ }): AudioBufferSourceNode | null;
36
+ playTone(busName: string, freq: number, durationMs: number, options?: {
37
+ gain?: number;
38
+ type?: OscillatorType;
39
+ }): void;
40
+ dispose(): void;
41
+ }
42
+ export declare const RESOURCE_AUDIO_BUS = "audio_bus";
43
+ //# sourceMappingURL=audio-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-bus.d.ts","sourceRoot":"","sources":["../../src/audio/audio-bus.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAqBD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,qBAAa,QAAQ;IACnB,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,SAAS,CAAiB;IAElC,OAAO;IAcP,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,YAAY,GAAG,QAAQ;IAarC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAe7B,UAAU,IAAI,OAAO;IAOrB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAQ9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI7B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,IAAI;IAejD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO7B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKjC,aAAa,IAAI,MAAM;IAIvB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAO5C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIhC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAO/C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAOjC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAUpC,cAAc,IAAI,MAAM;IAIxB,OAAO,CAAC,gBAAgB;IAmBxB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,qBAAqB,GAAG,IAAI;IAoB/H,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,cAAc,CAAA;KAAO,GAAG,IAAI;IAuBzH,OAAO,IAAI,IAAI;CAOhB;AAID,eAAO,MAAM,kBAAkB,cAAc,CAAC"}
@@ -0,0 +1,258 @@
1
+ // AudioBus - simple Web Audio mixer for the Loom Engine.
2
+ //
3
+ // Architecture:
4
+ // master (GainNode, output -> destination)
5
+ // |- 'sfx' sub-bus
6
+ // |- 'music' sub-bus
7
+ // |- 'voice' sub-bus
8
+ // |- 'ui' sub-bus
9
+ //
10
+ // Sub-buses are GainNodes that route into master. Sound-source code
11
+ // connects an AudioNode to bus.input(name) and the bus handles routing
12
+ // + per-bus gain + per-bus mute. New named buses can be created via
13
+ // addBus(name, opts).
14
+ //
15
+ // Lazy unlock: browsers block AudioContext until the first user
16
+ // gesture. AudioBus.create() does NOT auto-resume the context;
17
+ // callers must call unlock() inside a click / touchstart / keydown
18
+ // handler or use a UI prompt. Calling play* before unlock returns
19
+ // silently with no error - production builds want a single warning
20
+ // in the console; v1 keeps it quiet to avoid spam.
21
+ //
22
+ // VE-budget gating per LOOM-ENGINE-SPEC.md Section 3 / Phase 5: the
23
+ // caller passes the VeilBudget audioBudget into setMasterAudioBudget
24
+ // each tick (or on Director updates). When the budget drops below
25
+ // the priority floor for a bus, that bus mutes. Default tier order:
26
+ // essential (always plays): sfx, voice
27
+ // ambient (mutes under load): music, ui
28
+ //
29
+ // Inspirations (per PRIOR-ART.md):
30
+ // Web Audio API W3C spec - public technique, no patent IP
31
+ // Gain bus pattern - canonical (FMOD, Wwise, Web Audio docs)
32
+ // Priority-tiered ducking - common in game audio engines, not
33
+ // patented as a pattern; Loom-specific is the VE-budget driver
34
+ const DEFAULT_BUSES = [
35
+ { name: 'sfx', opts: { initialGain: 1.0, priority: 'essential' } },
36
+ { name: 'music', opts: { initialGain: 0.6, priority: 'ambient' } },
37
+ { name: 'voice', opts: { initialGain: 1.0, priority: 'essential' } },
38
+ { name: 'ui', opts: { initialGain: 0.8, priority: 'ambient' } },
39
+ ];
40
+ // Budget thresholds. When VeilBudget.audioBudget drops below the
41
+ // 'ambient' threshold, ambient buses mute. When it drops below
42
+ // 'essential', everything mutes (engine still runs in silence).
43
+ // These are arbitrary scalars; the Director's budget value should be
44
+ // in [0, 1] where 1 is "all good".
45
+ export const AUDIO_BUDGET_AMBIENT_FLOOR = 0.25;
46
+ export const AUDIO_BUDGET_ESSENTIAL_FLOOR = 0.05;
47
+ export class AudioBus {
48
+ ctx;
49
+ master;
50
+ buses = new Map();
51
+ masterGain = 1;
52
+ currentBudget = 1.0;
53
+ suspended = true;
54
+ constructor(ctx) {
55
+ this.ctx = ctx;
56
+ this.master = ctx.createGain();
57
+ this.master.gain.value = this.masterGain;
58
+ this.master.connect(ctx.destination);
59
+ for (const def of DEFAULT_BUSES) {
60
+ this.addBus(def.name, def.opts);
61
+ }
62
+ }
63
+ // Construct from an existing AudioContext (tests pass mocks) or
64
+ // let the bus create one. Browser autoplay policy means the new
65
+ // context starts suspended.
66
+ static create(ctx) {
67
+ if (ctx) {
68
+ return new AudioBus(ctx);
69
+ }
70
+ if (typeof AudioContext === 'undefined') {
71
+ throw new Error('AudioBus.create: AudioContext is unavailable in this environment');
72
+ }
73
+ return new AudioBus(new AudioContext());
74
+ }
75
+ // Resume the AudioContext after a user gesture. Returns a promise
76
+ // that settles when the context is running. Calling more than once
77
+ // is safe.
78
+ async unlock() {
79
+ if (this.ctx.state === 'running') {
80
+ this.suspended = false;
81
+ return;
82
+ }
83
+ try {
84
+ await this.ctx.resume();
85
+ this.suspended = this.ctx.state !== 'running';
86
+ }
87
+ catch {
88
+ // Already-suspended contexts that fail resume are usually a
89
+ // policy issue; nothing the engine can do beyond surface it.
90
+ this.suspended = true;
91
+ }
92
+ }
93
+ isUnlocked() {
94
+ return !this.suspended && this.ctx.state === 'running';
95
+ }
96
+ // Public: a node a sound source connects to so the bus routes its
97
+ // output through master. Returns the bus's input GainNode; users
98
+ // call audioSource.connect(bus.input('sfx')).
99
+ input(name) {
100
+ const entry = this.buses.get(name);
101
+ if (!entry) {
102
+ throw new Error('AudioBus.input: unknown bus "' + name + '"');
103
+ }
104
+ return entry.node;
105
+ }
106
+ hasBus(name) {
107
+ return this.buses.has(name);
108
+ }
109
+ addBus(name, opts = {}) {
110
+ if (this.buses.has(name))
111
+ return; // idempotent
112
+ const node = this.ctx.createGain();
113
+ const initial = opts.initialGain ?? 1.0;
114
+ node.gain.value = initial;
115
+ node.connect(this.master);
116
+ this.buses.set(name, {
117
+ node,
118
+ baseGain: initial,
119
+ muted: false,
120
+ priority: opts.priority ?? 'ambient',
121
+ });
122
+ this.applyBudgetToBus(name);
123
+ }
124
+ removeBus(name) {
125
+ const entry = this.buses.get(name);
126
+ if (!entry)
127
+ return;
128
+ entry.node.disconnect();
129
+ this.buses.delete(name);
130
+ }
131
+ setMasterGain(gain) {
132
+ this.masterGain = Math.max(0, gain);
133
+ this.master.gain.value = this.masterGain;
134
+ }
135
+ getMasterGain() {
136
+ return this.masterGain;
137
+ }
138
+ setBusGain(name, gain) {
139
+ const entry = this.buses.get(name);
140
+ if (!entry)
141
+ return;
142
+ entry.baseGain = Math.max(0, gain);
143
+ this.applyBudgetToBus(name);
144
+ }
145
+ getBusGain(name) {
146
+ return this.buses.get(name)?.baseGain ?? 0;
147
+ }
148
+ setBusMuted(name, muted) {
149
+ const entry = this.buses.get(name);
150
+ if (!entry)
151
+ return;
152
+ entry.muted = muted;
153
+ this.applyBudgetToBus(name);
154
+ }
155
+ isBusMuted(name) {
156
+ return this.buses.get(name)?.muted ?? false;
157
+ }
158
+ // Apply the latest VeilBudget audioBudget. Caller pushes this from
159
+ // the resource each tick (or on Director updates). Idempotent;
160
+ // re-apply with the same value is cheap.
161
+ setAudioBudget(budget) {
162
+ if (Number.isNaN(budget) || budget < 0)
163
+ budget = 0;
164
+ if (budget > 1)
165
+ budget = 1;
166
+ if (budget === this.currentBudget)
167
+ return;
168
+ this.currentBudget = budget;
169
+ for (const name of this.buses.keys()) {
170
+ this.applyBudgetToBus(name);
171
+ }
172
+ }
173
+ getAudioBudget() {
174
+ return this.currentBudget;
175
+ }
176
+ applyBudgetToBus(name) {
177
+ const entry = this.buses.get(name);
178
+ if (!entry)
179
+ return;
180
+ let effective = entry.baseGain;
181
+ if (entry.muted)
182
+ effective = 0;
183
+ if (this.currentBudget < AUDIO_BUDGET_ESSENTIAL_FLOOR) {
184
+ effective = 0;
185
+ }
186
+ else if (entry.priority === 'ambient' &&
187
+ this.currentBudget < AUDIO_BUDGET_AMBIENT_FLOOR) {
188
+ effective = 0;
189
+ }
190
+ entry.node.gain.value = effective;
191
+ }
192
+ // Convenience: play a one-shot AudioBuffer through a named bus.
193
+ // Returns the AudioBufferSourceNode so callers can stop / track it.
194
+ // Returns null if the bus or context isn't ready.
195
+ playOneShot(busName, buffer, options = {}) {
196
+ const entry = this.buses.get(busName);
197
+ if (!entry)
198
+ return null;
199
+ if (!this.isUnlocked())
200
+ return null;
201
+ const src = this.ctx.createBufferSource();
202
+ src.buffer = buffer;
203
+ src.playbackRate.value = options.rate ?? 1;
204
+ if (options.gain !== undefined) {
205
+ const g = this.ctx.createGain();
206
+ g.gain.value = options.gain;
207
+ src.connect(g).connect(entry.node);
208
+ }
209
+ else {
210
+ src.connect(entry.node);
211
+ }
212
+ src.start();
213
+ return src;
214
+ }
215
+ // Convenience: short tone via OscillatorNode. Useful for code-only
216
+ // demos and UI feedback when no sound assets are loaded yet.
217
+ playTone(busName, freq, durationMs, options = {}) {
218
+ const entry = this.buses.get(busName);
219
+ if (!entry)
220
+ return;
221
+ if (!this.isUnlocked())
222
+ return;
223
+ const osc = this.ctx.createOscillator();
224
+ const gain = this.ctx.createGain();
225
+ osc.type = options.type ?? 'sine';
226
+ osc.frequency.value = freq;
227
+ const peakGain = options.gain ?? 0.2;
228
+ // Tiny attack / release envelope so the tone doesn't click.
229
+ const now = this.ctx.currentTime;
230
+ const dur = Math.max(0.01, durationMs / 1000);
231
+ gain.gain.setValueAtTime(0, now);
232
+ gain.gain.linearRampToValueAtTime(peakGain, now + 0.005);
233
+ gain.gain.linearRampToValueAtTime(peakGain, now + dur - 0.02);
234
+ gain.gain.linearRampToValueAtTime(0, now + dur);
235
+ osc.connect(gain).connect(entry.node);
236
+ osc.start(now);
237
+ osc.stop(now + dur + 0.05);
238
+ }
239
+ // Tear down. Useful in tests; production demo lives the lifetime of
240
+ // the page.
241
+ dispose() {
242
+ for (const entry of this.buses.values()) {
243
+ try {
244
+ entry.node.disconnect();
245
+ }
246
+ catch { /* ignore */ }
247
+ }
248
+ this.buses.clear();
249
+ try {
250
+ this.master.disconnect();
251
+ }
252
+ catch { /* ignore */ }
253
+ }
254
+ }
255
+ // Resource key for the world's resource registry. Engine.create
256
+ // registers an AudioBus instance under this key.
257
+ export const RESOURCE_AUDIO_BUS = 'audio_bus';
258
+ //# sourceMappingURL=audio-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-bus.js","sourceRoot":"","sources":["../../src/audio/audio-bus.ts"],"names":[],"mappings":"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"}