@dryanovski/gamefoo 0.0.1 → 0.2.1

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 (75) hide show
  1. package/dist/core/animate.d.ts +129 -0
  2. package/dist/core/animate.d.ts.map +1 -0
  3. package/dist/core/asset.d.ts +59 -0
  4. package/dist/core/asset.d.ts.map +1 -0
  5. package/dist/core/behaviour.d.ts +118 -0
  6. package/dist/core/behaviour.d.ts.map +1 -1
  7. package/dist/core/behaviours/collidable.d.ts +203 -4
  8. package/dist/core/behaviours/collidable.d.ts.map +1 -1
  9. package/dist/core/behaviours/control.d.ts +51 -3
  10. package/dist/core/behaviours/control.d.ts.map +1 -1
  11. package/dist/core/behaviours/healtkit.d.ts +120 -3
  12. package/dist/core/behaviours/healtkit.d.ts.map +1 -1
  13. package/dist/core/behaviours/sprite_render.d.ts +141 -0
  14. package/dist/core/behaviours/sprite_render.d.ts.map +1 -0
  15. package/dist/core/camera.d.ts +101 -0
  16. package/dist/core/camera.d.ts.map +1 -1
  17. package/dist/core/engine.d.ts +377 -15
  18. package/dist/core/engine.d.ts.map +1 -1
  19. package/dist/core/fonts/font_bitmap.d.ts +156 -0
  20. package/dist/core/fonts/font_bitmap.d.ts.map +1 -0
  21. package/dist/core/fonts/font_bitmap_prebuild.d.ts +102 -0
  22. package/dist/core/fonts/font_bitmap_prebuild.d.ts.map +1 -0
  23. package/dist/core/fonts/internal/font_3x5.d.ts +76 -0
  24. package/dist/core/fonts/internal/font_3x5.d.ts.map +1 -0
  25. package/dist/core/fonts/internal/font_4x6.d.ts +76 -0
  26. package/dist/core/fonts/internal/font_4x6.d.ts.map +1 -0
  27. package/dist/core/fonts/internal/font_5x5.d.ts +79 -0
  28. package/dist/core/fonts/internal/font_5x5.d.ts.map +1 -0
  29. package/dist/core/fonts/internal/font_6x8.d.ts +76 -0
  30. package/dist/core/fonts/internal/font_6x8.d.ts.map +1 -0
  31. package/dist/core/fonts/internal/font_8x13.d.ts +76 -0
  32. package/dist/core/fonts/internal/font_8x13.d.ts.map +1 -0
  33. package/dist/core/fonts/internal/font_8x8.d.ts +76 -0
  34. package/dist/core/fonts/internal/font_8x8.d.ts.map +1 -0
  35. package/dist/core/game_object_register.d.ts +101 -1
  36. package/dist/core/game_object_register.d.ts.map +1 -1
  37. package/dist/core/input.d.ts +131 -0
  38. package/dist/core/input.d.ts.map +1 -1
  39. package/dist/core/sprite.d.ts +232 -0
  40. package/dist/core/sprite.d.ts.map +1 -0
  41. package/dist/core/utils/perlin_noise.d.ts +136 -0
  42. package/dist/core/utils/perlin_noise.d.ts.map +1 -0
  43. package/dist/core/world.d.ts +147 -0
  44. package/dist/core/world.d.ts.map +1 -1
  45. package/dist/debug/monitor.d.ts +12 -0
  46. package/dist/debug/monitor.d.ts.map +1 -0
  47. package/dist/decorators/index.d.ts +2 -0
  48. package/dist/decorators/index.d.ts.map +1 -0
  49. package/dist/decorators/log.d.ts +33 -0
  50. package/dist/decorators/log.d.ts.map +1 -0
  51. package/dist/entities/dynamic_entity.d.ts +82 -0
  52. package/dist/entities/dynamic_entity.d.ts.map +1 -1
  53. package/dist/entities/entity.d.ts +216 -11
  54. package/dist/entities/entity.d.ts.map +1 -1
  55. package/dist/entities/player.d.ts +76 -0
  56. package/dist/entities/player.d.ts.map +1 -1
  57. package/dist/entities/text.d.ts +52 -0
  58. package/dist/entities/text.d.ts.map +1 -0
  59. package/dist/index.d.ts +29 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +23 -604
  62. package/dist/index.js.map +3 -15
  63. package/dist/subsystems/camera_system.d.ts +25 -0
  64. package/dist/subsystems/camera_system.d.ts.map +1 -0
  65. package/dist/subsystems/collision_system.d.ts +16 -0
  66. package/dist/subsystems/collision_system.d.ts.map +1 -0
  67. package/dist/subsystems/monitor_system.d.ts +17 -0
  68. package/dist/subsystems/monitor_system.d.ts.map +1 -0
  69. package/dist/subsystems/object_system.d.ts +18 -0
  70. package/dist/subsystems/object_system.d.ts.map +1 -0
  71. package/dist/subsystems/types.d.ts +40 -0
  72. package/dist/subsystems/types.d.ts.map +1 -0
  73. package/dist/types.d.ts +140 -0
  74. package/dist/types.d.ts.map +1 -1
  75. package/package.json +25 -11
