emeraldengine 2.2.1 → 3.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 (208) hide show
  1. package/README.md +2198 -968
  2. package/dist/types/index.d.ts +75 -32
  3. package/dist/types/src/Animator.d.ts +50 -0
  4. package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
  5. package/dist/types/src/Camera.d.ts +122 -0
  6. package/dist/types/src/CameraController.d.ts +107 -0
  7. package/dist/types/src/CanvasText.d.ts +91 -0
  8. package/dist/types/src/CollisionLayers.d.ts +58 -0
  9. package/dist/types/{Color.d.ts → src/Color.d.ts} +6 -6
  10. package/dist/types/src/Coroutine.d.ts +111 -0
  11. package/dist/types/src/DebugOverlay.d.ts +86 -0
  12. package/dist/types/src/Drawable.d.ts +271 -0
  13. package/dist/types/src/Easing.d.ts +22 -0
  14. package/dist/types/src/Emerald.d.ts +420 -0
  15. package/dist/types/src/EmeraldDB.d.ts +159 -0
  16. package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
  17. package/dist/types/src/GLUtils.d.ts +4 -0
  18. package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
  19. package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +75 -23
  20. package/dist/types/src/Interpolator.d.ts +66 -0
  21. package/dist/types/src/Material.d.ts +87 -0
  22. package/dist/types/src/MathUtils.d.ts +81 -0
  23. package/dist/types/src/ParticleEmitter.d.ts +131 -0
  24. package/dist/types/src/Physics.d.ts +337 -0
  25. package/dist/types/src/Pool.d.ts +53 -0
  26. package/dist/types/src/PostEffects.d.ts +68 -0
  27. package/dist/types/src/PostProcessor.d.ts +124 -0
  28. package/dist/types/src/RenderTarget.d.ts +56 -0
  29. package/dist/types/src/Scene.d.ts +62 -0
  30. package/dist/types/src/ScreenEffects.d.ts +111 -0
  31. package/dist/types/src/Serializer.d.ts +86 -0
  32. package/dist/types/src/Shaders.d.ts +2 -0
  33. package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
  34. package/dist/types/src/SpatialGrid.d.ts +50 -0
  35. package/dist/types/src/SpriteBatch.d.ts +89 -0
  36. package/dist/types/src/StateMachine.d.ts +59 -0
  37. package/dist/types/src/Storage.d.ts +89 -0
  38. package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
  39. package/dist/types/src/TextureAtlas.d.ts +55 -0
  40. package/dist/types/src/Tilemap.d.ts +91 -0
  41. package/dist/types/src/Time.d.ts +53 -0
  42. package/dist/types/src/Timer.d.ts +54 -0
  43. package/dist/types/src/Transform.d.ts +94 -0
  44. package/dist/types/src/Tween.d.ts +81 -0
  45. package/dist/types/src/UI.d.ts +121 -0
  46. package/dist/types/src/components/Behaviour.d.ts +71 -0
  47. package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
  48. package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
  49. package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
  50. package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
  51. package/dist/types/src/components/Collider.d.ts +102 -0
  52. package/dist/types/src/components/GameObject.d.ts +135 -0
  53. package/dist/types/src/components/PolygonCollider.d.ts +33 -0
  54. package/dist/types/src/components/RigidBody.d.ts +456 -0
  55. package/dist/types/src/importers/Aseprite.d.ts +79 -0
  56. package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
  57. package/dist/types/src/importers/TiledMap.d.ts +62 -0
  58. package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
  59. package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
  60. package/dist/types/src/managers/AssetManager.d.ts +116 -0
  61. package/dist/types/src/managers/AudioManager.d.ts +259 -0
  62. package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
  63. package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
  64. package/dist/types/src/managers/GLManager.d.ts +84 -0
  65. package/dist/types/src/managers/GLState.d.ts +37 -0
  66. package/dist/types/src/managers/Gamepad.d.ts +102 -0
  67. package/dist/types/src/managers/IDManager.d.ts +31 -0
  68. package/dist/types/src/managers/InputManager.d.ts +381 -0
  69. package/dist/types/src/managers/NetworkManager.d.ts +93 -0
  70. package/dist/types/src/managers/RenderStats.d.ts +34 -0
  71. package/dist/types/src/managers/SceneManager.d.ts +44 -0
  72. package/dist/types/src/managers/ShaderManager.d.ts +55 -0
  73. package/dist/types/src/managers/TextureManager.d.ts +102 -0
  74. package/dist/types/src/particlesystem/Particle.d.ts +64 -0
  75. package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
  76. package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
  77. package/dist/types/src/physics/AABB.d.ts +92 -0
  78. package/dist/types/src/physics/Body.d.ts +435 -0
  79. package/dist/types/src/physics/BodyType.d.ts +6 -0
  80. package/dist/types/src/physics/BroadPhase.d.ts +210 -0
  81. package/dist/types/src/physics/Collision.d.ts +102 -0
  82. package/dist/types/src/physics/Contact.d.ts +206 -0
  83. package/dist/types/src/physics/ContactSolver.d.ts +108 -0
  84. package/dist/types/src/physics/Distance.d.ts +54 -0
  85. package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
  86. package/dist/types/src/physics/Fixture.d.ts +221 -0
  87. package/dist/types/src/physics/Island.d.ts +52 -0
  88. package/dist/types/src/physics/Joint.d.ts +59 -0
  89. package/dist/types/src/physics/Math2D.d.ts +371 -0
  90. package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
  91. package/dist/types/src/physics/Settings.d.ts +22 -0
  92. package/dist/types/src/physics/Shapes.d.ts +207 -0
  93. package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
  94. package/dist/types/src/physics/World.d.ts +274 -0
  95. package/dist/types/src/physics/index.d.ts +34 -0
  96. package/index.js +78 -0
  97. package/package.json +73 -60
  98. package/src/Animator.js +95 -0
  99. package/src/BitmapText.js +6 -5
  100. package/src/Camera.js +183 -0
  101. package/src/CameraController.js +192 -0
  102. package/src/CanvasText.js +281 -0
  103. package/src/CollisionLayers.js +86 -0
  104. package/src/Color.js +26 -18
  105. package/src/Coroutine.js +259 -0
  106. package/src/DebugOverlay.js +246 -0
  107. package/src/Drawable.js +842 -582
  108. package/src/Easing.js +57 -0
  109. package/src/Emerald.js +1150 -459
  110. package/src/EmeraldDB.js +328 -0
  111. package/src/FPSCounter.js +43 -43
  112. package/src/GLUtils.js +60 -67
  113. package/src/Instance.js +41 -5
  114. package/src/InstancedTexture.js +308 -127
  115. package/src/Interpolator.js +124 -0
  116. package/src/Material.js +202 -0
  117. package/src/MathUtils.js +134 -0
  118. package/src/ParticleEmitter.js +284 -0
  119. package/src/Physics.js +431 -40
  120. package/src/Pool.js +85 -0
  121. package/src/PostEffects.js +296 -0
  122. package/src/PostProcessor.js +304 -0
  123. package/src/RenderTarget.js +134 -0
  124. package/src/Scene.js +115 -83
  125. package/src/ScreenEffects.js +266 -0
  126. package/src/Serializer.js +131 -0
  127. package/src/Shaders.js +150 -165
  128. package/src/Shapes.js +118 -129
  129. package/src/SpatialGrid.js +111 -0
  130. package/src/SpriteBatch.js +299 -0
  131. package/src/StateMachine.js +82 -0
  132. package/src/Storage.js +175 -47
  133. package/src/Texture.js +58 -67
  134. package/src/TextureAtlas.js +96 -0
  135. package/src/Tilemap.js +274 -0
  136. package/src/Time.js +51 -6
  137. package/src/Timer.js +99 -0
  138. package/src/Transform.js +100 -7
  139. package/src/Tween.js +160 -0
  140. package/src/UI.js +394 -0
  141. package/src/components/Behaviour.js +90 -0
  142. package/src/components/BoxCollider.js +29 -12
  143. package/src/components/BoxColliderDebug.js +3 -4
  144. package/src/components/CircleCollider.js +26 -12
  145. package/src/components/CircleColliderDebug.js +25 -24
  146. package/src/components/Collider.js +150 -34
  147. package/src/components/GameObject.js +130 -21
  148. package/src/components/PolygonCollider.js +55 -0
  149. package/src/components/RigidBody.js +556 -8
  150. package/src/importers/Aseprite.js +142 -0
  151. package/src/importers/ForgeLevel.js +581 -0
  152. package/src/importers/TiledMap.js +158 -0
  153. package/src/lights/DirectionalLight.js +6 -15
  154. package/src/lights/PointLight.js +4 -4
  155. package/src/managers/AssetManager.js +239 -0
  156. package/src/managers/AudioManager.js +565 -146
  157. package/src/managers/EventManager.js +488 -477
  158. package/src/managers/GLManager.js +57 -0
  159. package/src/managers/GLState.js +70 -0
  160. package/src/managers/Gamepad.js +126 -0
  161. package/src/managers/IDManager.js +24 -2
  162. package/src/managers/InputManager.js +905 -0
  163. package/src/managers/NetworkManager.js +178 -0
  164. package/src/managers/RenderStats.js +34 -0
  165. package/src/managers/SceneManager.js +30 -0
  166. package/src/managers/ShaderManager.js +0 -2
  167. package/src/managers/TextureManager.js +218 -0
  168. package/src/particlesystem/Particle.js +82 -7
  169. package/src/particlesystem/ParticleSettings.js +21 -3
  170. package/src/particlesystem/Particles.js +80 -31
  171. package/src/physics/AABB.js +207 -0
  172. package/src/physics/Body.js +862 -0
  173. package/src/physics/BodyType.js +16 -0
  174. package/src/physics/BroadPhase.js +641 -0
  175. package/src/physics/Collision.js +534 -0
  176. package/src/physics/Contact.js +500 -0
  177. package/src/physics/ContactSolver.js +526 -0
  178. package/src/physics/Distance.js +403 -0
  179. package/src/physics/DistanceJoint.js +227 -0
  180. package/src/physics/Fixture.js +346 -0
  181. package/src/physics/Island.js +203 -0
  182. package/src/physics/Joint.js +78 -0
  183. package/src/physics/Math2D.js +573 -0
  184. package/src/physics/RevoluteJoint.js +278 -0
  185. package/src/physics/Settings.js +78 -0
  186. package/src/physics/Shapes.js +549 -0
  187. package/src/physics/TimeOfImpact.js +87 -0
  188. package/src/physics/World.js +731 -0
  189. package/src/physics/index.js +79 -0
  190. package/dist/types/Drawable.d.ts +0 -157
  191. package/dist/types/Emerald.d.ts +0 -73
  192. package/dist/types/GLUtils.d.ts +0 -6
  193. package/dist/types/Physics.d.ts +0 -139
  194. package/dist/types/Scene.d.ts +0 -39
  195. package/dist/types/Shaders.d.ts +0 -4
  196. package/dist/types/Storage.d.ts +0 -46
  197. package/dist/types/Time.d.ts +0 -22
  198. package/dist/types/Transform.d.ts +0 -41
  199. package/dist/types/components/BoxColliderDebug.d.ts +0 -19
  200. package/dist/types/components/CircleColliderDebug.d.ts +0 -19
  201. package/dist/types/components/Collider.d.ts +0 -53
  202. package/dist/types/components/GameObject.d.ts +0 -72
  203. package/dist/types/components/RigidBody.d.ts +0 -104
  204. package/dist/types/managers/AudioManager.d.ts +0 -60
  205. package/dist/types/managers/GLManager.d.ts +0 -47
  206. package/dist/types/managers/IDManager.d.ts +0 -21
  207. package/dist/types/managers/SceneManager.d.ts +0 -22
  208. package/dist/types/particlesystem/Particle.d.ts +0 -42
