js-game-zero 0.0.0-stage → 0.4.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 +138 -2
- 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-q9m20c2h.js +7972 -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 +8545 -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 +392 -0
- package/dist/reference/storage.md +90 -0
- package/dist/rendering.d.ts +216 -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 +69 -4
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { type AssetLoader, type SurfaceLike } from "./assets.js";
|
|
2
|
+
import { type Color } from "./color.js";
|
|
3
|
+
import { ZRect, type Point, type RectLike } from "./geometry.js";
|
|
4
|
+
export { normalizeColor } from "./color.js";
|
|
5
|
+
export type { Color } from "./color.js";
|
|
6
|
+
export type CanvasLike = {
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly height: number;
|
|
9
|
+
getContext?: (contextId: "2d") => CanvasRenderingContext2D | null;
|
|
10
|
+
};
|
|
11
|
+
export type ScreenOptions = {
|
|
12
|
+
readonly imageLoader?: Pick<AssetLoader<SurfaceLike>, "load" | "get">;
|
|
13
|
+
};
|
|
14
|
+
export type TextAlignOption = "left" | "center" | "right";
|
|
15
|
+
export type TextAnchor = "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "midTop" | "midLeft" | "midRight" | "midBottom" | "center" | readonly [number | HorizontalAnchor, number | VerticalAnchor];
|
|
16
|
+
export type HorizontalAnchor = "left" | "center" | "middle" | "right";
|
|
17
|
+
export type VerticalAnchor = "top" | "center" | "middle" | "bottom";
|
|
18
|
+
export type TextOptions = {
|
|
19
|
+
readonly fontname?: string;
|
|
20
|
+
readonly sysfontname?: string;
|
|
21
|
+
readonly fontsize?: number;
|
|
22
|
+
/** Same as `fontsize`. */
|
|
23
|
+
readonly size?: number;
|
|
24
|
+
readonly bold?: boolean;
|
|
25
|
+
readonly italic?: boolean;
|
|
26
|
+
readonly underline?: boolean;
|
|
27
|
+
readonly color?: Color;
|
|
28
|
+
readonly background?: Color;
|
|
29
|
+
readonly pos?: Point;
|
|
30
|
+
readonly topLeft?: Point;
|
|
31
|
+
readonly topRight?: Point;
|
|
32
|
+
readonly bottomLeft?: Point;
|
|
33
|
+
readonly bottomRight?: Point;
|
|
34
|
+
readonly midTop?: Point;
|
|
35
|
+
readonly midLeft?: Point;
|
|
36
|
+
readonly midRight?: Point;
|
|
37
|
+
readonly midBottom?: Point;
|
|
38
|
+
readonly center?: Point;
|
|
39
|
+
readonly top?: number;
|
|
40
|
+
readonly left?: number;
|
|
41
|
+
readonly bottom?: number;
|
|
42
|
+
readonly right?: number;
|
|
43
|
+
readonly centerX?: number;
|
|
44
|
+
readonly centerY?: number;
|
|
45
|
+
readonly width?: number;
|
|
46
|
+
readonly widthem?: number;
|
|
47
|
+
readonly lineheight?: number;
|
|
48
|
+
readonly align?: TextAlignOption;
|
|
49
|
+
readonly strip?: boolean;
|
|
50
|
+
readonly owidth?: number;
|
|
51
|
+
/** Same as `owidth`. */
|
|
52
|
+
readonly outline?: number;
|
|
53
|
+
readonly ocolor?: Color;
|
|
54
|
+
/** Same as `ocolor`. */
|
|
55
|
+
readonly outlineColor?: Color;
|
|
56
|
+
readonly shadow?: number | Point;
|
|
57
|
+
readonly scolor?: Color;
|
|
58
|
+
/** Same as `scolor`. */
|
|
59
|
+
readonly shadowColor?: Color;
|
|
60
|
+
readonly gcolor?: Color;
|
|
61
|
+
/** Same as `gcolor`. */
|
|
62
|
+
readonly gradientColor?: Color;
|
|
63
|
+
readonly alpha?: number;
|
|
64
|
+
readonly anchor?: TextAnchor;
|
|
65
|
+
readonly angle?: number;
|
|
66
|
+
};
|
|
67
|
+
export type TextboxOptions = Omit<TextOptions, TextPositionKey | "width" | "widthem" | "top" | "left" | "bottom" | "right" | "centerX" | "centerY"> & {
|
|
68
|
+
readonly minFontsize?: number;
|
|
69
|
+
/** Same as `minFontsize`. */
|
|
70
|
+
readonly minSize?: number;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* `screen.draw.text` takes its options either third, after a position, or
|
|
74
|
+
* second, in place of one: `text("Hi", [10, 10], { size: 20 })` and
|
|
75
|
+
* `text("Hi", { center: [160, 90], size: 20 })` both work.
|
|
76
|
+
*/
|
|
77
|
+
export type DrawText = {
|
|
78
|
+
(text: string, options?: TextOptions): void;
|
|
79
|
+
(text: string, pos: Point | undefined, options?: TextOptions): void;
|
|
80
|
+
};
|
|
81
|
+
/** How big `screen.measureText` finds text, as `screen.draw.text` would draw it. */
|
|
82
|
+
export type TextSize = {
|
|
83
|
+
/** Its widest line's width. */
|
|
84
|
+
readonly width: number;
|
|
85
|
+
/** Its lines' height together: as many lines as it has, each `lineheight` tall. */
|
|
86
|
+
readonly height: number;
|
|
87
|
+
/** The lines it breaks into: at each newline, and where a `width` wraps it. */
|
|
88
|
+
readonly lines: readonly string[];
|
|
89
|
+
};
|
|
90
|
+
export type TextLayout = {
|
|
91
|
+
readonly lines: readonly string[];
|
|
92
|
+
readonly font: string;
|
|
93
|
+
readonly fontSize: number;
|
|
94
|
+
readonly lineHeight: number;
|
|
95
|
+
readonly boxWidth: number;
|
|
96
|
+
readonly boxHeight: number;
|
|
97
|
+
readonly textWidth: number;
|
|
98
|
+
readonly align: TextAlignOption;
|
|
99
|
+
readonly topLeft: Point;
|
|
100
|
+
readonly anchorOffset: Point;
|
|
101
|
+
readonly alpha: number;
|
|
102
|
+
};
|
|
103
|
+
type TextPositionKey = "pos" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "midTop" | "midLeft" | "midRight" | "midBottom" | "center";
|
|
104
|
+
/**
|
|
105
|
+
* Sizes the screen to `width` × `height` logical pixels with `density`
|
|
106
|
+
* canvas pixels each, and scales the context so every drawing call keeps
|
|
107
|
+
* working in logical pixels. A density change keeps what's on the canvas,
|
|
108
|
+
* rescaled; a new size starts blank, as setting a canvas's size does.
|
|
109
|
+
*/
|
|
110
|
+
export declare function resizeScreen(screen: Screen, width: number, height: number, density?: number): void;
|
|
111
|
+
/**
|
|
112
|
+
* Restores the density scale if something reset the canvas since the last
|
|
113
|
+
* frame. Code outside the runtime that sets `canvas.width`/`height` means
|
|
114
|
+
* a logical size, so the screen adopts it at the same density.
|
|
115
|
+
*/
|
|
116
|
+
export declare function syncScreen(screen: Screen): void;
|
|
117
|
+
/** A value a drawn thing reports, as JSON. */
|
|
118
|
+
type DrawnValue = string | number | boolean | readonly number[];
|
|
119
|
+
/**
|
|
120
|
+
* One thing a frame drew, as a host reads it through the inspector's
|
|
121
|
+
* `$drawn`: its kind (`fill`, `image`, `actor`, `circle`, `rect`, `polygon`,
|
|
122
|
+
* `line`, `text`, `clear`), and what it is and where, as the game gave them.
|
|
123
|
+
*/
|
|
124
|
+
export type DrawnThing = {
|
|
125
|
+
readonly kind: string;
|
|
126
|
+
readonly [key: string]: DrawnValue;
|
|
127
|
+
};
|
|
128
|
+
/** Starts recording what `screen` draws, afresh: the runtime calls it before each `draw()`. */
|
|
129
|
+
export declare function beginDrawn(screen: Screen): void;
|
|
130
|
+
/** What `screen`'s last `draw()` drew, in order; empty before its first. */
|
|
131
|
+
export declare function drawnOn(screen: Screen): readonly DrawnThing[];
|
|
132
|
+
/** Records one thing drawn, when the screen is recording. */
|
|
133
|
+
export declare function recordDrawn(screen: Screen, thing: DrawnThing): void;
|
|
134
|
+
export declare class Screen {
|
|
135
|
+
readonly canvas: CanvasLike;
|
|
136
|
+
readonly context: CanvasRenderingContext2D;
|
|
137
|
+
readonly imageLoader: Pick<AssetLoader<SurfaceLike>, "load" | "get">;
|
|
138
|
+
readonly draw: {
|
|
139
|
+
line: (start: Point, end: Point, color: Color, width?: number) => void;
|
|
140
|
+
circle: (pos: Point, radius: number, color: Color, width?: number) => void;
|
|
141
|
+
filledCircle: (pos: Point, radius: number, color: Color) => void;
|
|
142
|
+
polygon: (points: Point[], color: Color, width?: number) => void;
|
|
143
|
+
filledPolygon: (points: Point[], color: Color) => void;
|
|
144
|
+
rect: (rect: RectLike, color: Color, width?: number) => void;
|
|
145
|
+
filledRect: (rect: RectLike, color: Color) => void;
|
|
146
|
+
/** Its corners are rounded `radius` round, at most half its shorter side. */
|
|
147
|
+
roundedRect: (rect: RectLike, radius: number, color: Color, width?: number) => void;
|
|
148
|
+
filledRoundedRect: (rect: RectLike, radius: number, color: Color) => void;
|
|
149
|
+
text: DrawText;
|
|
150
|
+
textbox: (text: string, rect: RectLike, options?: TextboxOptions) => void;
|
|
151
|
+
/** Same as `screen.blit`: draws an image with its top-left at `pos`. */
|
|
152
|
+
image: (image: string | SurfaceLike, pos: Point | RectLike) => Promise<void> | void;
|
|
153
|
+
};
|
|
154
|
+
constructor(canvas: CanvasLike, context?: CanvasRenderingContext2D | null, options?: ScreenOptions);
|
|
155
|
+
/** The screen's width in logical pixels: the game's `WIDTH`. */
|
|
156
|
+
get width(): number;
|
|
157
|
+
/** The screen's height in logical pixels: the game's `HEIGHT`. */
|
|
158
|
+
get height(): number;
|
|
159
|
+
/** Canvas pixels per logical pixel along each side. */
|
|
160
|
+
get density(): number;
|
|
161
|
+
bounds(): ZRect;
|
|
162
|
+
clear(): void;
|
|
163
|
+
fill(color: Color, gcolor?: Color): void;
|
|
164
|
+
blit(image: string | SurfaceLike, pos: Point | RectLike): Promise<void> | void;
|
|
165
|
+
/**
|
|
166
|
+
* The short forms take positions as separate numbers rather than `[x, y]`
|
|
167
|
+
* lists, fill their shapes, and default to white, so
|
|
168
|
+
* `screen.circle(200, 150, 25, "gold")` fits a phone-width line where
|
|
169
|
+
* `screen.draw.filledCircle([200, 150], 25, "gold")` doesn't. The
|
|
170
|
+
* outline versions stay under `screen.draw`.
|
|
171
|
+
*/
|
|
172
|
+
circle(x: number, y: number, radius: number, color?: Color): void;
|
|
173
|
+
/** A filled rectangle whose top-left corner is at `x, y`. */
|
|
174
|
+
rect(x: number, y: number, width: number, height: number, color?: Color): void;
|
|
175
|
+
/**
|
|
176
|
+
* A filled rectangle like `screen.rect`'s, with its corners rounded
|
|
177
|
+
* `radius` round. The radius comes before the color, as a circle's does.
|
|
178
|
+
*/
|
|
179
|
+
roundedRect(x: number, y: number, width: number, height: number, radius: number, color?: Color): void;
|
|
180
|
+
triangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, color?: Color): void;
|
|
181
|
+
line(x1: number, y1: number, x2: number, y2: number, color?: Color, width?: number): void;
|
|
182
|
+
/**
|
|
183
|
+
* Text whose top-left corner is at `x, y`, `size` pixels tall, so a score
|
|
184
|
+
* fits one short line: `screen.text("Score: " + score, 20, 30)`. A number
|
|
185
|
+
* is drawn as it reads. Placing by its center or any other option stays
|
|
186
|
+
* under `screen.draw.text`.
|
|
187
|
+
*/
|
|
188
|
+
text(text: string | number, x: number, y: number, color?: Color, size?: number): void;
|
|
189
|
+
/**
|
|
190
|
+
* How big `screen.draw.text` would draw `text`, measured the way it lays
|
|
191
|
+
* it out: its widest line's width, its lines' height together, and the
|
|
192
|
+
* lines. It takes what `screen.draw.text` takes, so a label is measured
|
|
193
|
+
* with the arguments it's drawn with; where it goes doesn't change its
|
|
194
|
+
* size. It draws nothing.
|
|
195
|
+
*/
|
|
196
|
+
measureText(text: string | number, options?: TextOptions): TextSize;
|
|
197
|
+
measureText(text: string | number, pos: Point | undefined, options?: TextOptions): TextSize;
|
|
198
|
+
private drawSurface;
|
|
199
|
+
private drawLine;
|
|
200
|
+
private drawCircle;
|
|
201
|
+
private drawPolygon;
|
|
202
|
+
private drawRect;
|
|
203
|
+
/** `call` is how the method that drew it is called, for its errors. */
|
|
204
|
+
private drawRoundedRect;
|
|
205
|
+
private drawText;
|
|
206
|
+
private drawTextbox;
|
|
207
|
+
}
|
|
208
|
+
export declare function setScreen(screen: Screen | null): Screen | null;
|
|
209
|
+
export declare function getScreen(): Screen;
|
|
210
|
+
export declare const screen: Screen;
|
|
211
|
+
export declare function layoutText(context: CanvasRenderingContext2D, text: string, pos: Point | undefined, options?: TextOptions): TextLayout;
|
|
212
|
+
export declare function fitTextboxFontSize(context: CanvasRenderingContext2D, text: string, rectLike: RectLike, options?: TextboxOptions): {
|
|
213
|
+
fontSize: number;
|
|
214
|
+
lineHeight: number;
|
|
215
|
+
lines: readonly string[];
|
|
216
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { AssetLoaders, AssetUrls, FontAsset, MusicAsset, SoundAsset, SurfaceLike } from "./assets.js";
|
|
2
|
+
import type { InputHooks, MappedKeyboardEvent, MappedMouseEvent } from "./events.js";
|
|
3
|
+
import type { Storage, StorageAdapter } from "./storage.js";
|
|
4
|
+
import type { TouchControlsConfig } from "./touch-controls.js";
|
|
5
|
+
export type GameModule = InputHooks & {
|
|
6
|
+
readonly WIDTH?: number;
|
|
7
|
+
readonly HEIGHT?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Canvas pixels per logical pixel. Defaults to the device's pixel ratio,
|
|
10
|
+
* at most 3, so the game is sharp on high-density screens.
|
|
11
|
+
*/
|
|
12
|
+
readonly PIXEL_DENSITY?: number;
|
|
13
|
+
readonly TITLE?: string;
|
|
14
|
+
readonly ICON?: string | null;
|
|
15
|
+
readonly TOUCH_CONTROLS?: TouchControlsConfig;
|
|
16
|
+
readonly draw?: () => void;
|
|
17
|
+
/** Called every frame with the seconds since the last one. */
|
|
18
|
+
readonly update?: (dt: number) => void;
|
|
19
|
+
};
|
|
20
|
+
export type RuntimeMode = "global" | "import";
|
|
21
|
+
export type RuntimeDiagnostic = {
|
|
22
|
+
readonly code: "asset-name" | "constant-typo" | "duplicate-config" | "hook-arity" | "hook-parameter" | "hook-typo" | "invalid-config" | "runtime-error";
|
|
23
|
+
readonly severity: "warning" | "error";
|
|
24
|
+
readonly message: string;
|
|
25
|
+
readonly name?: string;
|
|
26
|
+
readonly suggestion?: string;
|
|
27
|
+
readonly phase?: RuntimeFailurePhase;
|
|
28
|
+
/**
|
|
29
|
+
* The 1-based line in the game source the diagnostic is about, when known:
|
|
30
|
+
* where a `runtime-error` was thrown, or where a misspelled hook or
|
|
31
|
+
* setting is declared.
|
|
32
|
+
*/
|
|
33
|
+
readonly line?: number;
|
|
34
|
+
readonly recoverable?: boolean;
|
|
35
|
+
};
|
|
36
|
+
export type RuntimeFailurePhase = "startup" | "input" | "clock" | "update" | "draw" | "asset" | "storage" | "audio";
|
|
37
|
+
export type RuntimeFailure = {
|
|
38
|
+
readonly phase: RuntimeFailurePhase;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly cause: unknown;
|
|
41
|
+
/** 1-based line in the game source where the error was thrown, when the engine reports one. */
|
|
42
|
+
readonly line?: number;
|
|
43
|
+
};
|
|
44
|
+
export type GameHandleState = "starting" | "running" | "paused" | "failed" | "stopped";
|
|
45
|
+
export type RuntimeScheduler = {
|
|
46
|
+
requestAnimationFrame(callback: (timestamp: number) => void): unknown;
|
|
47
|
+
cancelAnimationFrame(handle: unknown): void;
|
|
48
|
+
now?(): number;
|
|
49
|
+
};
|
|
50
|
+
export type RuntimeCanvas = HTMLCanvasElement & {
|
|
51
|
+
getBoundingClientRect?: () => {
|
|
52
|
+
left: number;
|
|
53
|
+
top: number;
|
|
54
|
+
width?: number;
|
|
55
|
+
height?: number;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export type RunOptions = Partial<GameModule> & {
|
|
59
|
+
readonly assetBaseUrl?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The game's assets by path (`images/cat.svg`), each with its URL, such as
|
|
62
|
+
* a data URL. When set, assets load only from here, never from
|
|
63
|
+
* `assetBaseUrl`, so a page can run a game with no server.
|
|
64
|
+
*/
|
|
65
|
+
readonly assetUrls?: AssetUrls;
|
|
66
|
+
readonly autostart?: boolean;
|
|
67
|
+
readonly canvas?: RuntimeCanvas;
|
|
68
|
+
readonly container?: HTMLElement;
|
|
69
|
+
readonly diagnostics?: (diagnostic: RuntimeDiagnostic) => void;
|
|
70
|
+
readonly keyboardTarget?: EventTarget;
|
|
71
|
+
/**
|
|
72
|
+
* Where mouse releases (`mouseup`) are heard. Defaults to `window`, so a
|
|
73
|
+
* drag released outside the canvas still ends; a host running a game it
|
|
74
|
+
* drives itself passes the canvas, so its synthetic releases reach no
|
|
75
|
+
* other game on the page.
|
|
76
|
+
*/
|
|
77
|
+
readonly mouseReleaseTarget?: EventTarget;
|
|
78
|
+
readonly preload?: readonly string[];
|
|
79
|
+
readonly preloadedAssets?: PreloadedAssets;
|
|
80
|
+
readonly projectId?: string;
|
|
81
|
+
readonly scheduler?: RuntimeScheduler;
|
|
82
|
+
/**
|
|
83
|
+
* Makes the game's randomness repeatable: `Math.random` as a zero-boilerplate
|
|
84
|
+
* game sees it, and `randomInt` and `randomChoice`, follow one sequence for
|
|
85
|
+
* this seed, the same one `jgz test --seed` uses. Only this game's: the
|
|
86
|
+
* page's `Math.random` and other games are untouched. Up to nine plain
|
|
87
|
+
* digits are the number they spell; anything else is hashed.
|
|
88
|
+
*/
|
|
89
|
+
readonly seed?: string | number;
|
|
90
|
+
readonly storage?: Storage | StorageAdapter;
|
|
91
|
+
/** Context-owned loader overrides for embedding hosts and deterministic tests. */
|
|
92
|
+
readonly loaders?: Partial<AssetLoaders>;
|
|
93
|
+
};
|
|
94
|
+
export type PreloadedAssets = {
|
|
95
|
+
readonly images?: Record<string, SurfaceLike>;
|
|
96
|
+
readonly sounds?: Record<string, SoundAsset>;
|
|
97
|
+
readonly music?: Record<string, MusicAsset>;
|
|
98
|
+
readonly fonts?: Record<string, FontAsset>;
|
|
99
|
+
};
|
|
100
|
+
export type GameKeyEvent = Readonly<MappedKeyboardEvent>;
|
|
101
|
+
export type GameMouseEvent = Readonly<MappedMouseEvent>;
|
|
102
|
+
export type DefinedGameModule = {
|
|
103
|
+
readonly WIDTH?: number;
|
|
104
|
+
readonly HEIGHT?: number;
|
|
105
|
+
readonly PIXEL_DENSITY?: number;
|
|
106
|
+
readonly TITLE?: string;
|
|
107
|
+
readonly ICON?: string | null;
|
|
108
|
+
readonly TOUCH_CONTROLS?: TouchControlsConfig;
|
|
109
|
+
readonly draw?: () => void;
|
|
110
|
+
readonly update?: (dt: number) => void;
|
|
111
|
+
readonly onKeyDown?: (event: GameKeyEvent) => void;
|
|
112
|
+
readonly onKeyUp?: (event: GameKeyEvent) => void;
|
|
113
|
+
readonly onMouseDown?: (event: GameMouseEvent) => void;
|
|
114
|
+
readonly onMouseUp?: (event: GameMouseEvent) => void;
|
|
115
|
+
readonly onMouseMove?: (event: GameMouseEvent) => void;
|
|
116
|
+
readonly onMusicEnd?: () => void;
|
|
117
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { GameModule, RunOptions, RuntimeDiagnostic, RuntimeMode } from "./runtime-contracts.js";
|
|
2
|
+
export declare const RUNTIME_CONFIGURATION_NAMES: readonly ["WIDTH", "HEIGHT", "PIXEL_DENSITY", "TITLE", "ICON", "TOUCH_CONTROLS"];
|
|
3
|
+
/** Every hook a zero-boilerplate script can define. */
|
|
4
|
+
export declare const RUNTIME_SOURCE_HOOK_NAMES: readonly ["draw", "update", "onMouseDown", "onMouseUp", "onMouseMove", "onKeyDown", "onKeyUp", "onMusicEnd"];
|
|
5
|
+
export declare const RUNTIME_MODULE_OPTION_NAMES: readonly ["WIDTH", "HEIGHT", "PIXEL_DENSITY", "TITLE", "ICON", "TOUCH_CONTROLS", "draw", "update", "onMouseDown", "onMouseUp", "onMouseMove", "onKeyDown", "onKeyUp", "onMusicEnd"];
|
|
6
|
+
export declare function collectRuntimeDiagnostics(moduleOrSource: GameModule | string, options?: RunOptions): RuntimeDiagnostic[];
|
|
7
|
+
export declare function diagnoseGameModule(module: GameModule, options: RunOptions, emit: (diagnostic: RuntimeDiagnostic) => void, mode: RuntimeMode,
|
|
8
|
+
/** run() stops a game whose draw() takes parameters, and says so: a
|
|
9
|
+
* warning as well would be the same mistake twice. */
|
|
10
|
+
drawArityIsFatal?: boolean): void;
|
|
11
|
+
/**
|
|
12
|
+
* Static checks on zero-boilerplate source. Typo checks only look at what the
|
|
13
|
+
* script defines at top level — a top-level function whose name is nearly a
|
|
14
|
+
* hook name, or a top-level assignment whose name is nearly a configuration
|
|
15
|
+
* constant — never at identifiers the script merely uses (`row`, `col`,
|
|
16
|
+
* `rows[y]`), and never when the real name is also defined (`draw` plus a
|
|
17
|
+
* `drawn` helper is not a typo). Asset-name checks scan every quoted string.
|
|
18
|
+
*/
|
|
19
|
+
export declare function diagnoseSource(source: string, emit: (diagnostic: RuntimeDiagnostic) => void): void;
|
|
20
|
+
/**
|
|
21
|
+
* The 1-based line where a game's source declares `name` at the start of a
|
|
22
|
+
* line: `function name(`, `name = ` or `let name = `.
|
|
23
|
+
*/
|
|
24
|
+
export declare function declarationLine(source: string, name: string): number | undefined;
|
|
25
|
+
export declare function createDiagnosticEmitter(diagnostics: RuntimeDiagnostic[], callback?: (diagnostic: RuntimeDiagnostic) => void): (diagnostic: RuntimeDiagnostic) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Ambient diagnostic sink for whichever run is active at call time, mirroring
|
|
28
|
+
* the active-service pattern used for loaders/storage/clock. Only valid for
|
|
29
|
+
* the duration of the synchronous call that reads it — callers whose work
|
|
30
|
+
* continues asynchronously (e.g. a loader promise that may reject later)
|
|
31
|
+
* must capture the returned function and close over it rather than reading
|
|
32
|
+
* the ambient value again once the run may have moved on.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setActiveDiagnosticEmit(emit: ((diagnostic: RuntimeDiagnostic) => void) | null): ((diagnostic: RuntimeDiagnostic) => void) | null;
|
|
35
|
+
export declare function getActiveDiagnosticEmit(): ((diagnostic: RuntimeDiagnostic) => void) | null;
|
|
36
|
+
export declare function describeRuntimeError(error: unknown): string;
|
|
37
|
+
export declare function isValidRuntimeDimension(value: unknown): value is number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Storage } from "./storage.js";
|
|
2
|
+
type PersistenceDiagnostic = {
|
|
3
|
+
readonly code: "runtime-error";
|
|
4
|
+
readonly severity: "warning";
|
|
5
|
+
readonly phase: "storage";
|
|
6
|
+
readonly recoverable: true;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
};
|
|
9
|
+
type PersistenceDiagnosticEmitter = (diagnostic: PersistenceDiagnostic) => void;
|
|
10
|
+
export type Autosave = {
|
|
11
|
+
/**
|
|
12
|
+
* Cancels any pending debounce timer and saves immediately, if anything
|
|
13
|
+
* changed since the last save or load, inside stored objects and lists too.
|
|
14
|
+
*/
|
|
15
|
+
flush(): Promise<void>;
|
|
16
|
+
/** Unsubscribes from storage changes and browser lifecycle events. */
|
|
17
|
+
detach(): void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Coordinates debounced and lifecycle-triggered persistence for one runtime.
|
|
21
|
+
* Writes are serialized and mutations that arrive while a save is active are
|
|
22
|
+
* folded into a subsequent write.
|
|
23
|
+
*/
|
|
24
|
+
export declare function attachAutosave(storage: Storage, emit: PersistenceDiagnosticEmitter): Autosave;
|
|
25
|
+
/** Loads exactly once, preferring an adapter's truly synchronous path. */
|
|
26
|
+
export declare function primeStorage(storage: Storage, emit: PersistenceDiagnosticEmitter): {
|
|
27
|
+
state: {
|
|
28
|
+
current: boolean;
|
|
29
|
+
};
|
|
30
|
+
promise: Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { AssetLoaders } from "./assets.js";
|
|
2
|
+
import type { MusicPlayer } from "./audio.js";
|
|
3
|
+
import { Clock } from "./clock.js";
|
|
4
|
+
import { type KeyboardState, type MouseState } from "./input.js";
|
|
5
|
+
import { type RandomSource } from "./random.js";
|
|
6
|
+
import { Screen } from "./rendering.js";
|
|
7
|
+
import type { GameModule, RuntimeDiagnostic } from "./runtime-contracts.js";
|
|
8
|
+
import { Storage } from "./storage.js";
|
|
9
|
+
import type { ToneGenerator } from "./tone.js";
|
|
10
|
+
export type SourceEvaluationContext = {
|
|
11
|
+
readonly screen: Screen;
|
|
12
|
+
readonly clock: Clock;
|
|
13
|
+
readonly keyboard: KeyboardState;
|
|
14
|
+
readonly mouseState: MouseState;
|
|
15
|
+
readonly loaders: AssetLoaders;
|
|
16
|
+
readonly music: MusicPlayer;
|
|
17
|
+
readonly storage: Storage;
|
|
18
|
+
readonly tone: ToneGenerator;
|
|
19
|
+
/** The run's seeded random source, or null to leave `Math` as it is. */
|
|
20
|
+
readonly random: RandomSource | null;
|
|
21
|
+
stop: (() => void) | null;
|
|
22
|
+
};
|
|
23
|
+
export type SourceInspector = {
|
|
24
|
+
/** Non-function top-level declarations and assignments. */
|
|
25
|
+
bindings(): readonly string[];
|
|
26
|
+
/** Top-level function declarations and function-valued bindings. */
|
|
27
|
+
functions(): readonly string[];
|
|
28
|
+
read(name: string): unknown;
|
|
29
|
+
};
|
|
30
|
+
type SourceEvaluation = {
|
|
31
|
+
readonly module: GameModule;
|
|
32
|
+
readonly inspector: SourceInspector;
|
|
33
|
+
};
|
|
34
|
+
/** Evaluates one zero-boilerplate script inside its run-owned service scope. */
|
|
35
|
+
export declare function executeZeroBoilerplateSource(source: string, context: SourceEvaluationContext, emit: (diagnostic: RuntimeDiagnostic) => void): SourceEvaluation;
|
|
36
|
+
/**
|
|
37
|
+
* The one line of `source` that reads `name` (outside strings and comments,
|
|
38
|
+
* and not as a property after a dot), or undefined when it's on none or on
|
|
39
|
+
* more than one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function onlyLineOf(name: string, source: string): number | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* How this engine numbers lines in `new Function` code: from the place of a
|
|
44
|
+
* stack frame (V8, SpiderMonkey), or, where a frame there has no location
|
|
45
|
+
* (JavaScriptCore), from the error's own `line`.
|
|
46
|
+
*/
|
|
47
|
+
export type FrameCalibration = {
|
|
48
|
+
readonly kind: "stack";
|
|
49
|
+
readonly marker: string;
|
|
50
|
+
readonly offset: number;
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: "line";
|
|
53
|
+
readonly offset: number;
|
|
54
|
+
/** The `sourceURL` errors from game code carry: none, on JavaScriptCore. */
|
|
55
|
+
readonly url: string | undefined;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The calibration a throw from the first line of game source gives. The
|
|
59
|
+
* throwing frame is the stack's first frame, after V8's `Error: message` line:
|
|
60
|
+
* when it has a location, its place marks game frames; when it has none
|
|
61
|
+
* (JavaScriptCore's `anonymous@`), the error's own `line` is used instead.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readCalibration(error: unknown): FrameCalibration | null;
|
|
64
|
+
/** The game source's line for `error` under `calibration`, or undefined when it wasn't thrown there. */
|
|
65
|
+
export declare function lineWith(calibration: FrameCalibration, error: unknown): number | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* The 1-based line of the syntax error `message` in `source`, for engines that
|
|
68
|
+
* give none: the first line whose prefix of the source fails with the same
|
|
69
|
+
* message. An error at the end of the source (something left open) is placed
|
|
70
|
+
* on the line after the last prefix that compiles, where the open part starts.
|
|
71
|
+
*/
|
|
72
|
+
export declare function syntaxErrorLine(source: string, message: string): number | undefined;
|
|
73
|
+
/** Records the source line of a mistake the runtime itself finds in a
|
|
74
|
+
* game, which no stack trace could place. */
|
|
75
|
+
export declare function noteSourceLine(error: object, line: number): void;
|
|
76
|
+
/**
|
|
77
|
+
* The 1-based line in the game source where `error` was thrown, or undefined
|
|
78
|
+
* when it wasn't thrown in game code (the error came from the library, or the
|
|
79
|
+
* engine gives no usable position).
|
|
80
|
+
*/
|
|
81
|
+
export declare function sourceLineOf(error: unknown): number | undefined;
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Actor } from "./actor.js";
|
|
2
|
+
import { Animation, animate } from "./animation.js";
|
|
3
|
+
import { type AssetLoaders } from "./assets.js";
|
|
4
|
+
import { Clock } from "./clock.js";
|
|
5
|
+
import { Rect, ZRect } from "./geometry.js";
|
|
6
|
+
import { KeyboardState, keymods, keys, type LiveMouse } from "./input.js";
|
|
7
|
+
import { createMusicPlayer } from "./audio.js";
|
|
8
|
+
import { Screen } from "./rendering.js";
|
|
9
|
+
import { Storage } from "./storage.js";
|
|
10
|
+
import { createToneGenerator } from "./tone.js";
|
|
11
|
+
import { type SourceInspector } from "./runtime-source.js";
|
|
12
|
+
import type { DefinedGameModule, GameHandleState, GameModule, RunOptions, RuntimeCanvas, RuntimeDiagnostic, RuntimeFailure, RuntimeMode } from "./runtime-contracts.js";
|
|
13
|
+
export type { DefinedGameModule, GameHandleState, GameKeyEvent, GameModule, GameMouseEvent, PreloadedAssets, RunOptions, RuntimeCanvas, RuntimeDiagnostic, RuntimeFailure, RuntimeFailurePhase, RuntimeMode, RuntimeScheduler, } from "./runtime-contracts.js";
|
|
14
|
+
export { collectRuntimeDiagnostics } from "./runtime-diagnostics.js";
|
|
15
|
+
export type GameContext = {
|
|
16
|
+
readonly Actor: typeof Actor;
|
|
17
|
+
readonly Animation: typeof Animation;
|
|
18
|
+
readonly Rect: typeof Rect;
|
|
19
|
+
readonly ZRect: typeof ZRect;
|
|
20
|
+
readonly Screen: typeof Screen;
|
|
21
|
+
readonly Storage: typeof Storage;
|
|
22
|
+
readonly screen: Screen;
|
|
23
|
+
readonly clock: Clock;
|
|
24
|
+
readonly keyboard: KeyboardState;
|
|
25
|
+
readonly keymods: typeof keymods;
|
|
26
|
+
readonly keys: typeof keys;
|
|
27
|
+
readonly mouse: LiveMouse;
|
|
28
|
+
readonly images: AssetLoaders["images"];
|
|
29
|
+
readonly sounds: AssetLoaders["sounds"];
|
|
30
|
+
readonly music: ReturnType<typeof createMusicPlayer>;
|
|
31
|
+
readonly fonts: AssetLoaders["fonts"];
|
|
32
|
+
readonly storage: Storage;
|
|
33
|
+
readonly tone: ReturnType<typeof createToneGenerator>;
|
|
34
|
+
readonly animate: typeof animate;
|
|
35
|
+
readonly exit: typeof exit;
|
|
36
|
+
};
|
|
37
|
+
declare const definedGameBrand: unique symbol;
|
|
38
|
+
export type DefinedGame = {
|
|
39
|
+
readonly [definedGameBrand]: true;
|
|
40
|
+
readonly create: (context: GameContext) => DefinedGameModule;
|
|
41
|
+
};
|
|
42
|
+
export declare function defineGame(factory: (context: GameContext) => DefinedGameModule): DefinedGame;
|
|
43
|
+
export type GameHandle = {
|
|
44
|
+
readonly mode: RuntimeMode;
|
|
45
|
+
readonly canvas: RuntimeCanvas;
|
|
46
|
+
readonly screen: Screen;
|
|
47
|
+
readonly clock: Clock;
|
|
48
|
+
readonly keyboard: KeyboardState;
|
|
49
|
+
readonly loaders: AssetLoaders;
|
|
50
|
+
readonly storage: Storage;
|
|
51
|
+
readonly diagnostics: readonly RuntimeDiagnostic[];
|
|
52
|
+
readonly module: GameModule;
|
|
53
|
+
readonly state: GameHandleState;
|
|
54
|
+
readonly failure: RuntimeFailure | null;
|
|
55
|
+
/**
|
|
56
|
+
* Resolves once the "preload gate" is satisfied: storage has been
|
|
57
|
+
* loaded and every asset requested while the game module was
|
|
58
|
+
* evaluated (Actor constructions, `images.x` touches, etc.) has
|
|
59
|
+
* settled. The runtime itself already holds frames until this is
|
|
60
|
+
* true; this is exposed for tests and host integrations that want to
|
|
61
|
+
* await readiness explicitly instead of polling `step()`.
|
|
62
|
+
*/
|
|
63
|
+
readonly ready: Promise<void>;
|
|
64
|
+
readonly done: Promise<void>;
|
|
65
|
+
pause(): void;
|
|
66
|
+
resume(): void;
|
|
67
|
+
step(dt: number): void;
|
|
68
|
+
stop(): void;
|
|
69
|
+
dispose(): Promise<void>;
|
|
70
|
+
restart(source?: GameModule | string | DefinedGame): Promise<GameHandle>;
|
|
71
|
+
/** @deprecated Prefer restart(), which awaits cleanup before starting again. */
|
|
72
|
+
reload(source?: GameModule | string | DefinedGame): GameHandle;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The source bindings of a live zero-boilerplate game, or null. Hosts use
|
|
76
|
+
* `getGameInspector` from `js-game-zero/host`, which reads paths and
|
|
77
|
+
* serialises values on top of this.
|
|
78
|
+
*/
|
|
79
|
+
export declare function getSourceInspector(handle: GameHandle): SourceInspector | null;
|
|
80
|
+
export declare function run(moduleOrSource: GameModule | string | DefinedGame, options?: RunOptions): GameHandle;
|
|
81
|
+
export declare function exit(): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small lexical scan of zero-boilerplate game source, used by the typo
|
|
3
|
+
* diagnostics to find the names a script actually *defines* at top level
|
|
4
|
+
* (as opposed to every identifier it merely mentions). It is deliberately
|
|
5
|
+
* not a parser: it tokenizes comments, strings, template literals (with
|
|
6
|
+
* nested substitutions), regex literals, identifiers and punctuators while
|
|
7
|
+
* tracking brace depth, and pattern-matches definitions on the token stream.
|
|
8
|
+
*
|
|
9
|
+
* On anything it cannot make sense of (an unterminated string, comment,
|
|
10
|
+
* template or regex, or unbalanced braces/parens) the scan reports itself
|
|
11
|
+
* incomplete and callers emit nothing: no diagnostic beats a wrong one.
|
|
12
|
+
*/
|
|
13
|
+
export type SourceTokenType = "identifier" | "punctuator" | "literal";
|
|
14
|
+
export type SourceToken = {
|
|
15
|
+
readonly type: SourceTokenType;
|
|
16
|
+
readonly value: string;
|
|
17
|
+
/** Brace depth at the token: 0 means outside every `{…}` and `${…}`. */
|
|
18
|
+
readonly depth: number;
|
|
19
|
+
/** Parenthesis depth at the token: 0 means outside every `(…)`. */
|
|
20
|
+
readonly parens: number;
|
|
21
|
+
/** Square-bracket depth at the token: 0 means outside every `[…]`. */
|
|
22
|
+
readonly brackets: number;
|
|
23
|
+
};
|
|
24
|
+
export type SourceScan = {
|
|
25
|
+
/** False when the tokenizer gave up; the name lists are then empty. */
|
|
26
|
+
readonly complete: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Names given a function value at depth 0: `function NAME(`, and
|
|
29
|
+
* `NAME = function…`, `NAME = async …`, `NAME = (…) =>`, `NAME = x =>`
|
|
30
|
+
* (with or without `const`/`let`/`var`).
|
|
31
|
+
*/
|
|
32
|
+
readonly topLevelFunctions: readonly string[];
|
|
33
|
+
/** Depth-0 assignment and declaration targets (`NAME = …`, `let NAME`). */
|
|
34
|
+
readonly topLevelAssignments: readonly string[];
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Splits `source` into tokens, or returns null when it cannot be tokenized
|
|
38
|
+
* confidently. Comments vanish; strings, templates, regexes and numbers
|
|
39
|
+
* become `literal` tokens (the code inside template substitutions is
|
|
40
|
+
* tokenized normally, one brace deeper than the template).
|
|
41
|
+
*/
|
|
42
|
+
export declare function tokenizeSource(source: string,
|
|
43
|
+
/** Return the tokens of source that leaves a bracket open at the end,
|
|
44
|
+
* rather than null, to find out what's open. */
|
|
45
|
+
{ allowUnclosed }?: {
|
|
46
|
+
readonly allowUnclosed?: boolean;
|
|
47
|
+
}): SourceToken[] | null;
|
|
48
|
+
/** Finds the names `source` defines at top level; see {@link SourceScan}. */
|
|
49
|
+
export declare function scanSource(source: string): SourceScan;
|