@@ -1,39 +1,401 @@
1
- import type Player from "../entities/player";
2
- import type { GameObject } from "../types";
3
- import World from "./world";
1
+ import type { SubSystem } from "@/subsystems/types";
2
+ /**
3
+ * Configuration options for the {@link Engine}.
4
+ *
5
+ * Passed to the `Engine` constructor to customise visual defaults.
6
+ * Every property is optional; sensible defaults are applied internally.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const config: EngineConfig = {
11
+ * backgroundColor: "#1a1a2e",
12
+ * };
13
+ *
14
+ * const engine = new Engine("game", 800, 600, config);
15
+ * ```
16
+ */
4
17
  interface EngineConfig {
18
+ /**
19
+ * The CSS colour string used to clear the canvas each frame.
20
+ *
21
+ * Accepts any value valid for {@link CanvasRenderingContext2D.fillStyle}
22
+ * (hex, `rgb()`, `hsl()`, named colours, etc.).
23
+ *
24
+ * @defaultValue `"#000000"`
25
+ */
5
26
  backgroundColor?: string;
27
+ /**
28
+ * Global scale factor applied to the canvas via CSS transforms.
29
+ *
30
+ * @defaultValue `1` (no scaling)
31
+ */
32
+ gameScale?: number;
6
33
  }
7
34
  /**
8
- * Game Engine - the main class that manages the game loop, rendering, and overall game state.
35
+ * Core game engine responsible for the game loop, rendering pipeline,
36
+ * entity management, collision detection, and camera tracking.
37
+ *
38
+ * `Engine` owns a single HTML `<canvas>` element and drives a
39
+ * `requestAnimationFrame`-based loop that, on every tick:
40
+ *
41
+ * 1. Computes **deltaTime** (seconds since the previous frame).
42
+ * 2. Calls {@link Engine.update | update} — advances all entities and runs
43
+ * collision detection via the internal {@link World}.
44
+ * 3. Calls {@link Engine.render | render} — clears the canvas and draws every
45
+ * registered entity.
46
+ *
47
+ * ---
48
+ *
49
+ * ### Lifecycle
50
+ *
51
+ * ```text
52
+ * new Engine() — creates canvas context, camera, object register, world
53
+ * │
54
+ * ▼
55
+ * setup(fn) — runs the user-supplied initialiser, then starts the loop
56
+ * │
57
+ * ▼
58
+ * ┌─► loop(timestamp) — called every frame via requestAnimationFrame
59
+ * │ ├─ update(dt)
60
+ * │ └─ render()
61
+ * │ │
62
+ * └─────────┘
63
+ * │
64
+ * pause() / clear() / destroy() — stops or tears down the engine
65
+ * ```
66
+ *
67
+ * ---
68
+ *
69
+ * ### Minimal example
70
+ *
71
+ * ```ts
72
+ * import { Engine, Player } from "gamefoo";
73
+ *
74
+ * const engine = new Engine("game", 800, 600, {
75
+ * backgroundColor: "#1a1a2e",
76
+ * });
77
+ *
78
+ * const player = new Player("hero", 400, 300, 50, 50);
79
+ * engine.player = player;
80
+ *
81
+ * engine.setup(() => {
82
+ * console.log("Game started!");
83
+ * });
84
+ * ```
85
+ *
86
+ * ### Adding game objects
87
+ *
88
+ * ```ts
89
+ * import { Engine, DynamicEntity } from "gamefoo";
90
+ *
91
+ * const engine = new Engine("game", 800, 600, {});
92
+ *
93
+ * class Crate extends DynamicEntity {
94
+ * constructor(x: number, y: number) {
95
+ * super("crate", x, y, 32, 32);
96
+ * }
97
+ * override update(_dt: number) {}
98
+ * override render(ctx: CanvasRenderingContext2D) {
99
+ * ctx.fillStyle = "#8B4513";
100
+ * ctx.fillRect(this.x, this.y, 32, 32);
101
+ * }
102
+ * }
103
+ *
104
+ * engine.attachObjects(new Crate(200, 150));
105
+ *
106
+ * engine.setup(() => {
107
+ * console.log("Crate placed!");
108
+ * });
109
+ * ```
110
+ *
111
+ * @see {@link Camera} — viewport tracking
112
+ * @see {@link GameObjectRegister} — entity storage
113
+ * @see {@link World} — collision detection
9
114
  */
