js-game-zero 0.0.0-placeholder → 0.3.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 (53) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +135 -3
  3. package/dist/active-service.d.ts +21 -0
  4. package/dist/actor.d.ts +182 -0
  5. package/dist/animation.d.ts +61 -0
  6. package/dist/assets.d.ts +148 -0
  7. package/dist/audio.d.ts +150 -0
  8. package/dist/browser-asset-backends.d.ts +8 -0
  9. package/dist/browser.d.ts +10 -0
  10. package/dist/chunk-brk0rjcy.js +7846 -0
  11. package/dist/clock.d.ts +29 -0
  12. package/dist/color.d.ts +15 -0
  13. package/dist/events.d.ts +145 -0
  14. package/dist/friendly-names.d.ts +43 -0
  15. package/dist/geometry.d.ts +125 -0
  16. package/dist/helpers.d.ts +28 -0
  17. package/dist/host.d.ts +58 -0
  18. package/dist/host.js +435 -0
  19. package/dist/index.d.ts +340 -0
  20. package/dist/index.js +258 -0
  21. package/dist/input.d.ts +200 -0
  22. package/dist/inspection.d.ts +58 -0
  23. package/dist/jgz.js +8419 -0
  24. package/dist/node.d.ts +14 -0
  25. package/dist/node.js +145 -0
  26. package/dist/pixel-density.d.ts +23 -0
  27. package/dist/random.d.ts +31 -0
  28. package/dist/reference/actor.md +197 -0
  29. package/dist/reference/audio.md +128 -0
  30. package/dist/reference/clock.md +150 -0
  31. package/dist/reference/helpers.md +46 -0
  32. package/dist/reference/hooks.md +379 -0
  33. package/dist/reference/index.d.ts +2 -0
  34. package/dist/reference/index.js +39 -0
  35. package/dist/reference/loaders.md +164 -0
  36. package/dist/reference/rect.md +129 -0
  37. package/dist/reference/runtime.md +478 -0
  38. package/dist/reference/screen.md +313 -0
  39. package/dist/reference/storage.md +90 -0
  40. package/dist/rendering.d.ts +188 -0
  41. package/dist/runtime-contracts.d.ts +117 -0
  42. package/dist/runtime-diagnostics.d.ts +37 -0
  43. package/dist/runtime-persistence.d.ts +32 -0
  44. package/dist/runtime-source.d.ts +82 -0
  45. package/dist/runtime.d.ts +81 -0
  46. package/dist/source-scan.d.ts +49 -0
  47. package/dist/storage-path.d.ts +3 -0
  48. package/dist/storage.d.ts +88 -0
  49. package/dist/tone.d.ts +81 -0
  50. package/dist/touch-controls.d.ts +24 -0
  51. package/dist/touch-pointer.d.ts +11 -0
  52. package/dist/util.d.ts +62 -0
  53. package/package.json +63 -4
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Taha Rafiq
4
+ Copyright (c) 2001 Robert Penner (easing equations)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -1,7 +1,139 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Leaf-Learning/js-game-zero/main/docs/media/logo-lockup-dark.svg">
3
+ <img src="https://raw.githubusercontent.com/Leaf-Learning/js-game-zero/main/docs/media/logo-lockup-light.svg" alt="js-game-zero" width="280">
4
+ </picture>
5
+
1
6
  # js-game-zero
2
7
 
