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.
- package/LICENSE +22 -0
- package/README.md +135 -3
- package/dist/active-service.d.ts +21 -0
- package/dist/actor.d.ts +182 -0
- package/dist/animation.d.ts +61 -0
- package/dist/assets.d.ts +148 -0
- package/dist/audio.d.ts +150 -0
- package/dist/browser-asset-backends.d.ts +8 -0
- package/dist/browser.d.ts +10 -0
- package/dist/chunk-brk0rjcy.js +7846 -0
- package/dist/clock.d.ts +29 -0
- package/dist/color.d.ts +15 -0
- package/dist/events.d.ts +145 -0
- package/dist/friendly-names.d.ts +43 -0
- package/dist/geometry.d.ts +125 -0
- package/dist/helpers.d.ts +28 -0
- package/dist/host.d.ts +58 -0
- package/dist/host.js +435 -0
- package/dist/index.d.ts +340 -0
- package/dist/index.js +258 -0
- package/dist/input.d.ts +200 -0
- package/dist/inspection.d.ts +58 -0
- package/dist/jgz.js +8419 -0
- package/dist/node.d.ts +14 -0
- package/dist/node.js +145 -0
- package/dist/pixel-density.d.ts +23 -0
- package/dist/random.d.ts +31 -0
- package/dist/reference/actor.md +197 -0
- package/dist/reference/audio.md +128 -0
- package/dist/reference/clock.md +150 -0
- package/dist/reference/helpers.md +46 -0
- package/dist/reference/hooks.md +379 -0
- package/dist/reference/index.d.ts +2 -0
- package/dist/reference/index.js +39 -0
- package/dist/reference/loaders.md +164 -0
- package/dist/reference/rect.md +129 -0
- package/dist/reference/runtime.md +478 -0
- package/dist/reference/screen.md +313 -0
- package/dist/reference/storage.md +90 -0
- package/dist/rendering.d.ts +188 -0
- package/dist/runtime-contracts.d.ts +117 -0
- package/dist/runtime-diagnostics.d.ts +37 -0
- package/dist/runtime-persistence.d.ts +32 -0
- package/dist/runtime-source.d.ts +82 -0
- package/dist/runtime.d.ts +81 -0
- package/dist/source-scan.d.ts +49 -0
- package/dist/storage-path.d.ts +3 -0
- package/dist/storage.d.ts +88 -0
- package/dist/tone.d.ts +81 -0
- package/dist/touch-controls.d.ts +24 -0
- package/dist/touch-pointer.d.ts +11 -0
- package/dist/util.d.ts +62 -0
- 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
|
-
|
|
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
|
-
|
|
137
|
+
## License
|
|
6
138
|
|
|
7
|
-
|
|
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;
|
package/dist/actor.d.ts
ADDED
|
@@ -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;
|
package/dist/assets.d.ts
ADDED
|
@@ -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 {};
|