10
115
  export default class Engine {
116
+ /**
117
+ * The underlying `<canvas>` DOM element retrieved by its `id` during
118
+ * construction.
119
+ */
11
120
  private canvas;
121
+ /**
122
+ * The 2-D rendering context obtained from {@link Engine.canvas}.
123
+ * Used by {@link Engine.render} and exposed indirectly to game objects.
124
+ */
12
125
  private ctx;
126
+ /**
127
+ * Timestamp (in milliseconds) of the previous animation frame.
128
+ * Used internally to calculate `deltaTime` between frames.
129
+ *
130
+ * Reset to `0` when the engine is set up via {@link Engine.setup}.
131
+ */
13
132
  private lastTime;
133
+ /**
134
+ * The logical width of the game world (and the canvas), in pixels.
135
+ *
136
+ * Set once during construction and also used by the {@link Camera}.
137
+ */
14
138
  private width;
139
+ /**
140
+ * The logical height of the game world (and the canvas), in pixels.
141
+ *
142
+ * Set once during construction and also used by the {@link Camera}.
143
+ */
15
144
  private height;
145
+ /**
146
+ * Guards against calling {@link Engine.setup} more than once.
147
+ * Flipped to `true` after the first successful setup invocation.
148
+ */
16
149
  private _initialized;
150
+ /**
151
+ * Whether the game loop is actively requesting new frames.
152
+ *
153
+ * - Set to `true` inside {@link Engine.setup}.
154
+ * - Set to `false` by {@link Engine.pause} or {@link Engine.clear}.
155
+ */
17
156
  private running;
157
+ /**
158
+ * Merged engine configuration. Combines user-supplied values with internal
159
+ * defaults (`backgroundColor: "#000000"`).
160
+ */
18
161
  private cnf;
19
- private _player?;
20
- private engine;
162
+ /**
163
+ * Global scale factor applied to the canvas via CSS transforms.
164
+ *
165
+ * This does not affect the internal resolution (`width` / `height`) or the
166
+ * camera viewport, but simply scales the rendered output for display.
167
+ *
168
+ */
169
+ gameScale: number;
170
+ /**
171
+ * The main subsystems of the engine, responsible for core functionalities
172
+ * like rendering, object management, collision detection, and monitoring.
173
+ *
174
+ * Subsystems are executed in a specific order determined by their `order` property
175
+ */
176
+ private subsystems;
177
+ /**
178
+ * Creates a new `Engine` instance, binds it to a `<canvas>` element,
179
+ * and initialises the camera, object register, and collision world.
180
+ *
181
+ * @param canvasId - The DOM `id` attribute of the `<canvas>` element to
182
+ * render into. Must already exist in the document.
183
+ * @param width - Logical width of the game area in pixels. Sets both
184
+ * `canvas.width` and the camera viewport width.
185
+ * @param height - Logical height of the game area in pixels. Sets both
186
+ * `canvas.height` and the camera viewport height.
187
+ * @param config - Optional overrides for engine-level settings.
188
+ * See {@link EngineConfig}.
189
+ *
190
+ * @throws {Error} If no 2-D rendering context can be obtained from the
191
+ * canvas (e.g. the browser does not support Canvas 2D).
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const engine = new Engine("game", 800, 600, {
196
+ * backgroundColor: "#1a1a2e",
197
+ * });
198
+ * ```
199
+ */
21
200
  constructor(canvasId: string, width: number, height: number, config: EngineConfig);
22
- set player(player: Player);
23
- get player(): Player | undefined;
24
- get collisions(): World;
25
- attachObjects(objects: GameObject): void;
201
+ /**
202
+ * Attaches a subsystem to the engine, making it part of the update and render cycles.
203
+ *
204
+ * Subsystems are executed in a specific order determined by their `order` property (lower values run first).
205
+ *
206
+ * @since 0.2.0
207
+ *
208
+ * @param subsystem - The subsystem instance to attach. Must implement the {@link SubSystem} interface.
209
+ *
210
+ * @returns The engine instance, allowing for method chaining.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * engine.use(new ObjectSystem());
215
+ * engine.use(new CollisionSystem());
216
+ * ```
217
+ */
218
+ use(subsystem: SubSystem): this;
219
+ /**
220
+ * Runs a specific lifecycle hook on all enabled subsystems that implement it.
221
+ *
222
+ * This method is used internally by the game loop to delegate update and render calls to subsystems.
223
+ *
224
+ * @param hook - The name of the lifecycle method to invoke (e.g. "update", "render").
225
+ * @param args - Arguments to pass to the subsystem methods (e.g. `deltaTime` for updates, `ctx` for rendering).
226
+ *
227
+ * @remarks
228
+ * The method iterates over all subsystems, checks if they are enabled, and if they implement the specified hook. If so, it calls the hook method with the provided arguments.
229
+ * This allows for a flexible and modular architecture where subsystems can independently implement the lifecycle methods they need without being tightly coupled to the engine's core logic.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * // During the update phase of the game loop:
234
+ * this.run("update", deltaTime);
235
+ *
236
+ * // During the render phase of the game loop:
237
+ * this.run("render", ctx);
238
+ * ```
239
+ */
240
+ private run;
241
+ /**
242
+ * Resizes the canvas via CSS transforms so it fits within its parent
243
+ * container while preserving the original aspect ratio.
244
+ *
245
+ * Call this in a `window` `resize` event listener to keep the game
246
+ * centred and properly scaled in responsive layouts.
247
+ *
248
+ * @remarks
249
+ * The method calculates the uniform scale factor from the container
250
+ * dimensions and centres the canvas with a CSS `translate + scale`
251
+ * transform. The internal resolution (`width` / `height`) remains
252
+ * unchanged.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * window.addEventListener("resize", () => engine.handleResize());
257
+ * ```
258
+ */
26
259
  handleResize(): void;
260
+ /**
261
+ * Directly sets the canvas pixel dimensions.
262
+ *
263
+ * Unlike {@link Engine.handleResize}, this changes the **actual**
264
+ * resolution of the canvas (clearing its contents in the process).
265
+ *
266
+ * @param width - New canvas width in pixels.
267
+ * @param height - New canvas height in pixels.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * engine.resize(1024, 768);
272
+ * ```
273
+ */
27
274
  resize(width: number, height: number): void;
275
+ /**
276
+ * Binds the `loop` method to the current `this` context so it can be passed directly to
277
+ * `requestAnimationFrame` without losing the correct reference to the engine instance.
278
+ *
279
+ * Also prevent the need of GC to create a new function on each frame, which could lead to performance issues over time.
280
+ */
281
+ private boundLoop;
282
+ /**
283
+ * The core animation loop driven by `requestAnimationFrame`.
284
+ *
285
+ * Each iteration:
286
+ * 1. Computes **deltaTime** (seconds since the last frame).
287
+ * 2. Delegates to {@link Engine.update} and {@link Engine.render}.
288
+ * 3. Schedules itself for the next frame (unless `running` is `false`).
289
+ *
290
+ * @param timestamp - The high-resolution timestamp provided by
291
+ * `requestAnimationFrame`, in milliseconds.
292
+ *
293
+ * @internal
294
+ */
28
295
  private loop;
29
296
  /**
30
- * Public - cause I'm old school
297
+ * Initialises the engine and starts the game loop.
298
+ *
299
+ * The supplied `setupFn` callback is invoked **synchronously** before the
300
+ * first frame. Use it to perform any last-minute setup that depends on
301
+ * the engine being ready (e.g. spawning initial entities, binding UI).
302
+ *
303
+ * Calling `setup` more than once is a no-op — a warning is logged to the
304
+ * console and the method returns immediately.
305
+ *
306
+ * @param setupFn - (optional) A synchronous callback executed once before the loop
307
+ * begins. Typically used for scene initialisation.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * engine.setup(() => {
312
+ * console.log("Engine initialised — first frame incoming!");
313
+ * });
314
+ * ```
315
+ *
316
+ * @example
317
+ * ```ts
318
+ * // Attempting a second setup is safely ignored:
319
+ * engine.setup(() => {}); // warns: "Engine is already initialized."
320
+ * ```
321
+ */
322
+ setup(setupFn?: () => void): Promise<void>;
323
+ /**
324
+ * Advances the game state by one tick.
325
+ *
326
+ * Called automatically by the game loop, but is `public` so it can be
327
+ * invoked manually for deterministic / test-driven updates.
328
+ *
329
+ *
330
+ * @param deltaTime - Time elapsed since the last frame, **in seconds**.
331
+ *
332
+ * @example
333
+ * ```ts
334
+ * // Manual update (useful for unit testing):
335
+ * engine.update(1 / 60); // simulate a single 60 FPS tick
336
+ * ```
337
+ */
338
+ update(_deltaTime: number): void;
339
+ /**
340
+ * Draws one complete frame to the canvas.
341
+ *
342
+ * Called automatically after {@link Engine.update} in the game loop, but
343
+ * is `public` for manual / debug rendering.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * // Force a single frame repaint:
348
+ * engine.render((ctx) => {
349
+ * ctx.fillStyle = "red";
350
+ * });
351
+ * ```
352
+ */
353
+ render(_ctx: CanvasRenderingContext2D): void;
354
+ /**
355
+ * Pauses the game loop.
356
+ *
357
+ * The current frame finishes, but no further frames are scheduled.
358
+ * The canvas retains its last rendered state.
359
+ *
360
+ * Resume by calling {@link Engine.setup} on a **new** engine instance
361
+ * (the current instance cannot be restarted after pausing because
362
+ * `_initialized` remains `true`).
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * document.addEventListener("visibilitychange", () => {
367
+ * if (document.hidden) engine.pause();
368
+ * });
369
+ * ```
31
370
  */
32
- setup(setupFn: () => void): Promise<void>;
33
- update(deltaTime: number): void;
34
- render(): void;
35
371
  pause(): void;
36
- clear(): void;
372
+ /**
373
+ * Clear the screen and set default background colour.
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * engine.clearScrean();
378
+ * // Canvas is now blank; loop is stopped.
379
+ * ```
380
+ */
381
+ clearScrean(): void;
382
+ /**
383
+ * Tears down the engine and releases resources.
384
+ *
385
+ * Use this to perform any cleanup such as removing event listeners or
386
+ * releasing external references when the engine is no longer needed.
387
+ *
388
+ * @remarks
389
+ * Currently a no-op placeholder. Extend this method when the engine
390
+ * acquires resources that need explicit cleanup (e.g. `ResizeObserver`,
391
+ * `WebSocket`, audio contexts).
392
+ *
393
+ * @example
394
+ * ```ts
395
+ * // When leaving the game screen:
396
+ * engine.destroy();
397
+ * ```
398
+ */
37
399
  destroy(): void;
38
400
  }