@@ -0,0 +1,158 @@
1
+ import Tilemap from "../Tilemap.js";
2
+
3
+ const FLIP_FLAGS = 0xe0000000;
4
+ const GID_MASK = 0x1fffffff;
5
+
6
+ /**
7
+ * @class TiledMap
8
+ * @description Imports orthogonal maps exported from the Tiled editor
9
+ * (https://www.mapeditor.org) in JSON format into the engine's Tilemap, plus
10
+ * helpers to pull object layers (spawn points, triggers, etc.) out as plain
11
+ * data. Pure parsing: pass it the already-parsed JSON object (load it with
12
+ * AssetManager.json or fetch).
13
+ *
14
+ * @example
15
+ * const map = TiledMap.toTilemap(mapJson, "tiles.png");
16
+ * scene.add(map.gameObject);
17
+ * map.buildColliders(physics);
18
+ * const spawns = TiledMap.objects(mapJson, { layer: "spawns", flipY: true });
19
+ */
20
+ class TiledMap {
21
+ /**
22
+ * @method tilesetFor
23
+ * @description Finds the tileset a (masked) global id belongs to.
24
+ * @param {Object} map - Parsed Tiled map JSON
25
+ * @param {number} gid - Masked global tile id (flip flags removed)
26
+ * @returns {Object|null}
27
+ */
28
+ static tilesetFor(map, gid) {
29
+ const sets = map.tilesets || [];
30
+ let best = null;
31
+ for (const ts of sets) {
32
+ if (ts.firstgid <= gid && (!best || ts.firstgid > best.firstgid)) {
33
+ best = ts;
34
+ }
35
+ }
36
+ return best;
37
+ }
38
+
39
+ /** @private */
40
+ static _tileLayer(map, layer) {
41
+ const layers = (map.layers || []).filter((l) => l.type === "tilelayer");
42
+ if (layer == null) return layers[0] || null;
43
+ if (typeof layer === "number") return layers[layer] || null;
44
+ return layers.find((l) => l.name === layer) || null;
45
+ }
46
+
47
+ /**
48
+ * @method toFrameGrid
49
+ * @description Converts a tile layer into a 2D array of local frame indices
50
+ * (-1 for empty), ready for Tilemap.setMap. Global ids are converted to
51
+ * tileset-local indices and flip flags are stripped.
52
+ * @param {Object} map - Parsed Tiled map JSON
53
+ * @param {Object} [options] - { layer } (name, index, or first tile layer)
54
+ * @returns {number[][]}
55
+ */
56
+ static toFrameGrid(map, options = {}) {
57
+ const layer = TiledMap._tileLayer(map, options.layer);
58
+ if (!layer) return [];
59
+ const cols = layer.width;
60
+ const rows = layer.height;
61
+ const data = layer.data || [];
62
+ const grid = [];
63
+ for (let r = 0; r < rows; r++) {
64
+ const row = [];
65
+ for (let c = 0; c < cols; c++) {
66
+ const raw = data[r * cols + c] || 0;
67
+ if (raw === 0) {
68
+ row.push(-1);
69
+ continue;
70
+ }
71
+ const gid = (raw & GID_MASK) >>> 0;
72
+ const ts = TiledMap.tilesetFor(map, gid);
73
+ row.push(ts ? gid - ts.firstgid : gid - 1);
74
+ }
75
+ grid.push(row);
76
+ }
77
+ return grid;
78
+ }
79
+
80
+ /**
81
+ * @method toTilemap
82
+ * @description Builds a ready-to-render Tilemap from a Tiled map. The tile
83
+ * sheet config (frame size, columns, count) is read from the map's first
84
+ * tileset; the render tile size comes from the map's tilewidth.
85
+ * @param {Object} map - Parsed Tiled map JSON
86
+ * @param {string} texturePath - Path to the tile sheet image
87
+ * @param {Object} [options] - { layer, pixelart = true, name, originX, originY }
88
+ * @returns {Tilemap}
89
+ */
90
+ static toTilemap(map, texturePath, options = {}) {
91
+ const ts = (map.tilesets || [])[0] || {};
92
+ const tilemap = new Tilemap(options.name || "tiledmap", texturePath, {
93
+ tileSize: options.tileSize ?? map.tilewidth ?? ts.tilewidth ?? 16,
94
+ frameWidth: ts.tilewidth ?? map.tilewidth ?? 16,
95
+ frameHeight: ts.tileheight ?? map.tileheight ?? 16,
96
+ framesPerRow: ts.columns ?? 1,
97
+ totalFrames: ts.tilecount ?? 1,
98
+ pixelart: options.pixelart ?? true,
99
+ });
100
+ const grid = TiledMap.toFrameGrid(map, options);
101
+ tilemap.setMap(grid, {
102
+ originX: options.originX ?? 0,
103
+ originY: options.originY ?? 0,
104
+ flipY: options.flipY ?? true,
105
+ });
106
+ return tilemap;
107
+ }
108
+
109
+ /**
110
+ * @method objects
111
+ * @description Extracts the objects from an object layer as plain data
112
+ * (spawn points, triggers, regions). Tiled `properties` arrays are flattened
113
+ * into a `props` object. With `flipY` set, y is converted from Tiled's
114
+ * top-left pixel origin to a y-up world using the map's pixel height.
115
+ * @param {Object} map - Parsed Tiled map JSON
116
+ * @param {Object} [options] - { layer, flipY = false, originX = 0, originY = 0 }
117
+ * @returns {Array<Object>}
118
+ */
119
+ static objects(map, options = {}) {
120
+ const groups = (map.layers || []).filter((l) => l.type === "objectgroup");
121
+ let group;
122
+ if (options.layer == null) group = groups[0];
123
+ else if (typeof options.layer === "number") group = groups[options.layer];
124
+ else group = groups.find((l) => l.name === options.layer);
125
+ if (!group) return [];
126
+
127
+ const flipY = options.flipY ?? false;
128
+ const originX = options.originX ?? 0;
129
+ const originY = options.originY ?? 0;
130
+ const mapPixelH = (map.height || 0) * (map.tileheight || 0);
131
+
132
+ return (group.objects || []).map((o) => {
133
+ const props = {};
134
+ if (Array.isArray(o.properties)) {
135
+ for (const p of o.properties) props[p.name] = p.value;
136
+ }
137
+ const worldY = flipY ? mapPixelH - o.y : o.y;
138
+ return {
139
+ id: o.id,
140
+ name: o.name || "",
141
+ type: o.type || o.class || "",
142
+ x: originX + o.x,
143
+ y: originY + worldY,
144
+ width: o.width || 0,
145
+ height: o.height || 0,
146
+ rotation: o.rotation || 0,
147
+ gid: o.gid != null ? (o.gid & GID_MASK) >>> 0 : null,
148
+ point: !!o.point,
149
+ ellipse: !!o.ellipse,
150
+ polygon: o.polygon || null,
151
+ props,
152
+ };
153
+ });
154
+ }
155
+ }
156
+
157
+ export { FLIP_FLAGS, GID_MASK };
158
+ export default TiledMap;
@@ -10,15 +10,14 @@ import { Vector2 } from "../Physics.js";
10
10
  */
