@al8b/runtime 0.1.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 (185) hide show
  1. package/README.md +87 -0
  2. package/dist/assets/constructors.d.mts +6 -0
  3. package/dist/assets/constructors.d.ts +6 -0
  4. package/dist/assets/constructors.js +40 -0
  5. package/dist/assets/constructors.js.map +1 -0
  6. package/dist/assets/constructors.mjs +12 -0
  7. package/dist/assets/constructors.mjs.map +1 -0
  8. package/dist/assets/index.d.mts +11 -0
  9. package/dist/assets/index.d.ts +11 -0
  10. package/dist/assets/index.js +276 -0
  11. package/dist/assets/index.js.map +1 -0
  12. package/dist/assets/index.mjs +247 -0
  13. package/dist/assets/index.mjs.map +1 -0
  14. package/dist/assets/loader.d.mts +83 -0
  15. package/dist/assets/loader.d.ts +83 -0
  16. package/dist/assets/loader.js +260 -0
  17. package/dist/assets/loader.js.map +1 -0
  18. package/dist/assets/loader.mjs +237 -0
  19. package/dist/assets/loader.mjs.map +1 -0
  20. package/dist/browser/index.js +16599 -0
  21. package/dist/browser/index.js.map +1 -0
  22. package/dist/browser/index.min.js +171 -0
  23. package/dist/constants.d.mts +16 -0
  24. package/dist/constants.d.ts +16 -0
  25. package/dist/constants.js +49 -0
  26. package/dist/constants.js.map +1 -0
  27. package/dist/constants.mjs +18 -0
  28. package/dist/constants.mjs.map +1 -0
  29. package/dist/core/api-factory.d.mts +63 -0
  30. package/dist/core/api-factory.d.ts +63 -0
  31. package/dist/core/api-factory.js +239 -0
  32. package/dist/core/api-factory.js.map +1 -0
  33. package/dist/core/api-factory.mjs +214 -0
  34. package/dist/core/api-factory.mjs.map +1 -0
  35. package/dist/core/assets-registry.d.mts +14 -0
  36. package/dist/core/assets-registry.d.ts +14 -0
  37. package/dist/core/assets-registry.js +64 -0
  38. package/dist/core/assets-registry.js.map +1 -0
  39. package/dist/core/assets-registry.mjs +41 -0
  40. package/dist/core/assets-registry.mjs.map +1 -0
  41. package/dist/core/controller.d.mts +109 -0
  42. package/dist/core/controller.d.ts +109 -0
  43. package/dist/core/controller.js +1782 -0
  44. package/dist/core/controller.js.map +1 -0
  45. package/dist/core/controller.mjs +1758 -0
  46. package/dist/core/controller.mjs.map +1 -0
  47. package/dist/core/debug-logger.d.mts +35 -0
  48. package/dist/core/debug-logger.d.ts +35 -0
  49. package/dist/core/debug-logger.js +177 -0
  50. package/dist/core/debug-logger.js.map +1 -0
  51. package/dist/core/debug-logger.mjs +154 -0
  52. package/dist/core/debug-logger.mjs.map +1 -0
  53. package/dist/core/error-handler.d.mts +25 -0
  54. package/dist/core/error-handler.d.ts +25 -0
  55. package/dist/core/error-handler.js +106 -0
  56. package/dist/core/error-handler.js.map +1 -0
  57. package/dist/core/error-handler.mjs +81 -0
  58. package/dist/core/error-handler.mjs.map +1 -0
  59. package/dist/core/index.d.mts +14 -0
  60. package/dist/core/index.d.ts +14 -0
  61. package/dist/core/index.js +1782 -0
  62. package/dist/core/index.js.map +1 -0
  63. package/dist/core/index.mjs +1757 -0
  64. package/dist/core/index.mjs.map +1 -0
  65. package/dist/hot-reload/index.d.mts +7 -0
  66. package/dist/hot-reload/index.d.ts +7 -0
  67. package/dist/hot-reload/index.js +103 -0
  68. package/dist/hot-reload/index.js.map +1 -0
  69. package/dist/hot-reload/index.mjs +78 -0
  70. package/dist/hot-reload/index.mjs.map +1 -0
  71. package/dist/hot-reload/updater.d.mts +33 -0
  72. package/dist/hot-reload/updater.d.ts +33 -0
  73. package/dist/hot-reload/updater.js +101 -0
  74. package/dist/hot-reload/updater.js.map +1 -0
  75. package/dist/hot-reload/updater.mjs +78 -0
  76. package/dist/hot-reload/updater.mjs.map +1 -0
  77. package/dist/index.d.mts +24 -0
  78. package/dist/index.d.ts +24 -0
  79. package/dist/index.js +1859 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/index.mjs +1817 -0
  82. package/dist/index.mjs.map +1 -0
  83. package/dist/input/index.d.mts +2 -0
  84. package/dist/input/index.d.ts +2 -0
  85. package/dist/input/index.js +79 -0
  86. package/dist/input/index.js.map +1 -0
  87. package/dist/input/index.mjs +54 -0
  88. package/dist/input/index.mjs.map +1 -0
  89. package/dist/input/manager.d.mts +37 -0
  90. package/dist/input/manager.d.ts +37 -0
  91. package/dist/input/manager.js +77 -0
  92. package/dist/input/manager.js.map +1 -0
  93. package/dist/input/manager.mjs +54 -0
  94. package/dist/input/manager.mjs.map +1 -0
  95. package/dist/loop/game-loop.d.mts +63 -0
  96. package/dist/loop/game-loop.d.ts +63 -0
  97. package/dist/loop/game-loop.js +156 -0
  98. package/dist/loop/game-loop.js.map +1 -0
  99. package/dist/loop/game-loop.mjs +131 -0
  100. package/dist/loop/game-loop.mjs.map +1 -0
  101. package/dist/loop/index.d.mts +1 -0
  102. package/dist/loop/index.d.ts +1 -0
  103. package/dist/loop/index.js +156 -0
  104. package/dist/loop/index.js.map +1 -0
  105. package/dist/loop/index.mjs +131 -0
  106. package/dist/loop/index.mjs.map +1 -0
  107. package/dist/storage/index.d.mts +1 -0
  108. package/dist/storage/index.d.ts +1 -0
  109. package/dist/storage/index.js +31 -0
  110. package/dist/storage/index.js.map +1 -0
  111. package/dist/storage/index.mjs +6 -0
  112. package/dist/storage/index.mjs.map +1 -0
  113. package/dist/system/api.d.mts +28 -0
  114. package/dist/system/api.d.ts +28 -0
  115. package/dist/system/api.js +126 -0
  116. package/dist/system/api.js.map +1 -0
  117. package/dist/system/api.mjs +101 -0
  118. package/dist/system/api.mjs.map +1 -0
  119. package/dist/system/index.d.mts +2 -0
  120. package/dist/system/index.d.ts +2 -0
  121. package/dist/system/index.js +126 -0
  122. package/dist/system/index.js.map +1 -0
  123. package/dist/system/index.mjs +101 -0
  124. package/dist/system/index.mjs.map +1 -0
  125. package/dist/types/assets.d.mts +43 -0
  126. package/dist/types/assets.d.ts +43 -0
  127. package/dist/types/assets.js +19 -0
  128. package/dist/types/assets.js.map +1 -0
  129. package/dist/types/assets.mjs +1 -0
  130. package/dist/types/assets.mjs.map +1 -0
  131. package/dist/types/bridge.d.mts +66 -0
  132. package/dist/types/bridge.d.ts +66 -0
  133. package/dist/types/bridge.js +19 -0
  134. package/dist/types/bridge.js.map +1 -0
  135. package/dist/types/bridge.mjs +1 -0
  136. package/dist/types/bridge.mjs.map +1 -0
  137. package/dist/types/index.d.mts +6 -0
  138. package/dist/types/index.d.ts +6 -0
  139. package/dist/types/index.js +19 -0
  140. package/dist/types/index.js.map +1 -0
  141. package/dist/types/index.mjs +1 -0
  142. package/dist/types/index.mjs.map +1 -0
  143. package/dist/types/runtime.d.mts +71 -0
  144. package/dist/types/runtime.d.ts +71 -0
  145. package/dist/types/runtime.js +19 -0
  146. package/dist/types/runtime.js.map +1 -0
  147. package/dist/types/runtime.mjs +1 -0
  148. package/dist/types/runtime.mjs.map +1 -0
  149. package/dist/types/vm.d.mts +1 -0
  150. package/dist/types/vm.d.ts +1 -0
  151. package/dist/types/vm.js +19 -0
  152. package/dist/types/vm.js.map +1 -0
  153. package/dist/types/vm.mjs +1 -0
  154. package/dist/types/vm.mjs.map +1 -0
  155. package/dist/utils/deep-clone.d.mts +14 -0
  156. package/dist/utils/deep-clone.d.ts +14 -0
  157. package/dist/utils/deep-clone.js +42 -0
  158. package/dist/utils/deep-clone.js.map +1 -0
  159. package/dist/utils/deep-clone.mjs +19 -0
  160. package/dist/utils/deep-clone.mjs.map +1 -0
  161. package/dist/utils/index.d.mts +3 -0
  162. package/dist/utils/index.d.ts +3 -0
  163. package/dist/utils/index.js +156 -0
  164. package/dist/utils/index.js.map +1 -0
  165. package/dist/utils/index.mjs +129 -0
  166. package/dist/utils/index.mjs.map +1 -0
  167. package/dist/utils/object-pool.d.mts +66 -0
  168. package/dist/utils/object-pool.d.ts +66 -0
  169. package/dist/utils/object-pool.js +113 -0
  170. package/dist/utils/object-pool.js.map +1 -0
  171. package/dist/utils/object-pool.mjs +90 -0
  172. package/dist/utils/object-pool.mjs.map +1 -0
  173. package/dist/utils/shallow-equal.d.mts +15 -0
  174. package/dist/utils/shallow-equal.d.ts +15 -0
  175. package/dist/utils/shallow-equal.js +53 -0
  176. package/dist/utils/shallow-equal.js.map +1 -0
  177. package/dist/utils/shallow-equal.mjs +30 -0
  178. package/dist/utils/shallow-equal.mjs.map +1 -0
  179. package/dist/vm/index.d.mts +1 -0
  180. package/dist/vm/index.d.ts +1 -0
  181. package/dist/vm/index.js +37 -0
  182. package/dist/vm/index.js.map +1 -0
  183. package/dist/vm/index.mjs +9 -0
  184. package/dist/vm/index.mjs.map +1 -0
  185. package/package.json +52 -0
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/assets/loader.ts
22
+ var loader_exports = {};
23
+ __export(loader_exports, {
24
+ AssetLoader: () => AssetLoader
25
+ });
26
+ module.exports = __toCommonJS(loader_exports);
27
+ var import_audio = require("@al8b/audio");
28
+
29
+ // src/constants.ts
30
+ var DEFAULT_FPS = 60;
31
+ var FRAME_TIME_MS = 1e3 / DEFAULT_FPS;
32
+ var DEFAULT_BLOCK_SIZE = 16;
33
+ var LOADING_BAR_THROTTLE_MS = 16;
34
+ var ASSET_LOAD_TIMEOUT_MS = 3e4;
35
+
36
+ // src/assets/loader.ts
37
+ var import_map = require("@al8b/map");
38
+ var import_sprites = require("@al8b/sprites");
39
+ function withTimeout(promise, ms) {
40
+ return Promise.race([
41
+ promise,
42
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Asset load timed out after ${ms}ms`)), ms))
43
+ ]);
44
+ }
45
+ __name(withTimeout, "withTimeout");
46
+ var AssetLoader = class {
47
+ static {
48
+ __name(this, "AssetLoader");
49
+ }
50
+ url;
51
+ resources;
52
+ collections;
53
+ loadingBarTime = null;
54
+ audioCore;
55
+ listener;
56
+ constructor(url, resources, audioCore, listener) {
57
+ this.url = url;
58
+ this.resources = resources;
59
+ this.audioCore = audioCore;
60
+ this.listener = listener;
61
+ this.collections = {
62
+ sprites: {},
63
+ maps: {},
64
+ sounds: {},
65
+ music: {},
66
+ assets: {}
67
+ };
68
+ }
69
+ /**
70
+ * Load all assets
71
+ */
72
+ async loadAll() {
73
+ await Promise.all([
74
+ this.loadSprites(),
75
+ this.loadMaps(),
76
+ this.loadSounds(),
77
+ this.loadMusic(),
78
+ this.loadGenericAssets()
79
+ ]);
80
+ return this.collections;
81
+ }
82
+ /**
83
+ * Load a set of callback-based assets (sprites or maps) with timeout and placeholder fallback.
84
+ */
85
+ async loadCallbackAssets(resources, urlPrefix, collectionKey, load, createPlaceholder) {
86
+ if (!resources) return;
87
+ const promises = resources.map((res) => new Promise((resolve) => {
88
+ const name = res.file.split(".")[0].replace(/-/g, "/");
89
+ const url = `${this.url}${urlPrefix}/${res.file}?v=${res.version || 0}`;
90
+ try {
91
+ const inner = new Promise((onReady) => {
92
+ this.collections[collectionKey][name] = load(url, res, onReady);
93
+ });
94
+ withTimeout(inner, ASSET_LOAD_TIMEOUT_MS).then(resolve).catch((err) => {
95
+ this.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} "${name}": ${String(err)}`);
96
+ this.collections[collectionKey][name] = createPlaceholder(name, res);
97
+ resolve();
98
+ });
99
+ } catch (err) {
100
+ this.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} "${name}": ${String(err)}`);
101
+ this.collections[collectionKey][name] = createPlaceholder(name, res);
102
+ resolve();
103
+ }
104
+ }));
105
+ await Promise.all(promises);
106
+ }
107
+ /**
108
+ * Load sprites
109
+ */
110
+ async loadSprites() {
111
+ await this.loadCallbackAssets(this.resources.images, "sprites", "sprites", (url, img, onReady) => (0, import_sprites.LoadSprite)(url, img.properties, onReady), (name, img) => ({
112
+ name,
113
+ ready: false,
114
+ frames: [],
115
+ fps: img.properties?.fps || 5,
116
+ width: 0,
117
+ height: 0
118
+ }));
119
+ }
120
+ /**
121
+ * Load maps
122
+ */
123
+ async loadMaps() {
124
+ await this.loadCallbackAssets(this.resources.maps, "maps", "maps", (url, _mapRes, onReady) => (0, import_map.LoadMap)(url, this.collections.sprites, onReady), (name) => ({
125
+ name,
126
+ ready: false,
127
+ width: 0,
128
+ height: 0,
129
+ block_width: DEFAULT_BLOCK_SIZE,
130
+ block_height: DEFAULT_BLOCK_SIZE,
131
+ data: []
132
+ }));
133
+ }
134
+ /**
135
+ * Load sounds
136
+ */
137
+ async loadSounds() {
138
+ if (!this.resources.sounds) return;
139
+ const promises = this.resources.sounds.map((sound) => {
140
+ return new Promise((resolve) => {
141
+ const name = sound.file.split(".")[0];
142
+ const url = `${this.url}sounds/${sound.file}?v=${sound.version || 0}`;
143
+ try {
144
+ const soundInstance = new import_audio.Sound(this.audioCore, url);
145
+ this.collections.sounds[name] = soundInstance;
146
+ resolve();
147
+ } catch (err) {
148
+ this.listener?.log?.(`[AssetLoader] Failed to load sound "${name}": ${String(err)}`);
149
+ this.collections.sounds[name] = new import_audio.Sound(this.audioCore, url);
150
+ resolve();
151
+ }
152
+ });
153
+ });
154
+ await Promise.all(promises);
155
+ }
156
+ /**
157
+ * Load music
158
+ */
159
+ async loadMusic() {
160
+ if (!this.resources.music) return;
161
+ const promises = this.resources.music.map((mus) => {
162
+ return new Promise((resolve) => {
163
+ const name = mus.file.split(".")[0];
164
+ const url = `${this.url}music/${mus.file}?v=${mus.version || 0}`;
165
+ try {
166
+ const musicInstance = new import_audio.Music(this.audioCore, url);
167
+ this.collections.music[name] = musicInstance;
168
+ resolve();
169
+ } catch (err) {
170
+ this.listener?.log?.(`[AssetLoader] Failed to load music "${name}": ${String(err)}`);
171
+ this.collections.music[name] = new import_audio.Music(this.audioCore, url);
172
+ resolve();
173
+ }
174
+ });
175
+ });
176
+ await Promise.all(promises);
177
+ }
178
+ /**
179
+ * Load generic assets
180
+ */
181
+ async loadGenericAssets() {
182
+ if (!this.resources.assets) return;
183
+ for (const asset of this.resources.assets) {
184
+ const name = asset.file.split(".")[0].replace(/-/g, "/");
185
+ this.collections.assets[name] = {
186
+ name,
187
+ file: asset.file,
188
+ version: asset.version
189
+ };
190
+ }
191
+ }
192
+ /**
193
+ * Check if all assets are ready
194
+ */
195
+ isReady() {
196
+ return this.countReady() === this.countTotal();
197
+ }
198
+ /**
199
+ * Get loading progress (0-1)
200
+ */
201
+ getProgress() {
202
+ const total = this.countTotal();
203
+ if (total === 0) return 1;
204
+ return this.countReady() / total;
205
+ }
206
+ /**
207
+ * Count total assets
208
+ */
209
+ countTotal() {
210
+ let count = 0;
211
+ count += Object.keys(this.collections.sprites).length;
212
+ count += Object.keys(this.collections.maps).length;
213
+ count += Object.keys(this.collections.sounds).length;
214
+ count += Object.keys(this.collections.music).length;
215
+ return count;
216
+ }
217
+ /**
218
+ * Count ready assets
219
+ */
220
+ countReady() {
221
+ let ready = 0;
222
+ for (const sprite of Object.values(this.collections.sprites)) {
223
+ if (sprite.ready) ready++;
224
+ }
225
+ for (const map of Object.values(this.collections.maps)) {
226
+ if (map.ready) ready++;
227
+ }
228
+ for (const sound of Object.values(this.collections.sounds)) {
229
+ if (sound.ready) ready++;
230
+ }
231
+ for (const mus of Object.values(this.collections.music)) {
232
+ if (mus.ready) ready++;
233
+ }
234
+ return ready;
235
+ }
236
+ /**
237
+ * Show loading bar on screen
238
+ */
239
+ showLoadingBar(screenInterface) {
240
+ if (this.loadingBarTime && Date.now() < this.loadingBarTime + LOADING_BAR_THROTTLE_MS) {
241
+ return;
242
+ }
243
+ this.loadingBarTime = Date.now();
244
+ const progress = this.getProgress();
245
+ screenInterface.clear("#000");
246
+ screenInterface.drawRect(0, 0, 100, 10, "#DDD");
247
+ screenInterface.fillRect(-(1 - progress) * 48, 0, progress * 96, 6, "#DDD");
248
+ }
249
+ /**
250
+ * Get loaded collections
251
+ */
252
+ getCollections() {
253
+ return this.collections;
254
+ }
255
+ };
256
+ // Annotate the CommonJS export names for ESM import in node:
257
+ 0 && (module.exports = {
258
+ AssetLoader
259
+ });
260
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/assets/loader.ts","../../src/constants.ts"],"sourcesContent":["/**\n * AssetLoader - Handles loading of game assets\n *\n * Responsibilities:\n * - Load sprites, maps, sounds, music\n * - Track loading progress\n * - Show loading bar\n */\n\nimport { AudioCore, Sound, Music } from \"@al8b/audio\";\nimport { ASSET_LOAD_TIMEOUT_MS, DEFAULT_BLOCK_SIZE, LOADING_BAR_THROTTLE_MS } from \"../constants\";\nimport { LoadMap } from \"@al8b/map\";\nimport { LoadSprite } from \"@al8b/sprites\";\nimport type { AssetCollections, ResourceFile, Resources } from \"../types\";\nimport type { RuntimeListener } from \"../types\";\n\n/**\n * Race a promise against a timeout.\n * Rejects with a descriptive error if `ms` elapses before the promise settles.\n */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\treturn Promise.race([\n\t\tpromise,\n\t\tnew Promise<T>((_, reject) =>\n\t\t\tsetTimeout(() => reject(new Error(`Asset load timed out after ${ms}ms`)), ms),\n\t\t),\n\t]);\n}\n\nexport class AssetLoader {\n\tprivate url: string;\n\tprivate resources: Resources;\n\tprivate collections: AssetCollections;\n\tprivate loadingBarTime: number | null = null;\n\tprivate audioCore: AudioCore;\n\tprivate listener?: RuntimeListener;\n\n\tconstructor(url: string, resources: Resources, audioCore: AudioCore, listener?: RuntimeListener) {\n\t\tthis.url = url;\n\t\tthis.resources = resources;\n\t\tthis.audioCore = audioCore;\n\t\tthis.listener = listener;\n\t\tthis.collections = {\n\t\t\tsprites: {},\n\t\t\tmaps: {},\n\t\t\tsounds: {},\n\t\t\tmusic: {},\n\t\t\tassets: {},\n\t\t};\n\t}\n\n\t/**\n\t * Load all assets\n\t */\n\tasync loadAll(): Promise<AssetCollections> {\n\t\tawait Promise.all([\n\t\t\tthis.loadSprites(),\n\t\t\tthis.loadMaps(),\n\t\t\tthis.loadSounds(),\n\t\t\tthis.loadMusic(),\n\t\t\tthis.loadGenericAssets(),\n\t\t]);\n\n\t\treturn this.collections;\n\t}\n\n\t/**\n\t * Load a set of callback-based assets (sprites or maps) with timeout and placeholder fallback.\n\t */\n\tprivate async loadCallbackAssets<TRes extends ResourceFile>(\n\t\tresources: TRes[] | undefined,\n\t\turlPrefix: string,\n\t\tcollectionKey: \"sprites\" | \"maps\",\n\t\tload: (url: string, res: TRes, onReady: () => void) => any,\n\t\tcreatePlaceholder: (name: string, res: TRes) => any,\n\t): Promise<void> {\n\t\tif (!resources) return;\n\n\t\tconst promises = resources.map(\n\t\t\t(res) =>\n\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\tconst name = res.file.split(\".\")[0].replace(/-/g, \"/\");\n\t\t\t\t\tconst url = `${this.url}${urlPrefix}/${res.file}?v=${res.version || 0}`;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst inner = new Promise<void>((onReady) => {\n\t\t\t\t\t\t\tthis.collections[collectionKey][name] = load(url, res, onReady);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\twithTimeout(inner, ASSET_LOAD_TIMEOUT_MS).then(resolve).catch((err) => {\n\t\t\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} \"${name}\": ${String(err)}`);\n\t\t\t\t\t\t\tthis.collections[collectionKey][name] = createPlaceholder(name, res);\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} \"${name}\": ${String(err)}`);\n\t\t\t\t\t\tthis.collections[collectionKey][name] = createPlaceholder(name, res);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t);\n\n\t\tawait Promise.all(promises);\n\t}\n\n\t/**\n\t * Load sprites\n\t */\n\tprivate async loadSprites(): Promise<void> {\n\t\tawait this.loadCallbackAssets(\n\t\t\tthis.resources.images,\n\t\t\t\"sprites\",\n\t\t\t\"sprites\",\n\t\t\t(url, img, onReady) => LoadSprite(url, img.properties, onReady),\n\t\t\t(name, img) => ({\n\t\t\t\tname,\n\t\t\t\tready: false,\n\t\t\t\tframes: [],\n\t\t\t\tfps: (img.properties as any)?.fps || 5,\n\t\t\t\twidth: 0,\n\t\t\t\theight: 0,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Load maps\n\t */\n\tprivate async loadMaps(): Promise<void> {\n\t\tawait this.loadCallbackAssets(\n\t\t\tthis.resources.maps,\n\t\t\t\"maps\",\n\t\t\t\"maps\",\n\t\t\t(url, _mapRes, onReady) => LoadMap(url, this.collections.sprites, onReady),\n\t\t\t(name) => ({\n\t\t\t\tname,\n\t\t\t\tready: false,\n\t\t\t\twidth: 0,\n\t\t\t\theight: 0,\n\t\t\t\tblock_width: DEFAULT_BLOCK_SIZE,\n\t\t\t\tblock_height: DEFAULT_BLOCK_SIZE,\n\t\t\t\tdata: [],\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Load sounds\n\t */\n\tprivate async loadSounds(): Promise<void> {\n\t\tif (!this.resources.sounds) return;\n\n\t\tconst promises = this.resources.sounds.map((sound) => {\n\t\t\treturn new Promise<void>((resolve) => {\n\t\t\t\tconst name = sound.file.split(\".\")[0];\n\t\t\t\tconst url = `${this.url}sounds/${sound.file}?v=${sound.version || 0}`;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Sound class handles loading via XMLHttpRequest and AudioBuffer internally.\n\t\t\t\t\t// Resolve immediately — readiness is polled via isReady()/getProgress() in the game loop.\n\t\t\t\t\tconst soundInstance = new Sound(this.audioCore, url);\n\t\t\t\t\tthis.collections.sounds[name] = soundInstance;\n\t\t\t\t\tresolve();\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load sound \"${name}\": ${String(err)}`);\n\t\t\t\t\tthis.collections.sounds[name] = new Sound(this.audioCore, url);\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tawait Promise.all(promises);\n\t}\n\n\t/**\n\t * Load music\n\t */\n\tprivate async loadMusic(): Promise<void> {\n\t\tif (!this.resources.music) return;\n\n\t\tconst promises = this.resources.music.map((mus) => {\n\t\t\treturn new Promise<void>((resolve) => {\n\t\t\t\tconst name = mus.file.split(\".\")[0];\n\t\t\t\tconst url = `${this.url}music/${mus.file}?v=${mus.version || 0}`;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Music class handles HTML5 Audio internally (streaming — ready immediately).\n\t\t\t\t\tconst musicInstance = new Music(this.audioCore, url);\n\t\t\t\t\tthis.collections.music[name] = musicInstance;\n\t\t\t\t\tresolve();\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load music \"${name}\": ${String(err)}`);\n\t\t\t\t\tthis.collections.music[name] = new Music(this.audioCore, url);\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tawait Promise.all(promises);\n\t}\n\n\t/**\n\t * Load generic assets\n\t */\n\tprivate async loadGenericAssets(): Promise<void> {\n\t\tif (!this.resources.assets) return;\n\n\t\tfor (const asset of this.resources.assets) {\n\t\t\tconst name = asset.file.split(\".\")[0].replace(/-/g, \"/\");\n\t\t\tthis.collections.assets[name] = {\n\t\t\t\tname,\n\t\t\t\tfile: asset.file,\n\t\t\t\tversion: asset.version,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Check if all assets are ready\n\t */\n\tisReady(): boolean {\n\t\treturn this.countReady() === this.countTotal();\n\t}\n\n\t/**\n\t * Get loading progress (0-1)\n\t */\n\tgetProgress(): number {\n\t\tconst total = this.countTotal();\n\t\tif (total === 0) return 1;\n\t\treturn this.countReady() / total;\n\t}\n\n\t/**\n\t * Count total assets\n\t */\n\tprivate countTotal(): number {\n\t\tlet count = 0;\n\t\tcount += Object.keys(this.collections.sprites).length;\n\t\tcount += Object.keys(this.collections.maps).length;\n\t\tcount += Object.keys(this.collections.sounds).length;\n\t\tcount += Object.keys(this.collections.music).length;\n\t\treturn count;\n\t}\n\n\t/**\n\t * Count ready assets\n\t */\n\tprivate countReady(): number {\n\t\tlet ready = 0;\n\n\t\tfor (const sprite of Object.values(this.collections.sprites)) {\n\t\t\tif (sprite.ready) ready++;\n\t\t}\n\t\tfor (const map of Object.values(this.collections.maps)) {\n\t\t\tif (map.ready) ready++;\n\t\t}\n\t\tfor (const sound of Object.values(this.collections.sounds)) {\n\t\t\tif (sound.ready) ready++;\n\t\t}\n\t\tfor (const mus of Object.values(this.collections.music)) {\n\t\t\tif (mus.ready) ready++;\n\t\t}\n\n\t\treturn ready;\n\t}\n\n\t/**\n\t * Show loading bar on screen\n\t */\n\tshowLoadingBar(screenInterface: any): void {\n\t\t// Throttle redraws\n\t\tif (this.loadingBarTime && Date.now() < this.loadingBarTime + LOADING_BAR_THROTTLE_MS) {\n\t\t\treturn;\n\t\t}\n\t\tthis.loadingBarTime = Date.now();\n\n\t\tconst progress = this.getProgress();\n\t\tscreenInterface.clear(\"#000\");\n\t\tscreenInterface.drawRect(0, 0, 100, 10, \"#DDD\");\n\t\tscreenInterface.fillRect(-(1 - progress) * 48, 0, progress * 96, 6, \"#DDD\");\n\t}\n\n\t/**\n\t * Get loaded collections\n\t */\n\tgetCollections(): AssetCollections {\n\t\treturn this.collections;\n\t}\n}\n","/** Default frames per second */\nexport const DEFAULT_FPS = 60;\n\n/** Default update rate (updates per second) */\nexport const DEFAULT_UPDATE_RATE = 60;\n\n/** Frame time in milliseconds at 60 FPS */\nexport const FRAME_TIME_MS = 1000 / DEFAULT_FPS;\n\n/** Threshold in ms to detect long pauses (tab switch, etc.) */\nexport const PAUSE_THRESHOLD_MS = 160;\n\n/** Default tile/block size in pixels */\nexport const DEFAULT_BLOCK_SIZE = 16;\n\n/** Minimum interval between loading bar redraws in ms (~60fps) */\nexport const LOADING_BAR_THROTTLE_MS = 16;\n\n/** Timeout in ms for individual asset loads (sprite/map HTTP requests) */\nexport const ASSET_LOAD_TIMEOUT_MS = 30_000;\n"],"mappings":"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","names":["DEFAULT_FPS","FRAME_TIME_MS","DEFAULT_FPS","DEFAULT_BLOCK_SIZE","LOADING_BAR_THROTTLE_MS","ASSET_LOAD_TIMEOUT_MS","withTimeout","promise","ms","Promise","race","_","reject","setTimeout","Error","AssetLoader","url","resources","collections","loadingBarTime","audioCore","listener","sprites","maps","sounds","music","assets","loadAll","all","loadSprites","loadMaps","loadSounds","loadMusic","loadGenericAssets","loadCallbackAssets","urlPrefix","collectionKey","load","createPlaceholder","promises","map","res","resolve","name","file","split","replace","version","inner","onReady","ASSET_LOAD_TIMEOUT_MS","then","catch","err","log","slice","String","images","img","LoadSprite","properties","ready","frames","fps","width","height","_mapRes","LoadMap","block_width","DEFAULT_BLOCK_SIZE","block_height","data","sound","soundInstance","Sound","mus","musicInstance","Music","asset","isReady","countReady","countTotal","getProgress","total","count","Object","keys","length","sprite","values","showLoadingBar","screenInterface","Date","now","LOADING_BAR_THROTTLE_MS","progress","clear","drawRect","fillRect","getCollections"]}
@@ -0,0 +1,237 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/assets/loader.ts
5
+ import { Sound, Music } from "@al8b/audio";
6
+
7
+ // src/constants.ts
8
+ var DEFAULT_FPS = 60;
9
+ var FRAME_TIME_MS = 1e3 / DEFAULT_FPS;
10
+ var DEFAULT_BLOCK_SIZE = 16;
11
+ var LOADING_BAR_THROTTLE_MS = 16;
12
+ var ASSET_LOAD_TIMEOUT_MS = 3e4;
13
+
14
+ // src/assets/loader.ts
15
+ import { LoadMap } from "@al8b/map";
16
+ import { LoadSprite } from "@al8b/sprites";
17
+ function withTimeout(promise, ms) {
18
+ return Promise.race([
19
+ promise,
20
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Asset load timed out after ${ms}ms`)), ms))
21
+ ]);
22
+ }
23
+ __name(withTimeout, "withTimeout");
24
+ var AssetLoader = class {
25
+ static {
26
+ __name(this, "AssetLoader");
27
+ }
28
+ url;
29
+ resources;
30
+ collections;
31
+ loadingBarTime = null;
32
+ audioCore;
33
+ listener;
34
+ constructor(url, resources, audioCore, listener) {
35
+ this.url = url;
36
+ this.resources = resources;
37
+ this.audioCore = audioCore;
38
+ this.listener = listener;
39
+ this.collections = {
40
+ sprites: {},
41
+ maps: {},
42
+ sounds: {},
43
+ music: {},
44
+ assets: {}
45
+ };
46
+ }
47
+ /**
48
+ * Load all assets
49
+ */
50
+ async loadAll() {
51
+ await Promise.all([
52
+ this.loadSprites(),
53
+ this.loadMaps(),
54
+ this.loadSounds(),
55
+ this.loadMusic(),
56
+ this.loadGenericAssets()
57
+ ]);
58
+ return this.collections;
59
+ }
60
+ /**
61
+ * Load a set of callback-based assets (sprites or maps) with timeout and placeholder fallback.
62
+ */
63
+ async loadCallbackAssets(resources, urlPrefix, collectionKey, load, createPlaceholder) {
64
+ if (!resources) return;
65
+ const promises = resources.map((res) => new Promise((resolve) => {
66
+ const name = res.file.split(".")[0].replace(/-/g, "/");
67
+ const url = `${this.url}${urlPrefix}/${res.file}?v=${res.version || 0}`;
68
+ try {
69
+ const inner = new Promise((onReady) => {
70
+ this.collections[collectionKey][name] = load(url, res, onReady);
71
+ });
72
+ withTimeout(inner, ASSET_LOAD_TIMEOUT_MS).then(resolve).catch((err) => {
73
+ this.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} "${name}": ${String(err)}`);
74
+ this.collections[collectionKey][name] = createPlaceholder(name, res);
75
+ resolve();
76
+ });
77
+ } catch (err) {
78
+ this.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} "${name}": ${String(err)}`);
79
+ this.collections[collectionKey][name] = createPlaceholder(name, res);
80
+ resolve();
81
+ }
82
+ }));
83
+ await Promise.all(promises);
84
+ }
85
+ /**
86
+ * Load sprites
87
+ */
88
+ async loadSprites() {
89
+ await this.loadCallbackAssets(this.resources.images, "sprites", "sprites", (url, img, onReady) => LoadSprite(url, img.properties, onReady), (name, img) => ({
90
+ name,
91
+ ready: false,
92
+ frames: [],
93
+ fps: img.properties?.fps || 5,
94
+ width: 0,
95
+ height: 0
96
+ }));
97
+ }
98
+ /**
99
+ * Load maps
100
+ */
101
+ async loadMaps() {
102
+ await this.loadCallbackAssets(this.resources.maps, "maps", "maps", (url, _mapRes, onReady) => LoadMap(url, this.collections.sprites, onReady), (name) => ({
103
+ name,
104
+ ready: false,
105
+ width: 0,
106
+ height: 0,
107
+ block_width: DEFAULT_BLOCK_SIZE,
108
+ block_height: DEFAULT_BLOCK_SIZE,
109
+ data: []
110
+ }));
111
+ }
112
+ /**
113
+ * Load sounds
114
+ */
115
+ async loadSounds() {
116
+ if (!this.resources.sounds) return;
117
+ const promises = this.resources.sounds.map((sound) => {
118
+ return new Promise((resolve) => {
119
+ const name = sound.file.split(".")[0];
120
+ const url = `${this.url}sounds/${sound.file}?v=${sound.version || 0}`;
121
+ try {
122
+ const soundInstance = new Sound(this.audioCore, url);
123
+ this.collections.sounds[name] = soundInstance;
124
+ resolve();
125
+ } catch (err) {
126
+ this.listener?.log?.(`[AssetLoader] Failed to load sound "${name}": ${String(err)}`);
127
+ this.collections.sounds[name] = new Sound(this.audioCore, url);
128
+ resolve();
129
+ }
130
+ });
131
+ });
132
+ await Promise.all(promises);
133
+ }
134
+ /**
135
+ * Load music
136
+ */
137
+ async loadMusic() {
138
+ if (!this.resources.music) return;
139
+ const promises = this.resources.music.map((mus) => {
140
+ return new Promise((resolve) => {
141
+ const name = mus.file.split(".")[0];
142
+ const url = `${this.url}music/${mus.file}?v=${mus.version || 0}`;
143
+ try {
144
+ const musicInstance = new Music(this.audioCore, url);
145
+ this.collections.music[name] = musicInstance;
146
+ resolve();
147
+ } catch (err) {
148
+ this.listener?.log?.(`[AssetLoader] Failed to load music "${name}": ${String(err)}`);
149
+ this.collections.music[name] = new Music(this.audioCore, url);
150
+ resolve();
151
+ }
152
+ });
153
+ });
154
+ await Promise.all(promises);
155
+ }
156
+ /**
157
+ * Load generic assets
158
+ */
159
+ async loadGenericAssets() {
160
+ if (!this.resources.assets) return;
161
+ for (const asset of this.resources.assets) {
162
+ const name = asset.file.split(".")[0].replace(/-/g, "/");
163
+ this.collections.assets[name] = {
164
+ name,
165
+ file: asset.file,
166
+ version: asset.version
167
+ };
168
+ }
169
+ }
170
+ /**
171
+ * Check if all assets are ready
172
+ */
173
+ isReady() {
174
+ return this.countReady() === this.countTotal();
175
+ }
176
+ /**
177
+ * Get loading progress (0-1)
178
+ */
179
+ getProgress() {
180
+ const total = this.countTotal();
181
+ if (total === 0) return 1;
182
+ return this.countReady() / total;
183
+ }
184
+ /**
185
+ * Count total assets
186
+ */
187
+ countTotal() {
188
+ let count = 0;
189
+ count += Object.keys(this.collections.sprites).length;
190
+ count += Object.keys(this.collections.maps).length;
191
+ count += Object.keys(this.collections.sounds).length;
192
+ count += Object.keys(this.collections.music).length;
193
+ return count;
194
+ }
195
+ /**
196
+ * Count ready assets
197
+ */
198
+ countReady() {
199
+ let ready = 0;
200
+ for (const sprite of Object.values(this.collections.sprites)) {
201
+ if (sprite.ready) ready++;
202
+ }
203
+ for (const map of Object.values(this.collections.maps)) {
204
+ if (map.ready) ready++;
205
+ }
206
+ for (const sound of Object.values(this.collections.sounds)) {
207
+ if (sound.ready) ready++;
208
+ }
209
+ for (const mus of Object.values(this.collections.music)) {
210
+ if (mus.ready) ready++;
211
+ }
212
+ return ready;
213
+ }
214
+ /**
215
+ * Show loading bar on screen
216
+ */
217
+ showLoadingBar(screenInterface) {
218
+ if (this.loadingBarTime && Date.now() < this.loadingBarTime + LOADING_BAR_THROTTLE_MS) {
219
+ return;
220
+ }
221
+ this.loadingBarTime = Date.now();
222
+ const progress = this.getProgress();
223
+ screenInterface.clear("#000");
224
+ screenInterface.drawRect(0, 0, 100, 10, "#DDD");
225
+ screenInterface.fillRect(-(1 - progress) * 48, 0, progress * 96, 6, "#DDD");
226
+ }
227
+ /**
228
+ * Get loaded collections
229
+ */
230
+ getCollections() {
231
+ return this.collections;
232
+ }
233
+ };
234
+ export {
235
+ AssetLoader
236
+ };
237
+ //# sourceMappingURL=loader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/assets/loader.ts","../../src/constants.ts"],"sourcesContent":["/**\n * AssetLoader - Handles loading of game assets\n *\n * Responsibilities:\n * - Load sprites, maps, sounds, music\n * - Track loading progress\n * - Show loading bar\n */\n\nimport { AudioCore, Sound, Music } from \"@al8b/audio\";\nimport { ASSET_LOAD_TIMEOUT_MS, DEFAULT_BLOCK_SIZE, LOADING_BAR_THROTTLE_MS } from \"../constants\";\nimport { LoadMap } from \"@al8b/map\";\nimport { LoadSprite } from \"@al8b/sprites\";\nimport type { AssetCollections, ResourceFile, Resources } from \"../types\";\nimport type { RuntimeListener } from \"../types\";\n\n/**\n * Race a promise against a timeout.\n * Rejects with a descriptive error if `ms` elapses before the promise settles.\n */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\treturn Promise.race([\n\t\tpromise,\n\t\tnew Promise<T>((_, reject) =>\n\t\t\tsetTimeout(() => reject(new Error(`Asset load timed out after ${ms}ms`)), ms),\n\t\t),\n\t]);\n}\n\nexport class AssetLoader {\n\tprivate url: string;\n\tprivate resources: Resources;\n\tprivate collections: AssetCollections;\n\tprivate loadingBarTime: number | null = null;\n\tprivate audioCore: AudioCore;\n\tprivate listener?: RuntimeListener;\n\n\tconstructor(url: string, resources: Resources, audioCore: AudioCore, listener?: RuntimeListener) {\n\t\tthis.url = url;\n\t\tthis.resources = resources;\n\t\tthis.audioCore = audioCore;\n\t\tthis.listener = listener;\n\t\tthis.collections = {\n\t\t\tsprites: {},\n\t\t\tmaps: {},\n\t\t\tsounds: {},\n\t\t\tmusic: {},\n\t\t\tassets: {},\n\t\t};\n\t}\n\n\t/**\n\t * Load all assets\n\t */\n\tasync loadAll(): Promise<AssetCollections> {\n\t\tawait Promise.all([\n\t\t\tthis.loadSprites(),\n\t\t\tthis.loadMaps(),\n\t\t\tthis.loadSounds(),\n\t\t\tthis.loadMusic(),\n\t\t\tthis.loadGenericAssets(),\n\t\t]);\n\n\t\treturn this.collections;\n\t}\n\n\t/**\n\t * Load a set of callback-based assets (sprites or maps) with timeout and placeholder fallback.\n\t */\n\tprivate async loadCallbackAssets<TRes extends ResourceFile>(\n\t\tresources: TRes[] | undefined,\n\t\turlPrefix: string,\n\t\tcollectionKey: \"sprites\" | \"maps\",\n\t\tload: (url: string, res: TRes, onReady: () => void) => any,\n\t\tcreatePlaceholder: (name: string, res: TRes) => any,\n\t): Promise<void> {\n\t\tif (!resources) return;\n\n\t\tconst promises = resources.map(\n\t\t\t(res) =>\n\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\tconst name = res.file.split(\".\")[0].replace(/-/g, \"/\");\n\t\t\t\t\tconst url = `${this.url}${urlPrefix}/${res.file}?v=${res.version || 0}`;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst inner = new Promise<void>((onReady) => {\n\t\t\t\t\t\t\tthis.collections[collectionKey][name] = load(url, res, onReady);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\twithTimeout(inner, ASSET_LOAD_TIMEOUT_MS).then(resolve).catch((err) => {\n\t\t\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} \"${name}\": ${String(err)}`);\n\t\t\t\t\t\t\tthis.collections[collectionKey][name] = createPlaceholder(name, res);\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load ${collectionKey.slice(0, -1)} \"${name}\": ${String(err)}`);\n\t\t\t\t\t\tthis.collections[collectionKey][name] = createPlaceholder(name, res);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t);\n\n\t\tawait Promise.all(promises);\n\t}\n\n\t/**\n\t * Load sprites\n\t */\n\tprivate async loadSprites(): Promise<void> {\n\t\tawait this.loadCallbackAssets(\n\t\t\tthis.resources.images,\n\t\t\t\"sprites\",\n\t\t\t\"sprites\",\n\t\t\t(url, img, onReady) => LoadSprite(url, img.properties, onReady),\n\t\t\t(name, img) => ({\n\t\t\t\tname,\n\t\t\t\tready: false,\n\t\t\t\tframes: [],\n\t\t\t\tfps: (img.properties as any)?.fps || 5,\n\t\t\t\twidth: 0,\n\t\t\t\theight: 0,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Load maps\n\t */\n\tprivate async loadMaps(): Promise<void> {\n\t\tawait this.loadCallbackAssets(\n\t\t\tthis.resources.maps,\n\t\t\t\"maps\",\n\t\t\t\"maps\",\n\t\t\t(url, _mapRes, onReady) => LoadMap(url, this.collections.sprites, onReady),\n\t\t\t(name) => ({\n\t\t\t\tname,\n\t\t\t\tready: false,\n\t\t\t\twidth: 0,\n\t\t\t\theight: 0,\n\t\t\t\tblock_width: DEFAULT_BLOCK_SIZE,\n\t\t\t\tblock_height: DEFAULT_BLOCK_SIZE,\n\t\t\t\tdata: [],\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Load sounds\n\t */\n\tprivate async loadSounds(): Promise<void> {\n\t\tif (!this.resources.sounds) return;\n\n\t\tconst promises = this.resources.sounds.map((sound) => {\n\t\t\treturn new Promise<void>((resolve) => {\n\t\t\t\tconst name = sound.file.split(\".\")[0];\n\t\t\t\tconst url = `${this.url}sounds/${sound.file}?v=${sound.version || 0}`;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Sound class handles loading via XMLHttpRequest and AudioBuffer internally.\n\t\t\t\t\t// Resolve immediately — readiness is polled via isReady()/getProgress() in the game loop.\n\t\t\t\t\tconst soundInstance = new Sound(this.audioCore, url);\n\t\t\t\t\tthis.collections.sounds[name] = soundInstance;\n\t\t\t\t\tresolve();\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load sound \"${name}\": ${String(err)}`);\n\t\t\t\t\tthis.collections.sounds[name] = new Sound(this.audioCore, url);\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tawait Promise.all(promises);\n\t}\n\n\t/**\n\t * Load music\n\t */\n\tprivate async loadMusic(): Promise<void> {\n\t\tif (!this.resources.music) return;\n\n\t\tconst promises = this.resources.music.map((mus) => {\n\t\t\treturn new Promise<void>((resolve) => {\n\t\t\t\tconst name = mus.file.split(\".\")[0];\n\t\t\t\tconst url = `${this.url}music/${mus.file}?v=${mus.version || 0}`;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Music class handles HTML5 Audio internally (streaming — ready immediately).\n\t\t\t\t\tconst musicInstance = new Music(this.audioCore, url);\n\t\t\t\t\tthis.collections.music[name] = musicInstance;\n\t\t\t\t\tresolve();\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.listener?.log?.(`[AssetLoader] Failed to load music \"${name}\": ${String(err)}`);\n\t\t\t\t\tthis.collections.music[name] = new Music(this.audioCore, url);\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tawait Promise.all(promises);\n\t}\n\n\t/**\n\t * Load generic assets\n\t */\n\tprivate async loadGenericAssets(): Promise<void> {\n\t\tif (!this.resources.assets) return;\n\n\t\tfor (const asset of this.resources.assets) {\n\t\t\tconst name = asset.file.split(\".\")[0].replace(/-/g, \"/\");\n\t\t\tthis.collections.assets[name] = {\n\t\t\t\tname,\n\t\t\t\tfile: asset.file,\n\t\t\t\tversion: asset.version,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Check if all assets are ready\n\t */\n\tisReady(): boolean {\n\t\treturn this.countReady() === this.countTotal();\n\t}\n\n\t/**\n\t * Get loading progress (0-1)\n\t */\n\tgetProgress(): number {\n\t\tconst total = this.countTotal();\n\t\tif (total === 0) return 1;\n\t\treturn this.countReady() / total;\n\t}\n\n\t/**\n\t * Count total assets\n\t */\n\tprivate countTotal(): number {\n\t\tlet count = 0;\n\t\tcount += Object.keys(this.collections.sprites).length;\n\t\tcount += Object.keys(this.collections.maps).length;\n\t\tcount += Object.keys(this.collections.sounds).length;\n\t\tcount += Object.keys(this.collections.music).length;\n\t\treturn count;\n\t}\n\n\t/**\n\t * Count ready assets\n\t */\n\tprivate countReady(): number {\n\t\tlet ready = 0;\n\n\t\tfor (const sprite of Object.values(this.collections.sprites)) {\n\t\t\tif (sprite.ready) ready++;\n\t\t}\n\t\tfor (const map of Object.values(this.collections.maps)) {\n\t\t\tif (map.ready) ready++;\n\t\t}\n\t\tfor (const sound of Object.values(this.collections.sounds)) {\n\t\t\tif (sound.ready) ready++;\n\t\t}\n\t\tfor (const mus of Object.values(this.collections.music)) {\n\t\t\tif (mus.ready) ready++;\n\t\t}\n\n\t\treturn ready;\n\t}\n\n\t/**\n\t * Show loading bar on screen\n\t */\n\tshowLoadingBar(screenInterface: any): void {\n\t\t// Throttle redraws\n\t\tif (this.loadingBarTime && Date.now() < this.loadingBarTime + LOADING_BAR_THROTTLE_MS) {\n\t\t\treturn;\n\t\t}\n\t\tthis.loadingBarTime = Date.now();\n\n\t\tconst progress = this.getProgress();\n\t\tscreenInterface.clear(\"#000\");\n\t\tscreenInterface.drawRect(0, 0, 100, 10, \"#DDD\");\n\t\tscreenInterface.fillRect(-(1 - progress) * 48, 0, progress * 96, 6, \"#DDD\");\n\t}\n\n\t/**\n\t * Get loaded collections\n\t */\n\tgetCollections(): AssetCollections {\n\t\treturn this.collections;\n\t}\n}\n","/** Default frames per second */\nexport const DEFAULT_FPS = 60;\n\n/** Default update rate (updates per second) */\nexport const DEFAULT_UPDATE_RATE = 60;\n\n/** Frame time in milliseconds at 60 FPS */\nexport const FRAME_TIME_MS = 1000 / DEFAULT_FPS;\n\n/** Threshold in ms to detect long pauses (tab switch, etc.) */\nexport const PAUSE_THRESHOLD_MS = 160;\n\n/** Default tile/block size in pixels */\nexport const DEFAULT_BLOCK_SIZE = 16;\n\n/** Minimum interval between loading bar redraws in ms (~60fps) */\nexport const LOADING_BAR_THROTTLE_MS = 16;\n\n/** Timeout in ms for individual asset loads (sprite/map HTTP requests) */\nexport const ASSET_LOAD_TIMEOUT_MS = 30_000;\n"],"mappings":"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","names":["Sound","Music","DEFAULT_FPS","FRAME_TIME_MS","DEFAULT_FPS","DEFAULT_BLOCK_SIZE","LOADING_BAR_THROTTLE_MS","ASSET_LOAD_TIMEOUT_MS","LoadMap","LoadSprite","withTimeout","promise","ms","Promise","race","_","reject","setTimeout","Error","AssetLoader","url","resources","collections","loadingBarTime","audioCore","listener","sprites","maps","sounds","music","assets","loadAll","all","loadSprites","loadMaps","loadSounds","loadMusic","loadGenericAssets","loadCallbackAssets","urlPrefix","collectionKey","load","createPlaceholder","promises","map","res","resolve","name","file","split","replace","version","inner","onReady","ASSET_LOAD_TIMEOUT_MS","then","catch","err","log","slice","String","images","img","LoadSprite","properties","ready","frames","fps","width","height","_mapRes","LoadMap","block_width","DEFAULT_BLOCK_SIZE","block_height","data","sound","soundInstance","Sound","mus","musicInstance","Music","asset","isReady","countReady","countTotal","getProgress","total","count","Object","keys","length","sprite","values","showLoadingBar","screenInterface","Date","now","LOADING_BAR_THROTTLE_MS","progress","clear","drawRect","fillRect","getCollections"]}