39
401
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../core/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,UAAU,YAAY;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,GAAG,CAA2B;IAEtC,OAAO,CAAC,QAAQ,CAAa;IAE7B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,OAAO,CAAkB;IAEjC,OAAO,CAAC,GAAG,CAET;IAEF,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,OAAO,CAAC,MAAM,CAIZ;gBAEU,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY;IA6BjF,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAExB;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,IAAI,UAAU,IAAI,KAAK,CAEtB;IAEM,aAAa,CAAC,OAAO,EAAE,UAAU;IAIxC,YAAY;IAkBZ,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAK3C,OAAO,CAAC,IAAI;IAiBZ;;OAEG;IAEU,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI;IAiB/B,MAAM,CAAC,SAAS,EAAE,MAAM;IAoBxB,MAAM;IAcN,KAAK;IAIL,KAAK;IAKL,OAAO;CAGf"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAIpD;;;;;;;;;;;;;;GAcG;AACH,UAAU,YAAY;IACpB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAoB;IAElC;;;OAGG;IACH,OAAO,CAAC,GAAG,CAA2B;IAEtC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAa;IAE7B;;;;OAIG;IACH,OAAO,CAAC,KAAK,CAAS;IAEtB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAS;IAEvB;;;OAGG;IACH,OAAO,CAAC,YAAY,CAAkB;IAEtC;;;;;OAKG;IACH,OAAO,CAAC,OAAO,CAAkB;IAEjC;;;OAGG;IACH,OAAO,CAAC,GAAG,CAOT;IAEF;;;;;;OAMG;IACI,SAAS,EAAE,MAAM,CAAsB;IAE9C;;;;;OAKG;IACH,OAAO,CAAC,UAAU,CAAmB;IAErC;;;;;;;;;;;;;;;;;;;;;;OAsBG;gBACS,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY;IA2BjF;;;;;;;;;;;;;;;;OAgBG;IACH,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAgB/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,GAAG;IAeX;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY;IAkBZ;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAK3C;;;;;OAKG;IACH,OAAO,CAAC,SAAS,CAAwB;IAEzC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IA+DZ;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACU,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI;IA8BvC;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAC,UAAU,EAAE,MAAM;IAEhC;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,IAAI,EAAE,wBAAwB;IAE5C;;;;;;;;;;;;;;;;OAgBG;IACI,KAAK;IAIZ;;;;;;;;OAQG;IACI,WAAW;IAMlB;;;;;;;;;;;;;;;;OAgBG;IACI,OAAO;CAQf"}
@@ -0,0 +1,156 @@
1
+ export type InternalBitmapFontName = "3x5" | "4x6" | "5x5" | "6x8" | "8x13" | "8x8";
2
+ /**
3
+ * Pixel-perfect bitmap font renderer.
4
+ *
5
+ * `FontBitmap` looks up a named font from the built-in catalogue,
6
+ * then renders individual characters or whole strings onto a
7
+ * `CanvasRenderingContext2D` one pixel at a time using `fillRect`.
8
+ *
9
+ * Each character is stored as an array of row bitmasks where each bit
10
+ * represents a single pixel.
11
+ *
12
+ * @category Fonts
13
+ * @since 0.1.0
14
+ *
15
+ * @example Rendering text with the built-in 5x5 font
16
+ * ```ts
17
+ * import { FontBitmap } from "gamefoo";
18
+ *
19
+ * const font = new FontBitmap("5x5");
20
+ *
21
+ * ctx.fillStyle = "#ffffff";
22
+ * font.renderText("HELLO WORLD", 10, 10, ctx);
23
+ * ```
24
+ *
25
+ * @example Measuring text width
26
+ * ```ts
27
+ * const font = new FontBitmap("5x5");
28
+ * const width = font.getTextWidth("SCORE 999");
29
+ * // width === 9 * 6 = 54 (each char is 5px + 1px spacing)
30
+ * ```
31
+ *
32
+ * @see {@link FONT_5x5} — the built-in 5x5 pixel font data
33
+ */
34
+ export default class FontBitmap {
35
+ /** The catalogue name of the loaded font (e.g. `"5x5"`). */
36
+ readonly name: string;
37
+ /**
38
+ * Character bitmask data keyed by character string.
39
+ *
40
+ * Each value is an array of integers where each integer represents
41
+ * one row of pixels (MSB = leftmost pixel).
42
+ */
43
+ protected readonly data: Record<string, number[]>;
44
+ /**
45
+ * Character cell width in pixels (including spacing).
46
+ *
47
+ * @defaultValue `0` (populated from catalogue on construction)
48
+ */
49
+ width: number;
50
+ /**
51
+ * Character cell height in pixels.
52
+ *
53
+ * @defaultValue `0` (populated from catalogue on construction)
54
+ */
55
+ height: number;
56
+ /**
57
+ * Horizontal spacing between the drawable area and the full cell
58
+ * width, in pixels.
59
+ *
60
+ * @defaultValue `0`
61
+ */
62
+ protected spacing: number;
63
+ /**
64
+ * Creates a font renderer for the named catalogue entry.
65
+ *
66
+ * If the name does not match any registered font the instance will
67
+ * have empty data and zero dimensions — calls to `renderChar` /
68
+ * `renderText` will be no-ops.
69
+ *
70
+ * @param name - The catalogue key (e.g. `"5x5"`).
71
+ *
72
+ * @throws Error if the name is not found in the catalogue.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const font = new FontBitmap("5x5");
77
+ * ```
78
+ */
79
+ constructor(name: InternalBitmapFontName);
80
+ /**
81
+ * Returns the raw catalogue entry for this font, or `null` if the
82
+ * font name was not found.
83
+ *
84
+ * @returns Font metadata object or `null`.
85
+ */
86
+ get metadata(): {
87
+ name: string;
88
+ width: number;
89
+ height: number;
90
+ chars: string;
91
+ spacing: number;
92
+ data: Record<string, number[]>;
93
+ } | null;
94
+ /**
95
+ * Retrieves the bitmask rows for a single character.
96
+ *
97
+ * @param char - A single character string (e.g. `"A"`).
98
+ * @returns An array of row bitmasks, or `null` if the character is
99
+ * not defined in this font.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const rows = font.getChar("A");
104
+ * // rows → [14, 17, 31, 17, 17] for the 5x5 font
105
+ * ```
106
+ */
107
+ getChar(char: string): number[] | null;
108
+ /**
109
+ * Computes the pixel width required to render the given text string.
110
+ *
111
+ * @param text - The string to measure.
112
+ * @returns Width in pixels (`text.length * cellWidth`).
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * const w = font.getTextWidth("HI"); // 12 for the 5x5 font
117
+ * ```
118
+ */
119
+ getTextWidth(text: string): number;
120
+ /**
121
+ * Renders a single character at the given pixel position.
122
+ *
123
+ * Each set bit in the character's row bitmask produces a 1x1
124
+ * `fillRect` call using the context's current `fillStyle`.
125
+ *
126
+ * @param char - The character to draw.
127
+ * @param x - Left edge X coordinate in canvas pixels.
128
+ * @param y - Top edge Y coordinate in canvas pixels.
129
+ * @param ctx - The 2-D rendering context to draw into.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * ctx.fillStyle = "#00ff00";
134
+ * font.renderChar("G", 20, 40, ctx);
135
+ * ```
136
+ */
137
+ renderChar(char: string, x: number, y: number, ctx: CanvasRenderingContext2D): void;
138
+ /**
139
+ * Renders a full text string by drawing each character sequentially.
140
+ *
141
+ * Characters are spaced according to the font's cell width.
142
+ *
143
+ * @param text - The string to render.
144
+ * @param x - Left edge X coordinate of the first character.
145
+ * @param y - Top edge Y coordinate.
146
+ * @param ctx - The 2-D rendering context.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * ctx.fillStyle = "#ffffff";
151
+ * font.renderText("GAME OVER", 100, 50, ctx);
152
+ * ```
153
+ */
154
+ renderText(text: string, x: number, y: number, ctx: CanvasRenderingContext2D): void;
155
+ }
156
+ //# sourceMappingURL=font_bitmap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font_bitmap.d.ts","sourceRoot":"","sources":["../../../src/core/fonts/font_bitmap.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B,4DAA4D;IAC5D,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAElD;;;;OAIG;IACI,KAAK,EAAE,MAAM,CAAK;IAEzB;;;;OAIG;IACI,MAAM,EAAE,MAAM,CAAK;IAE1B;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,CAAK;IAE9B;;;;;;;;;;;;;;;OAeG;gBACS,IAAI,EAAE,sBAAsB;IAgBxC;;;;;OAKG;IACH,IAAI,QAAQ;cA1HJ,MAAM;eACL,MAAM;gBACL,MAAM;eACP,MAAM;iBACJ,MAAM;cACT,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;aAuH/B;IAED;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAItC;;;;;;;;;;OAUG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIlC;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB;IAe5E;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB;CAO7E"}
@@ -0,0 +1,102 @@
1
+ import FontBitmap from "./font_bitmap";
2
+ /**
3
+ * Bitmap font renderer that pre-builds character paths for faster rendering and
4
+ * reduced CPU usage at the cost of increased memory usage.
5
+ *
6
+ * GC pressure is reduced by pre-building `Path2D` objects for each character,
7
+ * which are then reused on subsequent renders. This can significantly improve
8
+ * performance when rendering the same characters multiple times, such as in a
9
+ * game UI or repeated text elements.
10
+ *
11
+ * Build on top of {@link FontBitmap}, `FontBitmapPrebuild` overrides the character rendering
12
+ *
13
+ * @category Fonts
14
+ * @since 0.2.0
15
+ *
16
+ * @example Pre-building glyphs for a menu
17
+ *
18
+ * ```ts
19
+ * import { FontBitmapPrebuild } from "gamefoo";
20
+ *
21
+ * const font = new FontBitmapPrebuild("5x5");
22
+ * font.prebuildGlyphs("PLAY SETTINGS EXIT".split(""));
23
+ *
24
+ * // Later in the render loop
25
+ * ctx.fillStyle = "#ffffff";
26
+ * font.renderText("PLAY", 10, 10, ctx);
27
+ * font.renderText("SETTINGS", 10, 20, ctx);
28
+ * font.renderText("EXIT", 10, 30, ctx);
29
+ * ```
30
+ *
31
+ * @example Performance comparison
32
+ *
33
+ * ```ts
34
+ * import { FontBitmap, FontBitmapPrebuild } from "gamefoo";
35
+ *
36
+ * const fontStandard = new FontBitmap("5x5");
37
+ * const fontPrebuild = new FontBitmapPrebuild("5x5");
38
+ * fontPrebuild.prebuildGlyphs("ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""));
39
+ *
40
+ * // In the render loop, measure time taken to render a string multiple times
41
+ * console.time("Standard Bitmap");
42
+ * for (let i = 0; i < 1000; i++) {
43
+ * fontStandard.renderText("HELLO WORLD", 10, 10, ctx);
44
+ * }
45
+ * console.timeEnd("Standard Bitmap");
46
+ *
47
+ * console.time("Prebuilt Bitmap");
48
+ * for (let i = 0; i < 1000; i++) {
49
+ * fontPrebuild.renderText("HELLO WORLD", 10, 10, ctx);
50
+ * }
51
+ * console.timeEnd("Prebuilt Bitmap");
52
+ * ```
53
+ *
54
+ * @remarks
55
+ *
56
+ * Pre-building glyphs can significantly improve rendering performance when
57
+ * the same characters are rendered multiple times, as it avoids the overhead
58
+ * of constructing `Path2D` objects on each render. However, it does increase
59
+ * memory usage, especially if a large number of unique characters are pre-built.
60
+ * Use this class when you have a known set of characters that will be rendered
61
+ * frequently, such as in a game UI or static text elements.
62
+ *
63
+ * @see FontBitmap for a more memory-efficient alternative that builds paths on demand.
64
+ *
65
+ */
66
+ export default class FontBitmapPrebuild extends FontBitmap {
67
+ /**
68
+ * Map of pre-built `Path2D` objects for each character. Keys are characters,
69
+ * values are their corresponding paths.
70
+ * This cache is used to store pre-built paths for characters that have been
71
+ * rendered at least once, allowing for faster rendering on subsequent calls.
72
+ */
73
+ private glyphPaths;
74
+ /**
75
+ * Builds a `Path2D` object for the specified character based on its bitmap data.
76
+ * This method reads the character's bitmap data and constructs a path that
77
+ * represents the filled pixels of the character.
78
+ *
79
+ * @param char The character to build a path for.
80
+ *
81
+ * @returns A `Path2D` object representing the character's shape, or `null` if the character is not found in the font data.
82
+ *
83
+ * @remarks
84
+ * This method is called internally when a character is rendered for the first time
85
+ * or when pre-building glyphs. It converts the bitmap representation of the character
86
+ * into a vector path that can be efficiently rendered using `CanvasRenderingContext2D`.
87
+ * The resulting `Path2D` object is cached in the `glyphPaths` map for future use, reducing the overhead of path construction on subsequent renders.
88
+ */
89
+ private buildGlyphPath;
90
+ /**
91
+ * Pre-builds `Path2D` objects for a set of characters, storing them in the `glyphPaths`
92
+ * cache. This method can be called with a list of characters that are expected to
93
+ * be rendered frequently,
94
+ */
95
+ prebuildGlyphs(chars?: string[]): void;
96
+ /**
97
+ * Renders a single character at the given pixel position using a pre-built `Path2D` object.
98
+ * If the character's path has not been pre-built, it will be built on demand and cached for future use.
99
+ */
100
+ renderChar(char: string, x: number, y: number, ctx: CanvasRenderingContext2D): void;
101
+ }
102
+ //# sourceMappingURL=font_bitmap_prebuild.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font_bitmap_prebuild.d.ts","sourceRoot":"","sources":["../../../src/core/fonts/font_bitmap_prebuild.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,eAAe,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,UAAU;IACxD;;;;;OAKG;IACH,OAAO,CAAC,UAAU,CAAkC;IAEpD;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,cAAc;IAiBtB;;;;OAIG;IACI,cAAc,CAAC,KAAK,GAAE,MAAM,EAAO,GAAG,IAAI;IAWjD;;;OAGG;IACM,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB;CAatF"}