11
11
  class DirectionalLight {
12
12
  constructor(position, direction, color, intensity, width) {
13
- this.position = position; // Vector2 - origin point of the light
14
- // Normalize the direction vector
13
+ this.position = position;
15
14
  const length = Math.sqrt(
16
15
  direction.x * direction.x + direction.y * direction.y
17
16
  );
18
- this.direction = new Vector2(direction.x / length, direction.y / length); // Vector2 - normalized direction vector
19
- this.color = color; // Color
20
- this.intensity = intensity; // Number
21
- this.width = width; // Number - width of the light beam
17
+ this.direction = new Vector2(direction.x / length, direction.y / length);
18
+ this.color = color;
19
+ this.intensity = intensity;
20
+ this.width = width;
22
21
  }
23
22
 
24
23
  /**
@@ -27,38 +26,30 @@ class DirectionalLight {
27
26
  * @returns {number} - Light intensity (0-1)
28
27
  */
29
28
  getIntensityAtPoint(point) {
30
-
31
- // Calculate distance from light position to point
32
29
  const dx = point.x - this.position.x;
33
30
  const dy = point.y - this.position.y;
34
31
  const distance = Math.sqrt(dx * dx + dy * dy);
35
32
 
36
- // Calculate the projection of the point onto the light direction
37
33
  const lightDirNormalized = new Vector2(this.direction.x, this.direction.y);
38
34
  const toPoint = new Vector2(dx, dy);
39
35
  const projection =
40
36
  toPoint.x * lightDirNormalized.x + toPoint.y * lightDirNormalized.y;
41
37
 
42
- // If point is behind the light, no illumination
43
38
  if (projection < 0) {
44
39
  return 0;
45
40
  }
46
41
 
47
- // Calculate perpendicular distance from the light beam center
48
42
  const perpDistance = Math.abs(
49
43
  toPoint.x * lightDirNormalized.y - toPoint.y * lightDirNormalized.x
50
44
  );
51
45
 
52
- // If point is outside the light beam width, no illumination
53
46
  if (perpDistance > this.width / 2) {
54
47
  return 0;
55
48
  }
56
49
 
57
- // Calculate intensity based on distance within the beam
58
50
  const widthFactor = 1 - perpDistance / (this.width / 2);
59
51
 
60
- // Optional: Add distance attenuation
61
- const maxDistance = 1000; // Maximum effective distance
52
+ const maxDistance = 1000;
62
53
  const distanceFactor = Math.max(0, 1 - distance / maxDistance);
63
54
 
64
55
  return this.intensity * widthFactor * distanceFactor;
@@ -7,10 +7,10 @@
7
7
  */
8
8
  class PointLight {
9
9
  constructor(position, color, intensity, radius) {
10
- this.position = position; // Vector2
11
- this.color = color; // Color
12
- this.intensity = intensity; // Number
13
- this.radius = radius; // Number
10
+ this.position = position;
11
+ this.color = color;
12
+ this.intensity = intensity;
13
+ this.radius = radius;
14
14
  }
15
15
  }
16
16
 
@@ -0,0 +1,239 @@
1
+ import TextureManager from "./TextureManager.js";
2
+
3
+ /**
4
+ * @class AssetManager
5
+ * @description A general async loader for everything a game needs at startup:
6
+ * images/textures, audio, JSON, text, and web fonts. It deduplicates by key,
7
+ * reports aggregate progress (so you can drive a loading bar), and stores the
8
+ * resolved assets for synchronous lookup afterwards via `get(key)`.
9
+ *
10
+ * Textures are routed through TextureManager so the GL upload cache is shared
11
+ * with the rest of the engine.
12
+ *
13
+ * @example
14
+ * const assets = new AssetManager();
15
+ * assets.image("player", "player.png", { pixelart: true });
16
+ * assets.audio("jump", "jump.wav");
17
+ * assets.json("level1", "levels/1.json");
18
+ * assets.font("Press Start 2P", "fonts/press-start.woff2");
19
+ * assets.onProgress((loaded, total) => bar.set(loaded / total));
20
+ * await assets.load();
21
+ * const img = assets.get("player"); // HTMLImageElement
22
+ * const data = assets.get("level1"); // parsed JSON
23
+ */
24
+ class AssetManager {
25
+ constructor() {
26
+ /** @private */
27
+ this._queue = new Map();
28
+ this.assets = new Map();
29
+ /** @private */
30
+ this._paths = new Map();
31
+ /** @private */
32
+ this._progressHandlers = [];
33
+ /** @private */
34
+ this._loaded = 0;
35
+ /** @private */
36
+ this._total = 0;
37
+ }
38
+
39
+ /**
40
+ * @method image
41
+ * @description Queues an image/texture. The GL texture is created lazily by
42
+ * the renderer; `get(key)` returns the HTMLImageElement.
43
+ */
44
+ image(key, path, options = {}) {
45
+ this._queue.set(key, { type: "image", path, pixelart: !!options.pixelart });
46
+ return this;
47
+ }
48
+
49
+ /**
50
+ * @method audio
51
+ * @description Queues an audio file (returns an HTMLAudioElement on get()).
52
+ */
53
+ audio(key, path) {
54
+ this._queue.set(key, { type: "audio", path });
55
+ return this;
56
+ }
57
+
58
+ /**
59
+ * @method json
60
+ * @description Queues a JSON file (parsed on get()).
61
+ */
62
+ json(key, path) {
63
+ this._queue.set(key, { type: "json", path });
64
+ return this;
65
+ }
66
+
67
+ /**
68
+ * @method text
69
+ * @description Queues a plain-text file.
70
+ */
71
+ text(key, path) {
72
+ this._queue.set(key, { type: "text", path });
73
+ return this;
74
+ }
75
+
76
+ /**
77
+ * @method font
78
+ * @description Queues a web font. `key` is the font-family name you will use
79
+ * in CSS/canvas; `path` is the font file URL.
80
+ */
81
+ font(key, path, descriptors = {}) {
82
+ this._queue.set(key, { type: "font", family: key, path, descriptors });
83
+ return this;
84
+ }
85
+
86
+ /**
87
+ * @method onProgress
88
+ * @description Registers a callback invoked as (loaded, total) after each
89
+ * asset resolves.
90
+ */
91
+ onProgress(handler) {
92
+ if (typeof handler === "function") this._progressHandlers.push(handler);
93
+ return this;
94
+ }
95
+
96
+ /**
97
+ * @method progress
98
+ * @returns {number} - Fraction loaded in [0, 1] (1 when nothing is queued).
99
+ */
100
+ progress() {
101
+ return this._total === 0 ? 1 : this._loaded / this._total;
102
+ }
103
+
104
+ /**
105
+ * @method load
106
+ * @description Loads every queued asset, resolving when all are done. Failed
107
+ * assets reject the returned promise unless `continueOnError` is true, in
108
+ * which case they resolve to null and loading continues.
109
+ * @param {Object} [options] - { continueOnError = false }
110
+ * @returns {Promise<Map>} - The resolved assets map
111
+ */
112
+ async load(options = {}) {
113
+ const { continueOnError = false } = options;
114
+ const entries = Array.from(this._queue.entries());
115
+ this._total = entries.length;
116
+ this._loaded = 0;
117
+
118
+ await Promise.all(
119
+ entries.map(async ([key, spec]) => {
120
+ try {
121
+ const asset = await this._loadOne(spec);
122
+ this.assets.set(key, asset);
123
+ if (spec.type === "image") {
124
+ this._paths.set(key, { path: spec.path, pixelart: spec.pixelart });
125
+ }
126
+ } catch (err) {
127
+ if (!continueOnError) throw err;
128
+ console.warn(`[AssetManager] Failed to load "${key}":`, err);
129
+ this.assets.set(key, null);
130
+ } finally {
131
+ this._loaded++;
132
+ for (const handler of this._progressHandlers) {
133
+ handler(this._loaded, this._total);
134
+ }
135
+ }
136
+ })
137
+ );
138
+
139
+ this._queue.clear();
140
+ return this.assets;
141
+ }
142
+
143
+ /** @private */
144
+ _loadOne(spec) {
145
+ switch (spec.type) {
146
+ case "image":
147
+ return TextureManager.loadImage(spec.path);
148
+ case "audio":
149
+ return AssetManager._loadAudio(spec.path);
150
+ case "json":
151
+ return fetch(spec.path).then((r) => {
152
+ if (!r.ok) throw new Error(`HTTP ${r.status} for ${spec.path}`);
153
+ return r.json();
154
+ });
155
+ case "text":
156
+ return fetch(spec.path).then((r) => {
157
+ if (!r.ok) throw new Error(`HTTP ${r.status} for ${spec.path}`);
158
+ return r.text();
159
+ });
160
+ case "font":
161
+ return AssetManager._loadFont(spec);
162
+ default:
163
+ return Promise.reject(new Error(`Unknown asset type: ${spec.type}`));
164
+ }
165
+ }
166
+
167
+ /** @private */
168
+ static _loadAudio(path) {
169
+ return new Promise((resolve, reject) => {
170
+ const audio = new Audio();
171
+ audio.addEventListener("canplaythrough", () => resolve(audio), {
172
+ once: true,
173
+ });
174
+ audio.addEventListener("error", () =>
175
+ reject(new Error(`Failed to load audio: ${path}`))
176
+ );
177
+ audio.src = path;
178
+ audio.load();
179
+ });
180
+ }
181
+
182
+ /** @private */
183
+ static async _loadFont(spec) {
184
+ if (typeof FontFace === "undefined" || !document.fonts) {
185
+ return null;
186
+ }
187
+ const face = new FontFace(
188
+ spec.family,
189
+ `url(${spec.path})`,
190
+ spec.descriptors
191
+ );
192
+ const loaded = await face.load();
193
+ document.fonts.add(loaded);
194
+ return loaded;
195
+ }
196
+
197
+ /**
198
+ * @method get
199
+ * @description Returns a previously loaded asset by key.
200
+ */
201
+ get(key) {
202
+ return this.assets.get(key);
203
+ }
204
+
205
+ /**
206
+ * @method has
207
+ * @returns {boolean} - Whether an asset has been loaded for the key.
208
+ */
209
+ has(key) {
210
+ return this.assets.has(key);
211
+ }
212
+
213
+ /**
214
+ * @method getTexture
215
+ * @description Convenience: returns the cached GL texture for an image asset
216
+ * that was queued with `image()`. Resolves once the upload completes.
217
+ * @param {string} key - The image asset key
218
+ * @returns {Promise<{texture: WebGLTexture, width: number, height: number}>}
219
+ */
220
+ getTexture(key) {
221
+ const info = this._paths.get(key);
222
+ if (!info) return Promise.reject(new Error(`No image asset "${key}"`));
223
+ return TextureManager.getTexture(info.path, info.pixelart);
224
+ }
225
+
226
+ /**
227
+ * @method clear
228
+ * @description Forgets all loaded assets (does not free GL textures; use
229
+ * TextureManager.clear for that).
230
+ */
231
+ clear() {
232
+ this.assets.clear();
233
+ this._queue.clear();
234
+ this._loaded = 0;
235
+ this._total = 0;
236
+ }
237
+ }
238
+
239
+ export default AssetManager;