3
- This version only holds the package name. Install 0.3.0 or later:
8
+ A framework that teaches beginners to build browser games in JavaScript, with
9
+ as little boilerplate as possible, in the spirit of
10
+ [Pygame Zero](https://github.com/lordmauve/pgzero). Declare `WIDTH` and
11
+ `HEIGHT`, write a `draw()` function, and you have a game window: no build
12
+ step, no canvas setup, no game loop to write. Actors, drawing, geometry,
13
+ input, animation, sound and storage are all there.
14
+
15
+ ```js
16
+ WIDTH = 480;
17
+ HEIGHT = 360;
18
+
19
+ let ship = new Actor("player.svg", { midBottom: [240, 350] });
20
+
21
+ function update(dt) {
22
+ if (keyboard.left) ship.x -= 220 * dt;
23
+ if (keyboard.right) ship.x += 220 * dt;
24
+ }
25
+
26
+ function draw() {
27
+ screen.fill("#0b1220", "#132a43");
28
+ ship.draw();
29
+ }
30
+ ```
31
+
32
+ ```sh
33
+ bun add js-game-zero
34
+ # or
35
+ npm install js-game-zero
36
+ ```
37
+
38
+ ## Use
39
+
40
+ `run()` takes a game's source as a string and runs it with the framework's
41
+ globals (`Actor`, `screen`, `keyboard`, `clock` and the rest) in its own scope:
42
+
43
+ ```js
44
+ import { run } from "js-game-zero";
45
+
46
+ run(`
47
+ WIDTH = 320;
48
+ HEIGHT = 180;
49
+ function draw() { screen.fill("navy"); }
50
+ `);
51
+ ```
52
+
53
+ For a game written as an ordinary module with imports, `defineGame()` gives it
54
+ its own set of services, so two games on one page never share state:
55
+
56
+ ```ts
57
+ import { defineGame, run } from "js-game-zero";
58
+
59
+ run(
60
+ defineGame(({ Actor, screen, keyboard }) => {
61
+ const player = new Actor("player.svg");
62
+ return {
63
+ WIDTH: 320,
64
+ HEIGHT: 180,
65
+ update(dt) {
66
+ if (keyboard.right) player.x += 120 * dt;
67
+ },
68
+ draw() {
69
+ screen.clear();
70
+ player.draw();
71
+ },
72
+ };
73
+ }),
74
+ );
75
+ ```
76
+
77
+ `run()` also takes a plain object of hooks.
78
+
79
+ ## Script tag
80
+
81
+ `js-game-zero/iife` (the file `dist/jgz.js`) is one self-contained script for
82
+ pages with no bundler. It puts the whole API on `window.JGZ`, with the host
83
+ APIs a page needs to start, drive and read a game it runs: `loadImages`,
84
+ `getGameInspector`, `serializeGameValue`, `parseStatePath` and
85
+ `createSeededRandom`.
86
+
87
+ Load it from a CDN such as jsDelivr, or copy the file next to your page.
88
+ `JGZ.run` adds the game's canvas to the page's `<body>`:
89
+
90
+ ```html
91
+ <script src="https://cdn.jsdelivr.net/npm/js-game-zero@0.3/dist/jgz.js"></script>
92
+ <script>
93
+ JGZ.run(`
94
+ WIDTH = 320;
95
+ HEIGHT = 180;
96
+ function draw() { screen.fill("navy"); }
97
+ `);
98
+ </script>
99
+ ```
100
+
101
+ Problems with the game, from a misspelled name to an image that won't load,
102
+ go to the browser console, or to a `diagnostics` callback if you pass one. A
103
+ game that fails stops, and its canvas is removed. [Getting
104
+ started](https://github.com/Leaf-Learning/js-game-zero/blob/main/docs/getting-started.md#a-game-in-one-web-page)
105
+ has a complete page that shows them on the page, with images and sounds.
106
+
107
+ In TypeScript, `/// <reference types="js-game-zero/iife" />` declares
108
+ `window.JGZ`.
109
+
110
+ The file opens with its license notice, so a page that inlines it carries it.
111
+
112
+ ## Entry points
113
+
114
+ | Import | What it holds |
115
+ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
116
+ | `js-game-zero` | The game API: `run`, `defineGame`, `Actor`, `Rect`, `screen`, `keyboard`, `clock`, `animate`, `storage` and the rest. |
117
+ | `js-game-zero/host` | For pages and tools that embed games: asset loaders, browser adapters, the game inspector and the value serialiser. |
118
+ | `js-game-zero/node` | A file-system storage adapter for sandbox projects, under Node or Bun. |
119
+ | `js-game-zero/iife` | The script-tag build, `dist/jgz.js`, which puts the API on `window.JGZ`. |
120
+ | `js-game-zero/reference` | The API reference: `reference`, every page's Markdown by its file name (`reference["actor.md"]`). |
121
+ | `js-game-zero/reference/<page>.md` | Each reference page as a Markdown file. |
122
+
123
+ To test a game from the terminal, press its keys and read its state, there's
124
+ the [`jgz` CLI](https://www.npmjs.com/package/@js-game-zero/cli).
125
+
126
+ ## Docs
127
+
128
+ - [Tutorial](https://github.com/Leaf-Learning/js-game-zero/blob/main/docs/tutorial.md):
129
+ build a complete game step by step.
130
+ - [API reference](https://github.com/Leaf-Learning/js-game-zero/tree/main/docs/reference):
131
+ every symbol, with a signature, an explanation and an example. The
132
+ [runtime page](https://github.com/Leaf-Learning/js-game-zero/blob/main/docs/reference/runtime.md)
133
+ covers `run()`, its options and embedding games in a host page.
134
+ - [Cookbook](https://github.com/Leaf-Learning/js-game-zero/blob/main/docs/cookbook.md):
135
+ short recipes for specific tasks.
4
136
 
5
- npm install js-game-zero
137
+ ## License
6
138
 
7
- https://github.com/Leaf-Learning/js-game-zero
139
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Well-known brand answered by every active-service proxy's `get` trap.
3
+ * Lets callers (e.g. `RunOptions` resolvers) detect "this is the ambient
4
+ * public export" and treat it as "use the default" instead of installing it
5
+ * as its own backing target, which would otherwise resolve back to itself
6
+ * and recurse infinitely.
7
+ */
8
+ export declare const activeServiceProxyBrand: unique symbol;
9
+ /** True when `value` is a proxy created by {@link createActiveServiceProxy}. */
10
+ export declare function isActiveServiceProxy(value: unknown): boolean;
11
+ /**
12
+ * Creates the process-level compatibility proxy used by learner-facing
13
+ * singleton services. Runtime callbacks choose the active run-scoped target;
14
+ * property access outside a callback falls back through `getActive`.
15
+ *
16
+ * Keeping the complete proxy behavior here prevents clock, input, audio,
17
+ * rendering, and storage from drifting apart as new reflective operations are
18
+ * added. Methods are bound to the resolved target so private fields and class
19
+ * receivers continue to work through the proxy.
20
+ */
21
+ export declare function createActiveServiceProxy<T extends object>(getActive: () => T): T;
@@ -0,0 +1,182 @@
1
+ import { type AssetLoader, type SurfaceLike } from "./assets.js";
2
+ import { type Clock } from "./clock.js";
3
+ import { Rect, type Point, type RectLike } from "./geometry.js";
4
+ import { type Screen } from "./rendering.js";
5
+ export type ActorAnchor = readonly [
6
+ HorizontalActorAnchor | number,
7
+ VerticalActorAnchor | number
8
+ ];
9
+ export type HorizontalActorAnchor = "left" | "center" | "middle" | "right";
10
+ export type VerticalActorAnchor = "top" | "center" | "middle" | "bottom";
11
+ export type ActorPositionKey = "pos" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "midTop" | "midLeft" | "midRight" | "midBottom" | "center";
12
+ export type ActorOptions = Partial<Record<ActorPositionKey, Point>> & {
13
+ readonly anchor?: ActorAnchor;
14
+ readonly angle?: number;
15
+ readonly opacity?: number;
16
+ /** Draw the image at this multiple of its native size (hitbox follows). */
17
+ readonly scale?: number;
18
+ /** Draw the image this many pixels wide, keeping its proportions. */
19
+ readonly width?: number;
20
+ /** Draw the image this many pixels tall, keeping its proportions. */
21
+ readonly height?: number;
22
+ /** Pictures to cycle through, in order, as a flip-book animation. */
23
+ readonly images?: readonly string[];
24
+ /** How many pictures of `images` to show per second (default 10). */
25
+ readonly fps?: number;
26
+ /** Whether `images` starts over after the last picture (default true). */
27
+ readonly loop?: boolean;
28
+ /** The clock whose time drives `images` (the running game's by default). */
29
+ readonly clock?: Pick<Clock, "now">;
30
+ readonly screen?: Screen;
31
+ readonly loader?: Pick<AssetLoader<SurfaceLike>, "load" | "get" | "unload">;
32
+ readonly imageLoader?: Pick<AssetLoader<SurfaceLike>, "load" | "get" | "unload">;
33
+ };
34
+ export declare class Actor {
35
+ private readonly loader;
36
+ private readonly screen?;
37
+ private _image;
38
+ private _surface;
39
+ private _pos;
40
+ private _anchor;
41
+ private _angle;
42
+ private _opacity;
43
+ private _scale;
44
+ /** The native image, before inflateIp/normalize wrap the hitbox size. */
45
+ private _native;
46
+ private sizeSource;
47
+ private positionSource;
48
+ private imageLoadPromise;
49
+ private _images;
50
+ private _fps;
51
+ private _loop;
52
+ /** Clock time when `images` was last set; frame 0 starts here. */
53
+ private animationStart;
54
+ private readonly clockSource;
55
+ constructor(image: string, pos?: Point | ActorOptions, options?: ActorOptions);
56
+ get image(): string;
57
+ set image(value: string);
58
+ /**
59
+ * Pictures to cycle through like a flip book, `fps` per second, using the
60
+ * game's clock (so the animation pauses with the game). Setting it starts
61
+ * from the first picture and loads the rest; `null` stops animating.
62
+ */
63
+ get images(): string[] | null;
64
+ set images(value: readonly string[] | null);
65
+ /** Pictures per second for `images`. */
66
+ get fps(): number;
67
+ set fps(value: number);
68
+ /** Whether `images` starts over after the last picture. */
69
+ get loop(): boolean;
70
+ set loop(value: boolean);
71
+ /** Which picture of `images` is showing (0 for the first). */
72
+ get frame(): number;
73
+ /** True once a non-looping `images` animation has shown its last picture. */
74
+ get finished(): boolean;
75
+ private elapsedFrames;
76
+ private now;
77
+ get pos(): Point;
78
+ set pos(value: Point);
79
+ get x(): number;
80
+ set x(value: number);
81
+ get y(): number;
82
+ set y(value: number);
83
+ get anchor(): ActorAnchor;
84
+ set anchor(value: ActorAnchor);
85
+ get angle(): number;
86
+ set angle(value: number);
87
+ get opacity(): number;
88
+ set opacity(value: number);
89
+ /**
90
+ * Size multiplier applied when drawing: `2` doubles the sprite, `0.5`
91
+ * halves it. The hitbox and every position property follow the drawn
92
+ * size, and the anchored position (a `midBottom` on the floor) stays put.
93
+ */
94
+ get scale(): number;
95
+ set scale(value: number);
96
+ /** The current (possibly rotated) bounding-box width. Setting it resizes
97
+ * the sprite to that width, keeping its proportions (like `scale`). */
98
+ get width(): number;
99
+ set width(value: number);
100
+ get height(): number;
101
+ set height(value: number);
102
+ get topLeft(): Point;
103
+ set topLeft(value: Point);
104
+ get topRight(): Point;
105
+ set topRight(value: Point);
106
+ get bottomLeft(): Point;
107
+ set bottomLeft(value: Point);
108
+ get bottomRight(): Point;
109
+ set bottomRight(value: Point);
110
+ get midTop(): Point;
111
+ set midTop(value: Point);
112
+ get midLeft(): Point;
113
+ set midLeft(value: Point);
114
+ get midRight(): Point;
115
+ set midRight(value: Point);
116
+ get midBottom(): Point;
117
+ set midBottom(value: Point);
118
+ get center(): Point;
119
+ set center(value: Point);
120
+ get left(): number;
121
+ set left(value: number);
122
+ get top(): number;
123
+ set top(value: number);
124
+ get right(): number;
125
+ set right(value: number);
126
+ get bottom(): number;
127
+ set bottom(value: number);
128
+ ready(): Promise<SurfaceLike>;
129
+ rect(): Rect;
130
+ draw(screen?: Screen): void;
131
+ unloadImage(): void;
132
+ angleTo(target: Actor | Point): number;
133
+ distanceTo(target: Actor | Point): number;
134
+ copy(): Rect;
135
+ move(x: number, y: number): Rect;
136
+ move(offset: Point): Rect;
137
+ moveIp(x: number, y: number): void;
138
+ moveIp(offset: Point): void;
139
+ inflate(x: number, y: number): Rect;
140
+ inflate(size: Point): Rect;
141
+ inflateIp(x: number, y: number): void;
142
+ inflateIp(size: Point): void;
143
+ normalize(): void;
144
+ clamp(other: RectLike): Rect;
145
+ /**
146
+ * Moves the actor (without resizing it) so its bounding box fits inside
147
+ * `other` — `player.clampIp(screen.bounds())` keeps it on screen.
148
+ */
149
+ clampIp(other: RectLike): void;
150
+ clip(other: RectLike): Rect;
151
+ union(other: RectLike): Rect;
152
+ unionAll(others: RectLike[]): Rect;
153
+ fit(other: RectLike): Rect;
154
+ collidePoint(point: Point): boolean;
155
+ collidePoint(x: number, y: number): boolean;
156
+ /**
157
+ * True when the actor overlaps `target`: another actor, a rect, or an
158
+ * `[x, y]` point such as a click position.
159
+ */
160
+ touches(target: Actor | RectLike | Point): boolean;
161
+ collideRect(other: RectLike): boolean;
162
+ collideList(others: RectLike[]): number;
163
+ collideListAll(others: RectLike[]): number[];
164
+ contains(other: RectLike): boolean;
165
+ private baseRect;
166
+ private surfaceWidth;
167
+ private surfaceHeight;
168
+ private adoptSurface;
169
+ /** Turns the requested size (scale, width or height) into `_scale` once
170
+ * the native image size is known, then re-applies the anchored position
171
+ * so the actor stays where the learner put it. */
172
+ private applySizeSource;
173
+ private setRectPosition;
174
+ /** Moves the actor so `rect()` becomes `rect`, a moved copy of it. */
175
+ private setPosFromRect;
176
+ private applyBaseRectGeometry;
177
+ private applyPositionSource;
178
+ /** Where `pos` is from the top-left of `rect()`: the anchor offset, unless
179
+ * the actor is rotated and its bounding box isn't the base rect. */
180
+ private rectOffset;
181
+ private anchorOffset;
182
+ }
@@ -0,0 +1,61 @@
1
+ import { Clock } from "./clock.js";
2
+ export type TweenName = keyof typeof tweens;
3
+ export type AnimationOptions<T extends object> = Partial<Record<keyof T, unknown>> & {
4
+ tween?: TweenName | string;
5
+ duration?: number;
6
+ onFinished?: () => void;
7
+ };
8
+ export declare const tweens: Readonly<{
9
+ linear(n: number): number;
10
+ accelerate(n: number): number;
11
+ decelerate(n: number): number;
12
+ accelDecel(n: number): number;
13
+ inElastic(n: number): number;
14
+ outElastic(n: number): number;
15
+ inOutElastic(n: number): number;
16
+ bounceEnd(n: number): number;
17
+ bounceStart(n: number): number;
18
+ bounceStartEnd(n: number): number;
19
+ endElastic(n: number): number;
20
+ startElastic(n: number): number;
21
+ bothElastic(n: number): number;
22
+ easeIn(n: number): number;
23
+ easeOut(n: number): number;
24
+ easeInOut(n: number): number;
25
+ bounce(n: number): number;
26
+ spring(n: number): number;
27
+ }>;
28
+ export declare const tweenNames: readonly ("linear" | "accelerate" | "decelerate" | "accelDecel" | "inElastic" | "outElastic" | "inOutElastic" | "bounceEnd" | "bounceStart" | "bounceStartEnd" | "endElastic" | "startElastic" | "bothElastic" | "easeIn" | "easeOut" | "easeInOut" | "bounce" | "spring")[];
29
+ export declare class Animation<T extends object = object> {
30
+ onFinished: (() => void) | undefined;
31
+ readonly object: T;
32
+ readonly duration: number;
33
+ readonly tween: TweenName;
34
+ private elapsed;
35
+ private targetValues;
36
+ private initialValues;
37
+ private readonly scheduler;
38
+ private readonly tickCallback;
39
+ private _running;
40
+ constructor(object: T, options: AnimationOptions<T>, scheduler?: Clock);
41
+ get running(): boolean;
42
+ update(dt: number): void;
43
+ stop(complete?: boolean): void;
44
+ removeTarget(property: string, stopIfEmpty?: boolean): void;
45
+ private completeNaturally;
46
+ private apply;
47
+ }
48
+ export declare class ValueError extends Error {
49
+ constructor(message: string);
50
+ }
51
+ export declare function animate<T extends object>(object: T, options: AnimationOptions<T>): Animation<T>;
52
+ /**
53
+ * `Animation` and `animate` as a game's code gets them: they run on the
54
+ * game's own clock wherever they're called, so one started outside a hook,
55
+ * as in a promise callback, doesn't go to a clock that never ticks.
56
+ */
57
+ export declare function animationsOn(scheduler: Clock): {
58
+ readonly Animation: typeof Animation;
59
+ readonly animate: typeof animate;
60
+ };
61
+ export declare function withAnimationClock<T>(scheduler: Clock, callback: () => T): T;
@@ -0,0 +1,148 @@
1
+ import { Rect, type Point, type RectLike } from "./geometry.js";
2
+ export type AssetKind = "image" | "sound" | "music" | "font";
3
+ /**
4
+ * A game's assets by path (`images/cat.svg`, `sounds/pop.wav`), each with
5
+ * the URL to load it from, such as a data URL. When a run is given one, its
6
+ * assets come only from here, so a page needs no asset server.
7
+ */
8
+ export type AssetUrls = Readonly<Record<string, string>>;
9
+ export type AssetLoadOptions<T> = {
10
+ readonly kind: AssetKind;
11
+ readonly directory: string;
12
+ readonly extensions: readonly string[];
13
+ readonly projectId?: string;
14
+ readonly assetBaseUrl?: string;
15
+ /** Where each asset is, by path; when set, `assetBaseUrl` isn't used. */
16
+ readonly assetUrls?: AssetUrls;
17
+ readonly loadAsset?: (url: string, name: string) => Promise<T>;
18
+ };
19
+ export type AssetLoaderOptions<T> = Partial<Omit<AssetLoadOptions<T>, "kind" | "directory" | "extensions">>;
20
+ export type DrawableImage = CanvasImageSource & {
21
+ readonly width?: number;
22
+ readonly height?: number;
23
+ readonly naturalWidth?: number;
24
+ readonly naturalHeight?: number;
25
+ };
26
+ export type SurfaceRectOptions = Partial<Record<PositionKey, Point>>;
27
+ export interface SurfaceLike {
28
+ readonly source: DrawableImage;
29
+ getWidth(): number;
30
+ getHeight(): number;
31
+ getSize(): Point;
32
+ getRect(options?: SurfaceRectOptions): Rect;
33
+ }
34
+ export type LoadedAsset = {
35
+ readonly kind: AssetKind;
36
+ readonly name: string;
37
+ readonly url: string;
38
+ readonly bytes?: ArrayBuffer;
39
+ };
40
+ export type SoundAsset = LoadedAsset & {
41
+ play(loops?: number): void;
42
+ /** Plays the sound over and over until `stop()`. */
43
+ loop(): void;
44
+ stop(): void;
45
+ getLength(): number;
46
+ setVolume(volume: number): void;
47
+ getVolume(): number;
48
+ };
49
+ export type MusicAsset = LoadedAsset;
50
+ export type FontAsset = LoadedAsset & {
51
+ readonly family: string;
52
+ readonly face?: FontFace;
53
+ };
54
+ export type AssetLoader<T> = {
55
+ readonly kind: AssetKind;
56
+ readonly projectId: string;
57
+ readonly assetBaseUrl: string;
58
+ /** The asset map this loader reads instead of `assetBaseUrl`, if it was given one. */
59
+ readonly assetUrls?: AssetUrls | undefined;
60
+ readonly extensions: readonly string[];
61
+ readonly directory: string;
62
+ load(name: string): Promise<T>;
63
+ get(name: string): T | undefined;
64
+ has(name: string): boolean;
65
+ define(name: string, asset: T): T;
66
+ unload(name: string): boolean;
67
+ unloadAll(): void;
68
+ cacheKeys(): string[];
69
+ /**
70
+ * Number of loads currently in flight (started but not yet settled).
71
+ * Used by the runtime's preload gate to hold frames until assets that
72
+ * were requested during module evaluation (or mid-game) have resolved.
73
+ */
74
+ pendingCount(): number;
75
+ /**
76
+ * Resolves once there are no in-flight loads, or rejects with failures
77
+ * settled since the previous idle wait. Re-checks after each settle so
78
+ * loads triggered while waiting are also awaited.
79
+ */
80
+ waitForIdle(): Promise<void>;
81
+ };
82
+ export type AssetLoaders = {
83
+ images: AssetLoader<SurfaceLike>;
84
+ sounds: AssetLoader<SoundAsset>;
85
+ music: AssetLoader<MusicAsset>;
86
+ fonts: AssetLoader<FontAsset>;
87
+ };
88
+ type PositionKey = "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "midTop" | "midLeft" | "midRight" | "midBottom" | "center";
89
+ export declare class MissingAssetError extends Error {
90
+ readonly kind: AssetKind;
91
+ readonly assetName: string;
92
+ readonly attemptedUrls: readonly string[];
93
+ constructor(kind: AssetKind, name: string, options?: {
94
+ readonly attemptedUrls?: readonly string[];
95
+ readonly cause?: unknown;
96
+ /** Why a file that's there couldn't be loaded, when that's known. */
97
+ readonly why?: string;
98
+ });
99
+ }
100
+ export declare class InvalidAssetNameError extends TypeError {
101
+ constructor(kind: AssetKind, name: string, reason: string);
102
+ }
103
+ export declare class ImageSurface implements SurfaceLike {
104
+ readonly source: DrawableImage;
105
+ private readonly width;
106
+ private readonly height;
107
+ constructor(source: DrawableImage, width?: number, height?: number);
108
+ getWidth(): number;
109
+ getHeight(): number;
110
+ getSize(): Point;
111
+ getRect(options?: SurfaceRectOptions): Rect;
112
+ }
113
+ export declare function createImageLoader(options?: AssetLoaderOptions<SurfaceLike>): AssetLoader<SurfaceLike>;
114
+ /**
115
+ * Decodes every image in an asset map, by name under `images/`, for
116
+ * `RunOptions.preloadedAssets`. `run()` starts a game's code at once, so an
117
+ * image the run loads itself has no size yet while the top level runs: an
118
+ * Actor placed by `midBottom` and then moved lands as if it were 0×0.
119
+ * Images handed over decoded are ready from the first line, as Pygame
120
+ * Zero's are. One that fails to decode is left out, so the run loads it
121
+ * itself and reports the failure only if the game uses it.
122
+ */
123
+ export declare function loadImages(assetUrls: AssetUrls): Promise<Record<string, SurfaceLike>>;
124
+ export declare function createSoundLoader(options?: AssetLoaderOptions<SoundAsset>): AssetLoader<SoundAsset>;
125
+ export declare function createMusicLoader(options?: AssetLoaderOptions<MusicAsset>): AssetLoader<MusicAsset>;
126
+ export declare function createFontLoader(options?: AssetLoaderOptions<FontAsset>): AssetLoader<FontAsset>;
127
+ export declare function createAssetLoaders(options?: {
128
+ readonly projectId?: string;
129
+ readonly assetBaseUrl?: string;
130
+ readonly assetUrls?: AssetUrls;
131
+ }): AssetLoaders;
132
+ export declare function validateAssetName(kind: AssetKind, name: string, extensions: readonly string[]): string;
133
+ export declare function assetUrl(assetBaseUrl: string, projectId: string, directory: string, name: string): string;
134
+ /** Where a loader finds the asset with this exact name, or undefined when its asset map doesn't list it. */
135
+ export declare function locateAsset(loader: {
136
+ readonly assetBaseUrl: string;
137
+ readonly assetUrls?: AssetUrls | undefined;
138
+ readonly projectId: string;
139
+ readonly directory: string;
140
+ }, name: string): string | undefined;
141
+ export declare const defaultAssetLoaders: AssetLoaders;
142
+ export declare function setActiveAssetLoaders(loaders: AssetLoaders | null): AssetLoaders | null;
143
+ export declare const images: AssetLoader<SurfaceLike>;
144
+ export declare const sounds: AssetLoader<SoundAsset>;
145
+ export declare const musicAssets: AssetLoader<LoadedAsset>;
146
+ export declare const fonts: AssetLoader<FontAsset>;
147
+ export declare function rectFromBlitTarget(target: Point | RectLike): Rect | Point;
148
+ export {};