@debuffjp/core 0.0.0-stage → 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 (82) hide show
  1. package/README.md +68 -2
  2. package/dist/camera.d.ts +60 -0
  3. package/dist/camera.d.ts.map +1 -0
  4. package/dist/camera.js +86 -0
  5. package/dist/camera.js.map +1 -0
  6. package/dist/clock.d.ts +21 -0
  7. package/dist/clock.d.ts.map +1 -0
  8. package/dist/clock.js +26 -0
  9. package/dist/clock.js.map +1 -0
  10. package/dist/game.d.ts +149 -0
  11. package/dist/game.d.ts.map +1 -0
  12. package/dist/game.js +372 -0
  13. package/dist/game.js.map +1 -0
  14. package/dist/hooks.d.ts +45 -0
  15. package/dist/hooks.d.ts.map +1 -0
  16. package/dist/hooks.js +57 -0
  17. package/dist/hooks.js.map +1 -0
  18. package/dist/index.d.ts +18 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +14 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/inspect.d.ts +12 -0
  23. package/dist/inspect.d.ts.map +1 -0
  24. package/dist/inspect.js +29 -0
  25. package/dist/inspect.js.map +1 -0
  26. package/dist/math.d.ts +15 -0
  27. package/dist/math.d.ts.map +1 -0
  28. package/dist/math.js +14 -0
  29. package/dist/math.js.map +1 -0
  30. package/dist/params.d.ts +59 -0
  31. package/dist/params.d.ts.map +1 -0
  32. package/dist/params.js +76 -0
  33. package/dist/params.js.map +1 -0
  34. package/dist/plugin.d.ts +34 -0
  35. package/dist/plugin.d.ts.map +1 -0
  36. package/dist/plugin.js +34 -0
  37. package/dist/plugin.js.map +1 -0
  38. package/dist/random.d.ts +13 -0
  39. package/dist/random.d.ts.map +1 -0
  40. package/dist/random.js +29 -0
  41. package/dist/random.js.map +1 -0
  42. package/dist/render/backend.d.ts +48 -0
  43. package/dist/render/backend.d.ts.map +1 -0
  44. package/dist/render/backend.js +2 -0
  45. package/dist/render/backend.js.map +1 -0
  46. package/dist/render/canvas2d.d.ts +26 -0
  47. package/dist/render/canvas2d.d.ts.map +1 -0
  48. package/dist/render/canvas2d.js +103 -0
  49. package/dist/render/canvas2d.js.map +1 -0
  50. package/dist/render/color.d.ts +7 -0
  51. package/dist/render/color.d.ts.map +1 -0
  52. package/dist/render/color.js +49 -0
  53. package/dist/render/color.js.map +1 -0
  54. package/dist/render/draw.d.ts +111 -0
  55. package/dist/render/draw.d.ts.map +1 -0
  56. package/dist/render/draw.js +369 -0
  57. package/dist/render/draw.js.map +1 -0
  58. package/dist/render/webgl2.d.ts +46 -0
  59. package/dist/render/webgl2.d.ts.map +1 -0
  60. package/dist/render/webgl2.js +312 -0
  61. package/dist/render/webgl2.js.map +1 -0
  62. package/dist/scene.d.ts +14 -0
  63. package/dist/scene.d.ts.map +1 -0
  64. package/dist/scene.js +2 -0
  65. package/dist/scene.js.map +1 -0
  66. package/dist/splash.d.ts +11 -0
  67. package/dist/splash.d.ts.map +1 -0
  68. package/dist/splash.js +39 -0
  69. package/dist/splash.js.map +1 -0
  70. package/dist/sprite.d.ts +57 -0
  71. package/dist/sprite.d.ts.map +1 -0
  72. package/dist/sprite.js +108 -0
  73. package/dist/sprite.js.map +1 -0
  74. package/dist/stats.d.ts +23 -0
  75. package/dist/stats.d.ts.map +1 -0
  76. package/dist/stats.js +50 -0
  77. package/dist/stats.js.map +1 -0
  78. package/dist/storage.d.ts +15 -0
  79. package/dist/storage.d.ts.map +1 -0
  80. package/dist/storage.js +45 -0
  81. package/dist/storage.js.map +1 -0
  82. package/package.json +40 -4
