hayao 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/app/browser.d.ts +19 -0
- package/dist/app/game.d.ts +34 -0
- package/dist/art/palette.d.ts +20 -0
- package/dist/art/shapes.d.ts +12 -0
- package/dist/audio/audio.d.ts +42 -0
- package/dist/core/clock.d.ts +37 -0
- package/dist/core/events.d.ts +23 -0
- package/dist/core/hash.d.ts +6 -0
- package/dist/core/math.d.ts +44 -0
- package/dist/core/rng.d.ts +38 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +2844 -0
- package/dist/index.js.map +7 -0
- package/dist/input/actions.d.ts +40 -0
- package/dist/input/source.d.ts +25 -0
- package/dist/physics/aabb.d.ts +38 -0
- package/dist/physics/platformer.d.ts +112 -0
- package/dist/physics/raycast.d.ts +22 -0
- package/dist/physics/spatialHash.d.ts +15 -0
- package/dist/physics/tilemap.d.ts +38 -0
- package/dist/render/canvas.d.ts +19 -0
- package/dist/render/commands.d.ts +65 -0
- package/dist/render/headless.d.ts +17 -0
- package/dist/render/renderer.d.ts +16 -0
- package/dist/render/svg.d.ts +16 -0
- package/dist/render/svgString.d.ts +5 -0
- package/dist/scene/node.d.ts +100 -0
- package/dist/scene/nodes.d.ts +90 -0
- package/dist/scene/particles.d.ts +78 -0
- package/dist/scene/registry.d.ts +5 -0
- package/dist/scene/tween.d.ts +20 -0
- package/dist/ui/overlay.d.ts +25 -0
- package/dist/ui/settings.d.ts +23 -0
- package/dist/ui/shell.d.ts +19 -0
- package/dist/verify/bot.d.ts +21 -0
- package/dist/verify/capture.d.ts +21 -0
- package/dist/verify/determinism.d.ts +29 -0
- package/dist/verify/driver.d.ts +23 -0
- package/dist/verify/feel.d.ts +25 -0
- package/dist/verify/filmstrip.d.ts +19 -0
- package/dist/verify/playthrough.d.ts +21 -0
- package/dist/verify/solver.d.ts +37 -0
- package/dist/world.d.ts +75 -0
- package/docs/API.md +226 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jan Pacan (hellojanpacan)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# hayao.js
|
|
2
|
+
|
|
3
|
+
[](https://github.com/hellojanpacan/hayao-js/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
**An AI-first game engine.** A deterministic, headless-native simulation kernel
|
|
6
|
+
with a Godot-style scene tree, pluggable renderers (SVG / Canvas / headless),
|
|
7
|
+
and a built-in verification harness — designed so an LLM can author, test, and
|
|
8
|
+
*prove correct* a whole game without ever opening a browser.
|
|
9
|
+
|
|
10
|
+
## The one idea
|
|
11
|
+
|
|
12
|
+
> The game is a pure, deterministic function of its inputs. Rendering, audio,
|
|
13
|
+
> and the browser are **observer plugins**: they watch that function and paint
|
|
14
|
+
> what it produces, but they can never change its result.
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
state₀ ──step(inputs₀)──▶ state₁ ──step(inputs₁)──▶ state₂ ──▶ …
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Hold that invariant and every hard problem for an AI author collapses:
|
|
21
|
+
|
|
22
|
+
| Normally hard for an AI | Why it's easy here |
|
|
23
|
+
|---|---|
|
|
24
|
+
| "Does my game work?" | `world.step()` runs in Node. Assert on state. No browser, no pixels. |
|
|
25
|
+
| "Is this level winnable?" | `step()` is a pure transition → BFS/DFS search proves it. |
|
|
26
|
+
| "Did my refactor change behavior?" | Replay recorded inputs, compare `world.hash()`. Identical or it's a bug. |
|
|
27
|
+
| "Undo / time-travel / replay" | `world.snapshot()` / `world.restore()`, or replay the input log. Free. |
|
|
28
|
+
| "Testing a canvas game is opaque" | Rendering is a *projection* of state; the sim never needed the canvas. |
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install
|
|
34
|
+
npm run dev # vite dev server (hub + examples, MPA mode)
|
|
35
|
+
npm test # vitest (headless, runs in Node)
|
|
36
|
+
npm run build # tsc -b && vite build
|
|
37
|
+
npm run check # tsc --noEmit — typecheck only
|
|
38
|
+
npm run verify # run the determinism + solver harness over all examples
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A game is one folder under `examples/<slug>/`, imports **only** from `@hayao`,
|
|
42
|
+
and is a `defineGame()` call. See [docs/CONVENTIONS.md](docs/CONVENTIONS.md).
|
|
43
|
+
|
|
44
|
+
## The layers
|
|
45
|
+
|
|
46
|
+
Everything is one npm package behind a single barrel — **`@hayao`**. Games
|
|
47
|
+
import only from there; the internals are swappable behind it and the whole
|
|
48
|
+
public surface is greppable in one file (`src/index.ts`).
|
|
49
|
+
|
|
50
|
+
| Layer | Modules | Role |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| **app/** | `runBrowser` (rAF loop) · `runHeadless` | plugs the kernel into a host |
|
|
53
|
+
| **ui/** | DOM overlays: menus, HUD, pause/settings shell | observer (never mutates sim) |
|
|
54
|
+
| **render/** | display list → `SvgRenderer` \| `Canvas2DRenderer` \| `HeadlessRenderer` | observer (projection → paint) |
|
|
55
|
+
| **audio/** | procedural Web Audio bus (no-op in Node) | observer |
|
|
56
|
+
| **verify/** | probes · replay · `assertDeterministic` · solver | the AI-first harness |
|
|
57
|
+
| **scene/** | `Node`, `Node2D`, `Sprite`, `Text`, `Camera2D`, `Timer`, `AnimationPlayer`, behaviors | **THE STATE** |
|
|
58
|
+
| **input/** | action map · per-step sampling · record / replay | deterministic |
|
|
59
|
+
| **core/** | `Rng` · `Clock` · `EventBus` · `World` · state hash | **THE KERNEL** (headless, pure) |
|
|
60
|
+
|
|
61
|
+
`core + scene + input` are **deterministic and run in Node**. `render + audio +
|
|
62
|
+
ui + app` are the browser-only observer shell. Break that boundary and the
|
|
63
|
+
verification harness stops being able to prove anything.
|
|
64
|
+
|
|
65
|
+
## Why not Godot or a raw canvas engine?
|
|
66
|
+
|
|
67
|
+
Godot's authoring model (nodes, signals, scenes, resources, input actions,
|
|
68
|
+
tweens) is *excellent* — so hayao borrows it wholesale. But Godot's interface is
|
|
69
|
+
GUIs, binary scenes, and a bespoke language, all opaque to an LLM; hayao keeps
|
|
70
|
+
the model and makes every part **text, typed, greppable, and headlessly
|
|
71
|
+
verifiable**. Canvas engines couple the sim to the render loop and the browser;
|
|
72
|
+
hayao decouples them so the sim is a pure function you can test in Node. Full
|
|
73
|
+
reasoning in [docs/ENGINE.md](docs/ENGINE.md).
|
|
74
|
+
|
|
75
|
+
## Documentation
|
|
76
|
+
|
|
77
|
+
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — the authoritative design and the determinism contract.
|
|
78
|
+
- [docs/CONVENTIONS.md](docs/CONVENTIONS.md) — how games are structured, house style, definition of done.
|
|
79
|
+
- [docs/VERIFICATION.md](docs/VERIFICATION.md) — the two verification channels; how to prove a game correct.
|
|
80
|
+
- [docs/ENGINE.md](docs/ENGINE.md) — why a custom engine, and when NOT to use one.
|
|
81
|
+
- [docs/LESSONS.md](docs/LESSONS.md) — transferable lessons from real LLM-authored game builds.
|
|
82
|
+
- [docs/FRICTION.md](docs/FRICTION.md) — process-lesson log: what fought an AI session and what check/doc now prevents it.
|
|
83
|
+
- [AGENTS.md](AGENTS.md) — the operating manual for an AI author working in this repo.
|
|
84
|
+
|
|
85
|
+
## Status
|
|
86
|
+
|
|
87
|
+
**v0.1 — complete and verified.** All layers are implemented and tested:
|
|
88
|
+
`core/`, `scene/`, `input/`, `render/` (SVG + Canvas2D + headless backends),
|
|
89
|
+
`audio/`, `ui/`, `verify/`, and `app/`. The flagship **Sokoban** example
|
|
90
|
+
(`examples/sokoban/`) is solver-proven winnable on every level and plays in the
|
|
91
|
+
browser. 40 tests pass; `npm run check`, `npm run verify`, and `npm run build`
|
|
92
|
+
are all green. Roadmap: more node types (physics bodies, particles), an ECS
|
|
93
|
+
option for large entity counts, additional example genres, and asset-free audio
|
|
94
|
+
tracks. Contributions welcome.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
[MIT](LICENSE) © Jan Pacan
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GameDefinition } from './game';
|
|
2
|
+
import { KeyboardSource } from '../input/source';
|
|
3
|
+
import type { Renderer } from '../render/renderer';
|
|
4
|
+
import type { World } from '../world';
|
|
5
|
+
export interface RunOptions {
|
|
6
|
+
renderer?: 'svg' | 'canvas';
|
|
7
|
+
/** Start the pause/settings shell (Esc). Default true. */
|
|
8
|
+
shell?: boolean;
|
|
9
|
+
onRestart?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface GameHandle {
|
|
12
|
+
world: World;
|
|
13
|
+
renderer: Renderer;
|
|
14
|
+
/** The live input source — game UI calls input.press('action') for buttons. */
|
|
15
|
+
input: KeyboardSource;
|
|
16
|
+
stop(): void;
|
|
17
|
+
restart(): void;
|
|
18
|
+
}
|
|
19
|
+
export declare function runBrowser(def: GameDefinition, mount: HTMLElement, opts?: RunOptions): GameHandle;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { World } from '../world';
|
|
2
|
+
import type { Node } from '../scene/node';
|
|
3
|
+
import { type InputLog, type InputMap } from '../input/actions';
|
|
4
|
+
import type { ClockConfig } from '../core/clock';
|
|
5
|
+
export interface GameDefinition {
|
|
6
|
+
title: string;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
seed?: number;
|
|
10
|
+
background?: string;
|
|
11
|
+
clock?: ClockConfig;
|
|
12
|
+
inputMap?: InputMap;
|
|
13
|
+
/** Build the initial scene tree for a fresh world. */
|
|
14
|
+
build(world: World): Node;
|
|
15
|
+
/** Optional compact probe snapshot for verification (defaults to World.probe). */
|
|
16
|
+
probe?(world: World): Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/** Identity + defaults. Kept as a function so games read `export default defineGame({…})`. */
|
|
19
|
+
export declare function defineGame(def: GameDefinition): Required<Pick<GameDefinition, 'width' | 'height' | 'seed' | 'inputMap' | 'background'>> & GameDefinition;
|
|
20
|
+
/** Build a live, deterministic World from a game definition. No browser needed. */
|
|
21
|
+
export declare function createWorld(def: GameDefinition, seedOverride?: number): World;
|
|
22
|
+
/** The input map a game uses (with defaults applied). */
|
|
23
|
+
export declare function gameInputMap(def: GameDefinition): InputMap;
|
|
24
|
+
export interface HeadlessResult {
|
|
25
|
+
world: World;
|
|
26
|
+
hash: string;
|
|
27
|
+
steps: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run a game to completion in Node with no host — play an input log (or zero
|
|
31
|
+
* steps) and return the final world + state hash. This is what tests, the CI
|
|
32
|
+
* verifier, and replays call. The browser is never involved.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runHeadless(def: GameDefinition, inputLog?: InputLog): HeadlessResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface Palette {
|
|
2
|
+
name: string;
|
|
3
|
+
bg: string;
|
|
4
|
+
ink: string;
|
|
5
|
+
inkSoft: string;
|
|
6
|
+
line: string;
|
|
7
|
+
accent: string;
|
|
8
|
+
accent2: string;
|
|
9
|
+
good: string;
|
|
10
|
+
warn: string;
|
|
11
|
+
/** A small ordered ramp for categorical fills. */
|
|
12
|
+
ramp: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare const MEADOW: Palette;
|
|
15
|
+
export declare const DUSK: Palette;
|
|
16
|
+
export declare const PAPER: Palette;
|
|
17
|
+
export declare const PALETTES: Record<string, Palette>;
|
|
18
|
+
/** Blend two hex colors (t in [0,1]). Deterministic, no allocations of note. */
|
|
19
|
+
export declare function mix(a: string, b: string, t: number): string;
|
|
20
|
+
export declare function withAlpha(hex: string, alpha: number): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Rng } from '../core/rng';
|
|
2
|
+
import { type Vec2 } from '../core/math';
|
|
3
|
+
/** Regular polygon points (flat [x,y,…]) centered at origin. */
|
|
4
|
+
export declare function regularPolygon(sides: number, radius: number, rotation?: number): number[];
|
|
5
|
+
/** Star with alternating outer/inner radii. */
|
|
6
|
+
export declare function star(points: number, outer: number, inner: number, rotation?: number): number[];
|
|
7
|
+
/** An organic blob outline as an SVG path (deterministic given the Rng). */
|
|
8
|
+
export declare function blobPath(rng: Rng, radius: number, wobble?: number, lobes?: number): string;
|
|
9
|
+
/** Convert a point list into a smooth closed path (Catmull-Rom → cubic Bézier). */
|
|
10
|
+
export declare function smoothClosedPath(points: Vec2[], tension?: number): string;
|
|
11
|
+
/** Smooth open path through points (for trails, curves). */
|
|
12
|
+
export declare function smoothOpenPath(points: Vec2[], tension?: number): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface Volumes {
|
|
2
|
+
master: number;
|
|
3
|
+
music: number;
|
|
4
|
+
sfx: number;
|
|
5
|
+
muted: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** A single zzfx-ish tone spec. */
|
|
8
|
+
export interface Tone {
|
|
9
|
+
freq: number;
|
|
10
|
+
duration: number;
|
|
11
|
+
type?: OscillatorType;
|
|
12
|
+
gain?: number;
|
|
13
|
+
delay?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class AudioBus {
|
|
16
|
+
private ctx;
|
|
17
|
+
private master;
|
|
18
|
+
private musicGain;
|
|
19
|
+
private sfxGain;
|
|
20
|
+
private vol;
|
|
21
|
+
private padOn;
|
|
22
|
+
get available(): boolean;
|
|
23
|
+
get started(): boolean;
|
|
24
|
+
/** Must be called from a user gesture (browser autoplay policy). */
|
|
25
|
+
start(): void;
|
|
26
|
+
setVolumes(v: Partial<Volumes>): void;
|
|
27
|
+
getVolumes(): Volumes;
|
|
28
|
+
private applyVolumes;
|
|
29
|
+
/** Play a single tone (no-op if audio unstarted). */
|
|
30
|
+
tone(spec: Tone): void;
|
|
31
|
+
/** Play a sequence of tones as an arpeggio/chord. */
|
|
32
|
+
play(tones: Tone[]): void;
|
|
33
|
+
/** Convenience SFX. */
|
|
34
|
+
blip(freq?: number): void;
|
|
35
|
+
chime(): void;
|
|
36
|
+
success(): void;
|
|
37
|
+
thud(): void;
|
|
38
|
+
/** Start a soft evolving ambient pad on the music bus. */
|
|
39
|
+
startAmbient(root?: number, voices?: number[]): void;
|
|
40
|
+
}
|
|
41
|
+
/** Shared default bus. */
|
|
42
|
+
export declare const audio: AudioBus;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ClockConfig {
|
|
2
|
+
/** Simulation rate in Hz (steps per second). Default 60. */
|
|
3
|
+
hz?: number;
|
|
4
|
+
/** Max real ms consumed per advance, to avoid a spiral of death. Default 250. */
|
|
5
|
+
maxFrameMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class Clock {
|
|
8
|
+
readonly stepMs: number;
|
|
9
|
+
readonly dt: number;
|
|
10
|
+
private maxFrameMs;
|
|
11
|
+
private accumulator;
|
|
12
|
+
private _frame;
|
|
13
|
+
private _simTimeMs;
|
|
14
|
+
constructor(config?: ClockConfig);
|
|
15
|
+
/**
|
|
16
|
+
* Feed real elapsed ms; returns how many fixed steps to run now.
|
|
17
|
+
* Caller runs the sim exactly that many times.
|
|
18
|
+
*/
|
|
19
|
+
advance(realMs: number): number;
|
|
20
|
+
/** Called by the World once per executed step to keep counters honest. */
|
|
21
|
+
tick(): void;
|
|
22
|
+
/** Interpolation alpha in [0,1) for smooth rendering between fixed steps. */
|
|
23
|
+
get alpha(): number;
|
|
24
|
+
get frame(): number;
|
|
25
|
+
get simTimeMs(): number;
|
|
26
|
+
get simTimeSec(): number;
|
|
27
|
+
getState(): {
|
|
28
|
+
accumulator: number;
|
|
29
|
+
frame: number;
|
|
30
|
+
simTimeMs: number;
|
|
31
|
+
};
|
|
32
|
+
setState(s: {
|
|
33
|
+
accumulator: number;
|
|
34
|
+
frame: number;
|
|
35
|
+
simTimeMs: number;
|
|
36
|
+
}): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type Listener<T> = (payload: T) => void;
|
|
2
|
+
export declare class Signal<T = void> {
|
|
3
|
+
private listeners;
|
|
4
|
+
connect(fn: Listener<T>): () => void;
|
|
5
|
+
once(fn: Listener<T>): () => void;
|
|
6
|
+
disconnect(fn: Listener<T>): void;
|
|
7
|
+
emit(payload: T): void;
|
|
8
|
+
get count(): number;
|
|
9
|
+
clear(): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A named event bus for cross-cutting game events, keyed by an event map so
|
|
13
|
+
* payloads are typed. Prefer node-local Signals for local wiring; use the bus
|
|
14
|
+
* for global concerns (score changed, level complete, sound cue).
|
|
15
|
+
*/
|
|
16
|
+
export declare class EventBus<Events extends Record<string, unknown>> {
|
|
17
|
+
private signals;
|
|
18
|
+
private signalFor;
|
|
19
|
+
on<K extends keyof Events>(key: K, fn: Listener<Events[K]>): () => void;
|
|
20
|
+
once<K extends keyof Events>(key: K, fn: Listener<Events[K]>): () => void;
|
|
21
|
+
emit<K extends keyof Events>(key: K, payload: Events[K]): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface Vec2 {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
}
|
|
5
|
+
export declare const vec2: (x?: number, y?: number) => Vec2;
|
|
6
|
+
export declare const vadd: (a: Vec2, b: Vec2) => Vec2;
|
|
7
|
+
export declare const vsub: (a: Vec2, b: Vec2) => Vec2;
|
|
8
|
+
export declare const vscale: (a: Vec2, s: number) => Vec2;
|
|
9
|
+
export declare const vdot: (a: Vec2, b: Vec2) => number;
|
|
10
|
+
export declare const vlen: (a: Vec2) => number;
|
|
11
|
+
export declare const vdist: (a: Vec2, b: Vec2) => number;
|
|
12
|
+
export declare function vnorm(a: Vec2): Vec2;
|
|
13
|
+
export declare const clamp: (v: number, lo: number, hi: number) => number;
|
|
14
|
+
export declare const lerp: (a: number, b: number, t: number) => number;
|
|
15
|
+
export declare const invLerp: (a: number, b: number, v: number) => number;
|
|
16
|
+
export declare const remap: (v: number, a0: number, a1: number, b0: number, b1: number) => number;
|
|
17
|
+
export declare const TAU: number;
|
|
18
|
+
export declare const deg2rad: (d: number) => number;
|
|
19
|
+
export declare const rad2deg: (r: number) => number;
|
|
20
|
+
/** Axis-aligned rectangle. */
|
|
21
|
+
export interface Rect {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
w: number;
|
|
25
|
+
h: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function rectContains(r: Rect, p: Vec2): boolean;
|
|
28
|
+
export declare function rectsOverlap(a: Rect, b: Rect): boolean;
|
|
29
|
+
/** 2D affine transform as a 6-value matrix [a b c d e f] (like SVG/Canvas). */
|
|
30
|
+
export interface Transform {
|
|
31
|
+
a: number;
|
|
32
|
+
b: number;
|
|
33
|
+
c: number;
|
|
34
|
+
d: number;
|
|
35
|
+
e: number;
|
|
36
|
+
f: number;
|
|
37
|
+
}
|
|
38
|
+
export declare const IDENTITY: Transform;
|
|
39
|
+
export declare function composeTransform(m: Transform, n: Transform): Transform;
|
|
40
|
+
/** Build a transform from position, rotation (radians), scale. */
|
|
41
|
+
export declare function makeTransform(pos: Vec2, rotation: number, scale: Vec2): Transform;
|
|
42
|
+
export declare function applyTransform(m: Transform, p: Vec2): Vec2;
|
|
43
|
+
/** Invert an affine transform (returns identity if singular). */
|
|
44
|
+
export declare function invertTransform(m: Transform): Transform;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface RngState {
|
|
2
|
+
s: [number, number, number, number];
|
|
3
|
+
}
|
|
4
|
+
export declare class Rng {
|
|
5
|
+
private s0;
|
|
6
|
+
private s1;
|
|
7
|
+
private s2;
|
|
8
|
+
private s3;
|
|
9
|
+
constructor(seed?: number | RngState);
|
|
10
|
+
/** Raw 32-bit unsigned integer. */
|
|
11
|
+
private next;
|
|
12
|
+
private rotl;
|
|
13
|
+
/** Float in [0, 1). */
|
|
14
|
+
float(): number;
|
|
15
|
+
/** Float in [lo, hi). */
|
|
16
|
+
range(lo: number, hi: number): number;
|
|
17
|
+
/** Integer in [0, n). */
|
|
18
|
+
int(n: number): number;
|
|
19
|
+
/** Integer in [lo, hi] inclusive. */
|
|
20
|
+
intRange(lo: number, hi: number): number;
|
|
21
|
+
/** True with probability p. */
|
|
22
|
+
chance(p: number): boolean;
|
|
23
|
+
/** Uniform element of an array. */
|
|
24
|
+
pick<T>(arr: readonly T[]): T;
|
|
25
|
+
/** In-place Fisher–Yates shuffle (deterministic). */
|
|
26
|
+
shuffle<T>(arr: T[]): T[];
|
|
27
|
+
/**
|
|
28
|
+
* Derive an independent child stream. Same parent state + same key always
|
|
29
|
+
* yields the same child — so subsystems (enemy AI, loot, terrain) get stable,
|
|
30
|
+
* decorrelated randomness without threading one generator through everything.
|
|
31
|
+
*/
|
|
32
|
+
split(key?: number): Rng;
|
|
33
|
+
/** Serialize / restore for saves and replay. */
|
|
34
|
+
getState(): RngState;
|
|
35
|
+
setState(state: RngState): void;
|
|
36
|
+
}
|
|
37
|
+
/** Deterministic 32-bit FNV-1a hash of a string — handy for stable ids/seeds. */
|
|
38
|
+
export declare function hashString(str: string): number;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export * from './core/math';
|
|
2
|
+
export * from './core/rng';
|
|
3
|
+
export * from './core/clock';
|
|
4
|
+
export * from './core/events';
|
|
5
|
+
export * from './core/hash';
|
|
6
|
+
export * from './scene/node';
|
|
7
|
+
export { Node as Node2D } from './scene/node';
|
|
8
|
+
export * from './scene/nodes';
|
|
9
|
+
export * from './scene/tween';
|
|
10
|
+
export * from './scene/particles';
|
|
11
|
+
export * from './scene/registry';
|
|
12
|
+
export * from './input/actions';
|
|
13
|
+
export * from './input/source';
|
|
14
|
+
export * from './physics/tilemap';
|
|
15
|
+
export * from './physics/aabb';
|
|
16
|
+
export * from './physics/platformer';
|
|
17
|
+
export * from './physics/spatialHash';
|
|
18
|
+
export * from './physics/raycast';
|
|
19
|
+
export * from './render/commands';
|
|
20
|
+
export * from './render/renderer';
|
|
21
|
+
export * from './render/svgString';
|
|
22
|
+
export * from './render/svg';
|
|
23
|
+
export * from './render/canvas';
|
|
24
|
+
export * from './render/headless';
|
|
25
|
+
export * from './art/palette';
|
|
26
|
+
export * from './art/shapes';
|
|
27
|
+
export * from './audio/audio';
|
|
28
|
+
export * from './ui/overlay';
|
|
29
|
+
export * from './ui/settings';
|
|
30
|
+
export * from './ui/shell';
|
|
31
|
+
export * from './verify/solver';
|
|
32
|
+
export * from './verify/determinism';
|
|
33
|
+
export * from './verify/playthrough';
|
|
34
|
+
export * from './verify/capture';
|
|
35
|
+
export * from './verify/driver';
|
|
36
|
+
export * from './verify/bot';
|
|
37
|
+
export * from './verify/feel';
|
|
38
|
+
export * from './verify/filmstrip';
|
|
39
|
+
export * from './world';
|
|
40
|
+
export * from './app/game';
|
|
41
|
+
export * from './app/browser';
|
|
42
|
+
/** Engine version. */
|
|
43
|
+
export declare const VERSION = "0.1.0";
|