package/README.md CHANGED
@@ -1,3 +1,69 @@
1
- # Temporary Holding Version
1
+ # @debuffjp/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Game loop, fixed-step clock, scenes, camera, sprites, typed creative parameters and a batched WebGL2 renderer with automatic Canvas 2D fallback. No dependencies.
4
+
5
+ ```bash
6
+ npm install @debuffjp/core
7
+ ```
8
+
9
+ ```ts
10
+ import { Game } from "@debuffjp/core";
11
+
12
+ const game = new Game({ canvas, width: 320, height: 180, scale: "integer", pixelArt: true });
13
+
14
+ const tuning = game.params.define("player", {
15
+ speed: { type: "number", default: 140, min: 0, max: 600, units: "px/s" },
16
+ });
17
+
18
+ game
19
+ .add({
20
+ id: "play",
21
+ update(dt) {
22
+ /* simulation at a fixed step */
23
+ },
24
+ render(draw, alpha) {
25
+ /* draw in logical units */
26
+ },
27
+ inspect: () => ({
28
+ /* JSON snapshot for tools and tests */
29
+ }),
30
+ })
31
+ .go("play");
32
+ game.start();
33
+ ```
34
+
35
+ ## API
36
+
37
+ | Export | Purpose |
38
+ | --- | --- |
39
+ | `Game` | Loop, scenes, scaling, `camera`, `random`, `params`, `stats`, `freeze()`, `timeScale`, `step(n)` |
40
+ | `game.use(plugin)`, `Plugin` | Install runtime plugins. See [docs/plugins.md](https://github.com/debuffjp/debuff/blob/main/docs/plugins.md) |
41
+ | `game.hooks` | `beforeUpdate`, `afterUpdate`, `beforeRender`, `afterRender`, `sceneChange`. Each returns an unsubscribe function |
42
+ | `game.inspect` | `register(name, () => json)` for dev tools and scenarios |
43
+ | `Scene` | `{ id, enter?, update?, render?, exit?, inspect? }` |
44
+ | `Camera` | Smoothed follow, deadzone, bounds, trauma shake, culling, `toWorld()` |
45
+ | `Sprite`, `sliceGrid` | Frames, named animations, origin, flip, impact flash |
46
+ | `ParamRegistry`, `ParamGroup` | Typed, ranged, unit-annotated values; `describe()` is JSON |
47
+ | `Random` | Seeded PRNG: `next`, `range`, `int`, `pick` |
48
+ | `FixedClock` | Fixed-step accumulator with render interpolation |
49
+ | `Storage` | `localStorage` wrapper that never throws |
50
+ | `FrameStats` | Allocation-free frame interval / work timings |
51
+ | `clamp`, `lerp`, `damp`, `approach`, `overlaps`, `circlesOverlap` | Math helpers |
52
+
53
+ ### Scale modes
54
+
55
+ - `fit` (default): fill the container, keep the aspect ratio.
56
+ - `integer`: whole device-pixel scales for crisp pixel art.
57
+ - `none`: logical size in CSS pixels.
58
+
59
+ Game code always uses logical units. The backing store follows `devicePixelRatio`.
60
+
61
+ ## Start-up splash
62
+
63
+ Production builds open with a short "made with debuff" card. It lasts 1.6 seconds and any key skips it. Clicking or tapping the card opens [debuff.jp](https://debuff.jp) in a new tab and skips it too. It never shows in dev, or when time is driven by `step()` (scenarios and agents). To remove it:
64
+
65
+ ```ts
66
+ new Game({ canvas, width, height, splash: false });
67
+ ```
68
+
69
+ You can also set its length with `splash: { duration: 1 }`, or end it from code with `game.skipSplash()`.
@@ -0,0 +1,60 @@
1
+ import type { Random } from "./random.js";
2
+ export interface CameraOptions {
3
+ /** Viewport size in logical pixels. */
4
+ width: number;
5
+ height: number;
6
+ /** World bounds the camera may not leave. Omit for unbounded. */
7
+ bounds?: {
8
+ x: number;
9
+ y: number;
10
+ width: number;
11
+ height: number;
12
+ };
13
+ }
14
+ /** 2D camera with smoothed follow, deadzone and trauma-based shake.
15
+ * `x`/`y` is the world point at the viewport centre. */
16
+ export declare class Camera {
17
+ x: number;
18
+ y: number;
19
+ zoom: number;
20
+ /** Follow responsiveness in 1/s; higher snaps faster. 0 disables smoothing. */
21
+ lerp: number;
22
+ /** Half-size of the region the target may move in without the camera moving. */
23
+ deadzone: {
24
+ x: number;
25
+ y: number;
26
+ };
27
+ /** Max shake offset in pixels at trauma 1. */
28
+ shakeAmplitude: number;
29
+ /** Trauma lost per second. */
30
+ shakeDecay: number;
31
+ /** Multiplier on shake. `Game` lowers it when the player prefers reduced motion. */
32
+ motionScale: number;
33
+ private trauma;
34
+ private offsetX;
35
+ private offsetY;
36
+ private target;
37
+ readonly width: number;
38
+ readonly height: number;
39
+ bounds: CameraOptions["bounds"];
40
+ constructor(options: CameraOptions);
41
+ follow(target: {
42
+ x: number;
43
+ y: number;
44
+ } | undefined, snap?: boolean): void;
45
+ /** Add shake trauma in [0, 1]. Shake intensity scales with trauma². */
46
+ shake(amount: number): void;
47
+ update(dt: number, random: Random): void;
48
+ /** The camera as an affine matrix `[a, b, c, d, e, f]` from world to viewport units.
49
+ * Use with `draw.withCamera(camera, () => { ... })`. Positions snap to whole pixels. */
50
+ view(): [number, number, number, number, number, number];
51
+ /** Convert a viewport point (e.g. pointer) to world coordinates. */
52
+ toWorld(sx: number, sy: number): {
53
+ x: number;
54
+ y: number;
55
+ };
56
+ /** True if a world-space rectangle overlaps the view (for culling). */
57
+ sees(x: number, y: number, w: number, h: number): boolean;
58
+ private clamp;
59
+ }
60
+ //# sourceMappingURL=camera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,MAAM,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAED;yDACyD;AACzD,qBAAa,MAAM;IACjB,CAAC,SAAK;IACN,CAAC,SAAK;IACN,IAAI,SAAK;IACT,+EAA+E;IAC/E,IAAI,SAAK;IACT,gFAAgF;IAChF,QAAQ;QAAK,CAAC;QAAK,CAAC;MAAM;IAC1B,8CAA8C;IAC9C,cAAc,SAAK;IACnB,8BAA8B;IAC9B,UAAU,SAAO;IACjB,oFAAoF;IACpF,WAAW,SAAK;IAEhB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAAuC;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAEhC,YAAY,OAAO,EAAE,aAAa,EAIjC;IAED,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,EAAE,IAAI,UAAO,GAAG,IAAI,CAOtE;IAED,uEAAuE;IACvE,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE1B;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAevC;IAED;6FACyF;IACzF,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGvD;IAED,oEAAoE;IACpE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAKxD;IAED,uEAAuE;IACvE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAIxD;IAED,OAAO,CAAC,KAAK;CAQd"}
package/dist/camera.js ADDED
@@ -0,0 +1,86 @@
1
+ /** 2D camera with smoothed follow, deadzone and trauma-based shake.
2
+ * `x`/`y` is the world point at the viewport centre. */
3
+ export class Camera {
4
+ x = 0;
5
+ y = 0;
6
+ zoom = 1;
7
+ /** Follow responsiveness in 1/s; higher snaps faster. 0 disables smoothing. */
8
+ lerp = 8;
9
+ /** Half-size of the region the target may move in without the camera moving. */
10
+ deadzone = { x: 0, y: 0 };
11
+ /** Max shake offset in pixels at trauma 1. */
12
+ shakeAmplitude = 8;
13
+ /** Trauma lost per second. */
14
+ shakeDecay = 1.5;
15
+ /** Multiplier on shake. `Game` lowers it when the player prefers reduced motion. */
16
+ motionScale = 1;
17
+ trauma = 0;
18
+ offsetX = 0;
19
+ offsetY = 0;
20
+ target;
21
+ width;
22
+ height;
23
+ bounds;
24
+ constructor(options) {
25
+ this.width = options.width;
26
+ this.height = options.height;
27
+ this.bounds = options.bounds;
28
+ }
29
+ follow(target, snap = true) {
30
+ this.target = target;
31
+ if (target && snap) {
32
+ this.x = target.x;
33
+ this.y = target.y;
34
+ this.clamp();
35
+ }
36
+ }
37
+ /** Add shake trauma in [0, 1]. Shake intensity scales with trauma². */
38
+ shake(amount) {
39
+ this.trauma = Math.min(1, this.trauma + amount);
40
+ }
41
+ update(dt, random) {
42
+ if (this.target) {
43
+ const dx = this.target.x - this.x;
44
+ const dy = this.target.y - this.y;
45
+ const tx = Math.abs(dx) > this.deadzone.x ? this.target.x - Math.sign(dx) * this.deadzone.x : this.x;
46
+ const ty = Math.abs(dy) > this.deadzone.y ? this.target.y - Math.sign(dy) * this.deadzone.y : this.y;
47
+ const k = this.lerp > 0 ? 1 - Math.exp(-this.lerp * dt) : 1;
48
+ this.x += (tx - this.x) * k;
49
+ this.y += (ty - this.y) * k;
50
+ }
51
+ this.clamp();
52
+ this.trauma = Math.max(0, this.trauma - this.shakeDecay * dt);
53
+ const power = this.trauma * this.trauma * this.shakeAmplitude * this.motionScale;
54
+ this.offsetX = power * random.range(-1, 1);
55
+ this.offsetY = power * random.range(-1, 1);
56
+ }
57
+ /** The camera as an affine matrix `[a, b, c, d, e, f]` from world to viewport units.
58
+ * Use with `draw.withCamera(camera, () => { ... })`. Positions snap to whole pixels. */
59
+ view() {
60
+ const z = this.zoom;
61
+ return [z, 0, 0, z, this.width / 2 - z * Math.round(this.x + this.offsetX), this.height / 2 - z * Math.round(this.y + this.offsetY)];
62
+ }
63
+ /** Convert a viewport point (e.g. pointer) to world coordinates. */
64
+ toWorld(sx, sy) {
65
+ return {
66
+ x: (sx - this.width / 2) / this.zoom + this.x,
67
+ y: (sy - this.height / 2) / this.zoom + this.y,
68
+ };
69
+ }
70
+ /** True if a world-space rectangle overlaps the view (for culling). */
71
+ sees(x, y, w, h) {
72
+ const hw = this.width / 2 / this.zoom;
73
+ const hh = this.height / 2 / this.zoom;
74
+ return x + w >= this.x - hw && x <= this.x + hw && y + h >= this.y - hh && y <= this.y + hh;
75
+ }
76
+ clamp() {
77
+ const b = this.bounds;
78
+ if (!b)
79
+ return;
80
+ const hw = this.width / 2 / this.zoom;
81
+ const hh = this.height / 2 / this.zoom;
82
+ this.x = b.width <= hw * 2 ? b.x + b.width / 2 : Math.min(Math.max(this.x, b.x + hw), b.x + b.width - hw);
83
+ this.y = b.height <= hh * 2 ? b.y + b.height / 2 : Math.min(Math.max(this.y, b.y + hh), b.y + b.height - hh);
84
+ }
85
+ }
86
+ //# sourceMappingURL=camera.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /** Fixed-timestep accumulator. Simulation advances in exact `step` increments so
2
+ * scenarios can replay identically; rendering receives the leftover `alpha`. */
3
+ export interface FixedClockOptions {
4
+ /** Simulation step in seconds. Default 1/60. */
5
+ step?: number;
6
+ /** Cap on real elapsed time per frame, to avoid a spiral after tab switches. Default 0.25s. */
7
+ maxFrame?: number;
8
+ }
9
+ export declare class FixedClock {
10
+ readonly step: number;
11
+ readonly maxFrame: number;
12
+ /** Total simulated seconds. */
13
+ time: number;
14
+ /** Total simulation steps taken. */
15
+ tick: number;
16
+ private accumulator;
17
+ constructor(options?: FixedClockOptions);
18
+ /** Feed real elapsed seconds; calls `update` once per whole step. Returns render alpha in [0, 1). */
19
+ advance(elapsed: number, update: (dt: number) => void): number;
20
+ }
21
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;iFACiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,UAAU;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+BAA+B;IAC/B,IAAI,SAAK;IACT,oCAAoC;IACpC,IAAI,SAAK;IACT,OAAO,CAAC,WAAW,CAAK;IAExB,YAAY,OAAO,GAAE,iBAAsB,EAG1C;IAED,qGAAqG;IACrG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAU7D;CACF"}
package/dist/clock.js ADDED
@@ -0,0 +1,26 @@
1
+ export class FixedClock {
2
+ step;
3
+ maxFrame;
4
+ /** Total simulated seconds. */
5
+ time = 0;
6
+ /** Total simulation steps taken. */
7
+ tick = 0;
8
+ accumulator = 0;
9
+ constructor(options = {}) {
10
+ this.step = options.step ?? 1 / 60;
11
+ this.maxFrame = options.maxFrame ?? 0.25;
12
+ }
13
+ /** Feed real elapsed seconds; calls `update` once per whole step. Returns render alpha in [0, 1). */
14
+ advance(elapsed, update) {
15
+ // Small epsilon absorbs float drift so e.g. 0.1 + 0.1 + 0.05 doesn't lose a step.
16
+ this.accumulator += Math.min(Math.max(elapsed, 0), this.maxFrame);
17
+ while (this.accumulator >= this.step - 1e-9) {
18
+ update(this.step);
19
+ this.accumulator = Math.max(0, this.accumulator - this.step);
20
+ this.time += this.step;
21
+ this.tick++;
22
+ }
23
+ return this.accumulator / this.step;
24
+ }
25
+ }
26
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,UAAU;IACZ,IAAI,CAAS;IACb,QAAQ,CAAS;IAC1B,+BAA+B;IAC/B,IAAI,GAAG,CAAC,CAAC;IACT,oCAAoC;IACpC,IAAI,GAAG,CAAC,CAAC;IACD,WAAW,GAAG,CAAC,CAAC;IAExB,YAAY,OAAO,GAAsB,EAAE;QACzC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC3C,CAAC;IAED,qGAAqG;IACrG,OAAO,CAAC,OAAe,EAAE,MAA4B;QACnD,kFAAkF;QAClF,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7D,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;YACvB,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC;IACtC,CAAC;CACF"}
package/dist/game.d.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { Camera } from "./camera.js";
2
+ import { FixedClock, type FixedClockOptions } from "./clock.js";
3
+ import { type GameErrorContext, Hooks } from "./hooks.js";
4
+ import { InspectRegistry } from "./inspect.js";
5
+ import { ParamRegistry } from "./params.js";
6
+ import { type Plugin } from "./plugin.js";
7
+ import { Random } from "./random.js";
8
+ import { Draw } from "./render/draw.js";
9
+ import type { Scene } from "./scene.js";
10
+ import { type SplashOptions } from "./splash.js";
11
+ import { FrameStats } from "./stats.js";
12
+ /**
13
+ * How the logical resolution maps onto the page:
14
+ * - `fit`: scale to fill the container, preserving aspect ratio (letterboxed).
15
+ * - `integer`: like `fit` but only whole-number scales, for crisp pixel art.
16
+ * - `none`: canvas stays at its logical size in CSS pixels.
17
+ */
18
+ export type ScaleMode = "fit" | "integer" | "none";
19
+ export interface GameOptions {
20
+ canvas: HTMLCanvasElement;
21
+ /** Logical resolution. Game code always works in these units. */
22
+ width: number;
23
+ height: number;
24
+ /** Default "fit". */
25
+ scale?: ScaleMode;
26
+ /** Element whose size drives scaling. Default: the canvas's parent. */
27
+ container?: HTMLElement;
28
+ /** Nearest-neighbour sampling for crisp pixel art. Default false. */
29
+ pixelArt?: boolean;
30
+ /**
31
+ * Rendering backend. `auto` (default) uses WebGL2 and falls back to Canvas 2D when it is unavailable.
32
+ * Both implement the same `draw` API (ADR 0007).
33
+ */
34
+ renderer?: "auto" | "webgl2" | "2d";
35
+ seed?: number;
36
+ clock?: FixedClockOptions;
37
+ /** Opaque background colour, cleared every frame. Default "#000". */
38
+ background?: string;
39
+ /**
40
+ * Honour the player's reduced-motion preference. `"auto"` (default) follows
41
+ * `prefers-reduced-motion` live; `true`/`false` force it.
42
+ */
43
+ reducedMotion?: "auto" | boolean;
44
+ /**
45
+ * A short "made with debuff" card when the game first starts, before your first frame. Any key,
46
+ * click or tap skips it. Not shown in dev, or when time is driven by `step()` (scenarios, agents).
47
+ * `false` removes it. Default on.
48
+ */
49
+ splash?: boolean | SplashOptions;
50
+ }
51
+ /** Owns the loop, clock, RNG, params, camera and active scene. Renders with the
52
+ * standard Canvas 2D API; nothing here depends on a third-party renderer. */
53
+ export declare class Game {
54
+ readonly canvas: HTMLCanvasElement;
55
+ /** Immediate-mode drawing API, passed to `Scene.render` and render hooks. */
56
+ readonly draw: Draw;
57
+ readonly width: number;
58
+ readonly height: number;
59
+ readonly clock: FixedClock;
60
+ readonly random: Random;
61
+ readonly params: ParamRegistry;
62
+ readonly camera: Camera;
63
+ readonly stats: FrameStats;
64
+ /** Typed lifecycle hooks for plugins and game code. */
65
+ readonly hooks: Hooks;
66
+ /** Named JSON snapshot providers for dev tools and scenarios. */
67
+ readonly inspect: InspectRegistry;
68
+ readonly scaleMode: ScaleMode;
69
+ readonly pixelArt: boolean;
70
+ background: string;
71
+ /** Multiplier on simulation dt; 0.5 = slow motion. */
72
+ timeScale: number;
73
+ /**
74
+ * True when the player prefers reduced motion. Camera shake is scaled down automatically; read this
75
+ * to tone down your own effects (flashes, screen-wide particles, parallax speed).
76
+ */
77
+ get reducedMotion(): boolean;
78
+ set reducedMotion(value: boolean);
79
+ /** Pause automatically while the tab is hidden. */
80
+ pauseWhenHidden: boolean;
81
+ /** Current backing-store pixels per logical unit (CSS scale × devicePixelRatio). */
82
+ renderScale: number;
83
+ private freezeTime;
84
+ private motionReduced;
85
+ private motionQuery;
86
+ private readonly onMotionChange;
87
+ private lastAlpha;
88
+ private readonly pluginRegistry;
89
+ private readonly scenes;
90
+ private active;
91
+ private frameHandle;
92
+ /** What the frame is doing right now, so a loop error can say where it came from. */
93
+ private phase;
94
+ private splashTotal;
95
+ private splashLeft;
96
+ private readonly onSplashSkip;
97
+ /** A click or tap on the splash opens the debuff site (the gesture allows the new tab); elsewhere it just skips. */
98
+ private readonly onSplashPointer;
99
+ private lastTime;
100
+ private running;
101
+ /** Whether the caller wants the loop running; visibility pausing respects this. */
102
+ private wanted;
103
+ private observer;
104
+ private readonly container;
105
+ private readonly onVisibility;
106
+ constructor(options: GameOptions);
107
+ /** End the start-up splash now (if it is showing or pending). */
108
+ skipSplash(): void;
109
+ /** Install a plugin. Throws if the name is invalid or already installed. */
110
+ use(plugin: Plugin): this;
111
+ /** Names of installed plugins, in install order. */
112
+ get plugins(): readonly string[];
113
+ /** Stop the loop, run plugin cleanups (reverse order) and detach browser listeners. */
114
+ dispose(): void;
115
+ /** The error that stopped the loop, if any. Cleared by `start()`. */
116
+ lastError: (GameErrorContext & {
117
+ error: unknown;
118
+ }) | undefined;
119
+ get scene(): Scene | undefined;
120
+ get isRunning(): boolean;
121
+ add(scene: Scene): this;
122
+ go(id: string): void;
123
+ start(): void;
124
+ stop(): void;
125
+ /** Advance by real elapsed seconds and render once. Exposed so tests and scenarios
126
+ * can drive the game without requestAnimationFrame. */
127
+ frame(elapsed: number): void;
128
+ /** Render the current state again without advancing time or touching frame stats (captures, editors). */
129
+ redraw(): void;
130
+ /** Hit-stop: pause simulation for `seconds` (rendering continues). */
131
+ freeze(seconds: number): void;
132
+ get frozen(): boolean;
133
+ /** Advance exactly `steps` simulation steps (deterministic; for scenarios). */
134
+ step(steps?: number): void;
135
+ /** Recompute CSS size and backing-store resolution. Called automatically on container
136
+ * resize and devicePixelRatio changes. */
137
+ resize(): void;
138
+ /** Convert client (event) coordinates to logical game coordinates. Bound, so it can be passed around (e.g. to `Input`). */
139
+ readonly toLogical: (clientX: number, clientY: number) => {
140
+ x: number;
141
+ y: number;
142
+ };
143
+ private run;
144
+ private halt;
145
+ /** devicePixelRatio changes (zoom, moving between monitors) don't fire resize on the container. */
146
+ private watchDpr;
147
+ private render;
148
+ }
149
+ //# sourceMappingURL=game.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game.d.ts","sourceRoot":"","sources":["../src/game.ts"],"names":[],"mappings":"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"}