hayao 0.1.0 → 0.2.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 (47) hide show
  1. package/README.md +29 -12
  2. package/dist/art/autotile.d.ts +77 -0
  3. package/dist/art/bitmapFont.d.ts +113 -0
  4. package/dist/art/font5.d.ts +11 -0
  5. package/dist/art/palette.d.ts +38 -0
  6. package/dist/art/texture.d.ts +78 -0
  7. package/dist/audio/audio.d.ts +7 -0
  8. package/dist/content/dsl.d.ts +61 -0
  9. package/dist/core/dmath.d.ts +20 -0
  10. package/dist/core/math.d.ts +2 -0
  11. package/dist/index.d.ts +37 -1
  12. package/dist/index.js +4549 -69
  13. package/dist/index.js.map +4 -4
  14. package/dist/logic/fsm.d.ts +85 -0
  15. package/dist/logic/graph.d.ts +88 -0
  16. package/dist/logic/history.d.ts +54 -0
  17. package/dist/logic/random.d.ts +32 -0
  18. package/dist/net/browser.d.ts +37 -0
  19. package/dist/net/inputBuffer.d.ts +27 -0
  20. package/dist/net/lockstep.d.ts +79 -0
  21. package/dist/net/players.d.ts +27 -0
  22. package/dist/net/protocol.d.ts +100 -0
  23. package/dist/net/rollback.d.ts +89 -0
  24. package/dist/net/room.d.ts +78 -0
  25. package/dist/net/transport.d.ts +78 -0
  26. package/dist/persist/codec.d.ts +4 -0
  27. package/dist/persist/save.d.ts +32 -0
  28. package/dist/persist/storage.d.ts +46 -0
  29. package/dist/physics/rigidBody.d.ts +104 -0
  30. package/dist/physics/rigidCollide.d.ts +16 -0
  31. package/dist/physics/rigidJoints.d.ts +65 -0
  32. package/dist/physics/rigidQueries.d.ts +15 -0
  33. package/dist/physics/rigidStep.d.ts +14 -0
  34. package/dist/procgen/cave.d.ts +21 -0
  35. package/dist/procgen/grid.d.ts +21 -0
  36. package/dist/procgen/rooms.d.ts +34 -0
  37. package/dist/procgen/scatter.d.ts +32 -0
  38. package/dist/procgen/terrain.d.ts +24 -0
  39. package/dist/render/nineSlice.d.ts +32 -0
  40. package/dist/scene/floatingText.d.ts +51 -0
  41. package/dist/scene/particles.d.ts +64 -0
  42. package/dist/scene/pool.d.ts +16 -0
  43. package/dist/scene/tween.d.ts +26 -0
  44. package/dist/ui/transition.d.ts +107 -0
  45. package/dist/verify/layout.d.ts +39 -0
  46. package/docs/API.md +247 -8
  47. package/package.json +31 -9
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![ci](https://github.com/hellojanpacan/hayao-js/actions/workflows/ci.yml/badge.svg)](https://github.com/hellojanpacan/hayao-js/actions/workflows/ci.yml)
4
4
 
5
+ **Play the 26 machine-verified example games at [hayao.dev](https://hayao.dev/).**
6
+
5
7
  **An AI-first game engine.** A deterministic, headless-native simulation kernel
6
8
  with a Godot-style scene tree, pluggable renderers (SVG / Canvas / headless),
7
9
  and a built-in verification harness — designed so an LLM can author, test, and
@@ -52,15 +54,23 @@ public surface is greppable in one file (`src/index.ts`).
52
54
  | **app/** | `runBrowser` (rAF loop) · `runHeadless` | plugs the kernel into a host |
53
55
  | **ui/** | DOM overlays: menus, HUD, pause/settings shell | observer (never mutates sim) |
54
56
  | **render/** | display list → `SvgRenderer` \| `Canvas2DRenderer` \| `HeadlessRenderer` | observer (projection → paint) |
57
+ | **art/** | code-as-art: palettes, shapes, textures, bitmap fonts, autotile | observer (paints, never hashed) |
55
58
  | **audio/** | procedural Web Audio bus (no-op in Node) | observer |
56
- | **verify/** | probes · replay · `assertDeterministic` · solver | the AI-first harness |
59
+ | **verify/** | probes · replay · `assertDeterministic` · solver · bots · filmstrips | the AI-first harness |
57
60
  | **scene/** | `Node`, `Node2D`, `Sprite`, `Text`, `Camera2D`, `Timer`, `AnimationPlayer`, behaviors | **THE STATE** |
61
+ | **physics/** | tilemap + kinematic AABB · character controllers · deterministic rigid-body dynamics | deterministic |
62
+ | **procgen/** | seeded grids, caves, terrain, rooms, stateless scatter | deterministic |
63
+ | **logic/** | pure primitives: FSM, weighted tables, graph search, history | deterministic |
64
+ | **content/** | data-driven wave/spawn directors + upgrade trees | deterministic |
65
+ | **net/** | deterministic multiplayer: lockstep + rollback over a pluggable transport | deterministic |
66
+ | **persist/** | save/load over a pluggable storage adapter + compact codecs | deterministic |
58
67
  | **input/** | action map · per-step sampling · record / replay | deterministic |
59
68
  | **core/** | `Rng` · `Clock` · `EventBus` · `World` · state hash | **THE KERNEL** (headless, pure) |
60
69
 
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.
70
+ `core + scene + input + physics + procgen + logic + content + net + persist` are
71
+ **deterministic and run in Node**. `render + art + audio + ui + app` are the
72
+ browser-only observer shell. Break that boundary and the verification harness
73
+ stops being able to prove anything.
64
74
 
65
75
  ## Why not Godot or a raw canvas engine?
66
76
 
@@ -84,14 +94,21 @@ reasoning in [docs/ENGINE.md](docs/ENGINE.md).
84
94
 
85
95
  ## Status
86
96
 
87
- **v0.1complete 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.
97
+ **v0.2the 20-genre campaign is complete, plus the waves it triggered.**
98
+ Twenty-six games live under `examples/`. The core twenty-one cover the most
99
+ popular 2D indie genres (platformer, metroidvania, Zelda-like, stealth, horde
100
+ survival, bullet hell, tower defense, RTS, roguelike, deckbuilder, tactics,
101
+ match-3, idle, farming, horror, city builder, rhythm, physics arcade, racing,
102
+ narrative plus the original Sokoban). On top of those sit two js13k-benchmark
103
+ reproductions (Seamfold, Gravewell), a deterministic rigid-body physics wave
104
+ (Rookspire demolition, Brasswick pinball), and a netplay showcase (Fernclash —
105
+ lockstep + rollback across tabs). Every one ships a `verify.ts` suite that
106
+ machine-proves its truth: solver-proven puzzles, bot-beaten levels, duel-proven
107
+ counter systems, win-rate-tuned balance, fairness gates for procgen,
108
+ frame-exact timing windows, bit-for-bit peer agreement, golden replay hashes.
109
+ 400+ tests; `npm run verify` runs the whole portfolio. The campaign's findings — what each
110
+ genre demanded of the engine and what generalized — live in
111
+ [docs/BUILDLOG.md](docs/BUILDLOG.md).
95
112
 
96
113
  ## License
97
114
 
@@ -0,0 +1,77 @@
1
+ import { type Transform } from '../core/math';
2
+ import type { PolyCommand, RectCommand } from '../render/commands';
3
+ export type BoolGrid = readonly (readonly boolean[])[];
4
+ /** Build a grid from rows of text; a cell is solid when its char is in `solid`. */
5
+ export declare function gridFromRows(rows: readonly string[], solid?: string): boolean[][];
6
+ export declare const Edge: {
7
+ readonly N: 1;
8
+ readonly E: 2;
9
+ readonly S: 4;
10
+ readonly W: 8;
11
+ };
12
+ export type Edge = (typeof Edge)[keyof typeof Edge];
13
+ /** 4-bit edge mask of solid neighbours around a cell. */
14
+ export declare function mask4(grid: BoolGrid, x: number, y: number): number;
15
+ /** 8-bit neighbour mask, bit order N,NE,E,SE,S,SW,W,NW (1,2,4,…,128). */
16
+ export declare function mask8(grid: BoolGrid, x: number, y: number): number;
17
+ /** Base tile shapes an edge mask resolves to (rotate `frame` by `rotation`×90° CW). */
18
+ export declare const WangFrame: {
19
+ readonly Isolated: 0;
20
+ readonly Cap: 1;
21
+ readonly Straight: 2;
22
+ readonly Bend: 3;
23
+ readonly Tee: 4;
24
+ readonly Cross: 5;
25
+ };
26
+ export type WangFrame = (typeof WangFrame)[keyof typeof WangFrame];
27
+ export interface WangTile {
28
+ mask: number;
29
+ frame: WangFrame;
30
+ /** Quarter turns clockwise applied to the base frame (0–3). */
31
+ rotation: number;
32
+ }
33
+ /** Resolve an edge mask (0–15) to its base frame + rotation. */
34
+ export declare function wangTile(mask: number): WangTile;
35
+ /** Classify every solid cell into a {frame, rotation}; empty cells are `null`. */
36
+ export declare function autotile4(grid: BoolGrid): (WangTile | null)[][];
37
+ /** Per-dual-cell case index (0–15) over the grid corners; size (h-1)×(w-1). */
38
+ export declare function marchingSquaresCases(grid: BoolGrid): number[][];
39
+ export interface ContourSegment {
40
+ a: {
41
+ x: number;
42
+ y: number;
43
+ };
44
+ b: {
45
+ x: number;
46
+ y: number;
47
+ };
48
+ }
49
+ export interface ContourOptions {
50
+ /** Cell size in design units. Default 1. */
51
+ cell?: number;
52
+ x?: number;
53
+ y?: number;
54
+ }
55
+ /** Iso-contour line segments tracing the solid/empty boundary (design units). */
56
+ export declare function marchingSquaresContours(grid: BoolGrid, options?: ContourOptions): ContourSegment[];
57
+ export interface AutotileDrawOptions {
58
+ /** Tile size in design units. Default 8. */
59
+ tile?: number;
60
+ x?: number;
61
+ y?: number;
62
+ z?: number;
63
+ transform?: Transform;
64
+ /** Fill colour of solid tiles. */
65
+ fill?: string;
66
+ /** Stroke colour drawn only on sides exposed to empty (the seam). */
67
+ edge?: string;
68
+ edgeWidth?: number;
69
+ }
70
+ /**
71
+ * Draw a boolean grid as seamless tiles: a fill per solid cell plus an edge
72
+ * stroke only on sides facing empty (derived from `mask4`). Pure draw data for
73
+ * a `cosmetic` node.
74
+ */
75
+ export declare function autotileToCommands(grid: BoolGrid, options?: AutotileDrawOptions): (RectCommand | PolyCommand)[];
76
+ /** Draw the marching-squares iso-contour as open polylines (cosmetic draw data). */
77
+ export declare function contourToCommands(grid: BoolGrid, options?: AutotileDrawOptions): PolyCommand[];
@@ -0,0 +1,113 @@
1
+ import { type Transform } from '../core/math';
2
+ import type { DrawCommand, RectCommand, TextAlign } from '../render/commands';
3
+ import { Node, type NodeConfig } from '../scene/node';
4
+ import { type BitmapFont } from './font5';
5
+ /** One source character with an optional colour and its index in the source string. */
6
+ export interface RichChar {
7
+ ch: string;
8
+ color?: string;
9
+ i: number;
10
+ }
11
+ /**
12
+ * Parse inline colour markup into per-character colours. `{name}` pushes a
13
+ * colour, `{/}` pops it (a stack, so nesting works); `{{` is a literal `{`.
14
+ * A tag resolves through `colorMap`, or is used literally if it starts with `#`
15
+ * or is not in the map. Text outside any tag has no colour (caller default).
16
+ */
17
+ export declare function parseRich(markup: string, colorMap?: Record<string, string>): RichChar[];
18
+ /** Width in font-pixels of a run of already-resolved chars on one line. */
19
+ export declare function measureLine(font: BitmapFont, chars: readonly RichChar[]): number;
20
+ /** Width in font-pixels of a plain string (no wrapping). */
21
+ export declare function measureText(font: BitmapFont, text: string): number;
22
+ export interface PlacedGlyph {
23
+ ch: string;
24
+ color?: string;
25
+ /** Source-string index (for typewriter reveal). */
26
+ i: number;
27
+ /** Top-left in font-pixel units. */
28
+ x: number;
29
+ y: number;
30
+ w: number;
31
+ }
32
+ export interface TextLayout {
33
+ glyphs: PlacedGlyph[];
34
+ /** Bounding size in font-pixel units. */
35
+ width: number;
36
+ height: number;
37
+ lines: number;
38
+ }
39
+ export interface TextLayoutOptions {
40
+ /** Wrap width in font-pixels. Omit for no wrapping. */
41
+ maxWidth?: number;
42
+ /** Extra blank rows between lines. Default 1. */
43
+ lineSpacing?: number;
44
+ /** Horizontal alignment within `maxWidth` (or the widest line). Default 'left'. */
45
+ align?: TextAlign;
46
+ }
47
+ /**
48
+ * Lay out text into placed glyphs (font-pixel coords), word-wrapping on spaces
49
+ * and breaking on `\n`. Accepts a plain string or a `RichChar[]` from
50
+ * `parseRich`. Pure and deterministic.
51
+ */
52
+ export declare function layoutText(font: BitmapFont, input: string | readonly RichChar[], options?: TextLayoutOptions): TextLayout;
53
+ /** Typewriter: how many source characters are revealed after `elapsed` seconds. */
54
+ export declare function typewriterCount(total: number, elapsedSec: number, charsPerSec: number): number;
55
+ export interface TextDrawOptions {
56
+ /** Font-pixel cell size in design units. Default 2. */
57
+ cell?: number;
58
+ /** Top-left origin in local space. Default 0,0. */
59
+ x?: number;
60
+ y?: number;
61
+ z?: number;
62
+ /** Default fill for glyphs without an inline colour. Default '#000'. */
63
+ color?: string;
64
+ transform?: Transform;
65
+ /** Draw only glyphs whose source index is `< reveal` (typewriter). */
66
+ reveal?: number;
67
+ }
68
+ /**
69
+ * Project a laid-out text to run-merged `rect` commands — one rect per
70
+ * horizontal span of lit pixels. Pure; the commands belong under a `cosmetic`
71
+ * node.
72
+ */
73
+ export declare function textToCommands(font: BitmapFont, layout: TextLayout, options?: TextDrawOptions): RectCommand[];
74
+ export interface BitmapTextConfig extends NodeConfig {
75
+ text: string;
76
+ font?: BitmapFont;
77
+ cell?: number;
78
+ color?: string;
79
+ /** Resolve `{tag}` markup names to colours. */
80
+ colorMap?: Record<string, string>;
81
+ /** Wrap width in font-pixels. */
82
+ maxWidth?: number;
83
+ lineSpacing?: number;
84
+ align?: TextAlign;
85
+ /** Typewriter speed in chars/sec (clock-driven). Omit to show all at once. */
86
+ charsPerSec?: number;
87
+ /** Center the block on the node origin instead of top-left. Default false. */
88
+ center?: boolean;
89
+ }
90
+ /**
91
+ * A scene node that renders bitmap text with optional inline colour and a
92
+ * clock-driven typewriter reveal. Pure view → `cosmetic = true` (never hashed).
93
+ */
94
+ export declare class BitmapText extends Node {
95
+ readonly type = "BitmapText";
96
+ text: string;
97
+ font: BitmapFont;
98
+ cell: number;
99
+ color: string;
100
+ colorMap: Record<string, string>;
101
+ maxWidth?: number;
102
+ lineSpacing: number;
103
+ align: TextAlign;
104
+ charsPerSec?: number;
105
+ center: boolean;
106
+ private startTime;
107
+ constructor(config: BitmapTextConfig);
108
+ protected onReady(): void;
109
+ /** Restart the typewriter from the current sim time. */
110
+ restartReveal(): void;
111
+ private buildLayout;
112
+ protected draw(out: DrawCommand[], world: Transform): void;
113
+ }
@@ -0,0 +1,11 @@
1
+ /** A font: named glyph bitmaps plus vertical/spacing metrics. */
2
+ export interface BitmapFont {
3
+ readonly height: number;
4
+ /** Blank columns between glyphs. */
5
+ readonly tracking: number;
6
+ /** Width of a space character (advance only, no pixels). */
7
+ readonly spaceWidth: number;
8
+ readonly glyphs: Readonly<Record<string, readonly string[]>>;
9
+ }
10
+ /** The default built-in font: 5px tall, proportional, uppercase + digits + punctuation. */
11
+ export declare const FONT_5: BitmapFont;
@@ -1,3 +1,4 @@
1
+ import type { Rng } from '../core/rng';
1
2
  export interface Palette {
2
3
  name: string;
3
4
  bg: string;
@@ -18,3 +19,40 @@ export declare const PALETTES: Record<string, Palette>;
18
19
  /** Blend two hex colors (t in [0,1]). Deterministic, no allocations of note. */
19
20
  export declare function mix(a: string, b: string, t: number): string;
20
21
  export declare function withAlpha(hex: string, alpha: number): string;
22
+ export interface HSL {
23
+ /** Hue in degrees [0,360). */
24
+ h: number;
25
+ /** Saturation [0,1]. */
26
+ s: number;
27
+ /** Lightness [0,1]. */
28
+ l: number;
29
+ }
30
+ /** HSL → hex. h in degrees (wraps), s/l in [0,1]. */
31
+ export declare function hsl(h: number, s: number, l: number): string;
32
+ /** HSV/HSB → hex. h in degrees (wraps), s/v in [0,1]. */
33
+ export declare function hsv(h: number, s: number, v: number): string;
34
+ /** Decompose a hex color into HSL. Inverse of `hsl()`. */
35
+ export declare function hexToHsl(hex: string): HSL;
36
+ export interface DriftAmounts {
37
+ /** Max hue drift ± degrees. */
38
+ hue?: number;
39
+ /** Max saturation drift ± (absolute, [0,1]). */
40
+ sat?: number;
41
+ /** Max lightness drift ± (absolute, [0,1]). */
42
+ light?: number;
43
+ }
44
+ /**
45
+ * Drift a color in HSL space by random amounts from `world.rng` (space-huggers'
46
+ * `mutate`). Deterministic given rng state; hue wraps, s/l clamp. Cosmetic, but
47
+ * because it consumes rng draws, call it in a stable order.
48
+ */
49
+ export declare function mutateColor(rng: Rng, hex: string, amounts?: DriftAmounts): string;
50
+ /** Blend two hex colors in linear light (gamma-correct). t in [0,1]. */
51
+ export declare function mixLinear(a: string, b: string, t: number): string;
52
+ /**
53
+ * Sample a multi-stop gradient at t in [0,1], blending in linear light. Stops
54
+ * are evenly spaced hex colors (≥ 1). Great for procedural sky/heat/health ramps.
55
+ */
56
+ export declare function sampleGradient(stops: readonly string[], t: number): string;
57
+ /** Materialize an n-color ramp from gradient stops (linear-light interpolation). */
58
+ export declare function gradient(stops: readonly string[], n: number): string[];
@@ -0,0 +1,78 @@
1
+ import { type Transform } from '../core/math';
2
+ import type { DrawCommand, RectCommand } from '../render/commands';
3
+ import { Node, type NodeConfig } from '../scene/node';
4
+ /** A palette entry; `null` (or a missing index) renders transparent. */
5
+ export type Swatch = string | null;
6
+ /** A small indexed bitmap: `data[y * width + x]` is a palette index. */
7
+ export declare class PixelBuffer {
8
+ readonly width: number;
9
+ readonly height: number;
10
+ readonly data: Uint8Array;
11
+ constructor(width: number, height: number, data?: Uint8Array);
12
+ get(x: number, y: number): number;
13
+ set(x: number, y: number, index: number): void;
14
+ /** Build from a rows-of-strings grid, mapping each character to an index. */
15
+ static fromRows(rows: readonly string[], charToIndex: Record<string, number>): PixelBuffer;
16
+ /** A copy with every index passed through `lut` (2-bit → palette remap). */
17
+ remap(lut: readonly number[]): PixelBuffer;
18
+ }
19
+ /**
20
+ * Decode a 1-bit-per-pixel bitmap packed MSB-first into a BigInt/hex string
21
+ * (the super-castle-style trick). Pixel `i` reads bit `(w*h-1-i)`, so a hex
22
+ * literal reads left-to-right, top-to-bottom. Produces indices 0 or 1.
23
+ */
24
+ export declare function decodeBits(source: bigint | string, width: number, height: number): PixelBuffer;
25
+ /**
26
+ * Decode a 2-bit-per-pixel bitmap (indices 0–3) packed MSB-first, matching the
27
+ * dying-dreams "2-bit source → palette LUT" pipeline. Pass the result through
28
+ * `PixelBuffer.remap(lut)` to lift the 2-bit indices into a wider palette.
29
+ */
30
+ export declare function decode2bit(source: bigint | string, width: number, height: number): PixelBuffer;
31
+ /**
32
+ * Decode a run-length-encoded index stream (witchcat-style). `runs` is a flat
33
+ * list of `[count, index, count, index, …]` filled row-major. A short encoding
34
+ * for sprites dominated by flat colour.
35
+ */
36
+ export declare function decodeRLE(runs: readonly number[], width: number, height: number): PixelBuffer;
37
+ /** Encode a PixelBuffer back to RLE run pairs — the inverse of `decodeRLE`. */
38
+ export declare function encodeRLE(buf: PixelBuffer): number[];
39
+ export interface PixelDrawOptions {
40
+ /** Size of one pixel cell in design units. Default 1. */
41
+ cell?: number;
42
+ /** Top-left origin in local space. Default 0,0. */
43
+ x?: number;
44
+ y?: number;
45
+ /** Painter z. Default 0. */
46
+ z?: number;
47
+ /** World transform for the emitted commands. Default identity. */
48
+ transform?: Transform;
49
+ }
50
+ /**
51
+ * Project a PixelBuffer to `rect` draw commands, run-merging horizontal spans
52
+ * of the same colour so a 16×16 sprite is a handful of rects, not 256. Palette
53
+ * entries that are `null`/undefined render transparent (skipped). Pure — the
54
+ * commands carry no state and belong under a `cosmetic` node.
55
+ */
56
+ export declare function pixelsToCommands(buf: PixelBuffer, palette: readonly Swatch[], options?: PixelDrawOptions): RectCommand[];
57
+ export interface TextureSpriteConfig extends NodeConfig {
58
+ buffer: PixelBuffer;
59
+ palette: readonly Swatch[];
60
+ /** Pixel cell size in design units. Default 1. */
61
+ cell?: number;
62
+ /** Draw centered on the node origin instead of top-left. Default true. */
63
+ center?: boolean;
64
+ }
65
+ /**
66
+ * A scene node that renders a decoded PixelBuffer. It is pure view over
67
+ * constant art data, so it is `cosmetic = true` by default — excluded from
68
+ * `serialize()`/`hash()`/snapshot. Position/scale it like any other node.
69
+ */
70
+ export declare class TextureSprite extends Node {
71
+ readonly type = "TextureSprite";
72
+ buffer: PixelBuffer;
73
+ palette: readonly Swatch[];
74
+ cell: number;
75
+ center: boolean;
76
+ constructor(config: TextureSpriteConfig);
77
+ protected draw(out: DrawCommand[], world: Transform): void;
78
+ }
@@ -11,6 +11,8 @@ export interface Tone {
11
11
  type?: OscillatorType;
12
12
  gain?: number;
13
13
  delay?: number;
14
+ /** Stereo position -1 (left) … 1 (right) — the spatial-audio hook. */
15
+ pan?: number;
14
16
  }
15
17
  export declare class AudioBus {
16
18
  private ctx;
@@ -28,6 +30,11 @@ export declare class AudioBus {
28
30
  private applyVolumes;
29
31
  /** Play a single tone (no-op if audio unstarted). */
30
32
  tone(spec: Tone): void;
33
+ /**
34
+ * A positional cue: gain falls with distance, pan follows the horizontal
35
+ * offset. dx/dist in design-space px; hearing range sets the falloff.
36
+ */
37
+ spatial(freq: number, dx: number, dist: number, hearing?: number, duration?: number, type?: OscillatorType): void;
31
38
  /** Play a sequence of tones as an arpeggio/chord. */
32
39
  play(tones: Tone[]): void;
33
40
  /** Convenience SFX. */
@@ -0,0 +1,61 @@
1
+ import type { Rng } from '../core/rng';
2
+ import { type WeightedEntry } from '../logic/random';
3
+ /**
4
+ * One declarative wave. Fires `count` of `spawn` at time `time` (sim seconds).
5
+ * When `every` is set it repeats on that interval until `end` (exclusive),
6
+ * modelling a continuous spawn stream. `spawn` may be a single id or a weighted
7
+ * table, in which case each firing rolls the table via `world.rng`.
8
+ */
9
+ export interface WaveDef {
10
+ /** First fire time, in fixed sim seconds (compare against `world.time`). */
11
+ time: number;
12
+ /** What to spawn: a fixed id, or a weighted set rolled per firing. */
13
+ spawn: string | WeightedEntry<string>[];
14
+ /** How many per firing (default 1). */
15
+ count?: number;
16
+ /** Repeat interval in seconds; omit for a one-shot wave. */
17
+ every?: number;
18
+ /** Repeats stop once the next fire time reaches this (seconds). */
19
+ end?: number;
20
+ }
21
+ /** Mutable director cursor — plain JSON, keep it in `world.state`. */
22
+ export interface DirectorState {
23
+ /** Next fire time per wave index; Infinity once a wave is exhausted. */
24
+ next: number[];
25
+ }
26
+ /** One resolved spawn instruction emitted by the director. */
27
+ export interface SpawnEvent {
28
+ /** Concrete spawn id (already rolled if the wave used a weighted set). */
29
+ spawn: string;
30
+ count: number;
31
+ /** Source wave index, for callers that want to key behavior off the wave. */
32
+ wave: number;
33
+ }
34
+ /** Initialize a director cursor for a schedule. */
35
+ export declare function initDirector(waves: readonly WaveDef[]): DirectorState;
36
+ /**
37
+ * Advance the director to `time` (sim seconds) and return every spawn that is
38
+ * now due, in wave order. Mutates `state.next` in place. Deterministic: it emits
39
+ * the same events for the same (schedule, time, rng-state) on every machine.
40
+ * A repeating wave can fire multiple times in one poll if the frame straddled
41
+ * several intervals (e.g. after a restore), so catch-up never drops spawns.
42
+ */
43
+ export declare function pollDirector(waves: readonly WaveDef[], state: DirectorState, time: number, rng?: Rng): SpawnEvent[];
44
+ /**
45
+ * A declarative upgrade or evolution node. `requires` lists ids that must all be
46
+ * owned before this one can be offered — the cat-survivors evolution gate and a
47
+ * deckbuilder's upgrade prerequisites are the same shape.
48
+ */
49
+ export interface UpgradeDef {
50
+ id: string;
51
+ /** Ids that must all be owned before this becomes available. */
52
+ requires?: string[];
53
+ /** Optional: only offer while UNDER this many owned copies (repeatable upgrades). */
54
+ maxStacks?: number;
55
+ }
56
+ /**
57
+ * Which upgrades are offerable given what's owned: prerequisites all met, and
58
+ * not already at `maxStacks` (default 1 — offered until owned once). `owned` may
59
+ * repeat ids to represent stacks. Ordered by definition order for hash stability.
60
+ */
61
+ export declare function availableUpgrades(defs: readonly UpgradeDef[], owned: readonly string[]): UpgradeDef[];
@@ -0,0 +1,20 @@
1
+ /** Deterministic sine (bit-identical on every JS engine). */
2
+ export declare function dsin(x: number): number;
3
+ /** Deterministic cosine (bit-identical on every JS engine). */
4
+ export declare function dcos(x: number): number;
5
+ /** Deterministic arctangent. */
6
+ export declare function datan(x: number): number;
7
+ /** Deterministic atan2(y, x) with standard quadrant conventions. */
8
+ export declare function datan2(y: number, x: number): number;
9
+ /** Deterministic 2^x via Taylor series on the reduced argument. */
10
+ export declare function dexp2(x: number): number;
11
+ /** Deterministic natural exponential eˣ. Routes through dexp2 (eˣ = 2^(x/ln2)). */
12
+ export declare function dexp(x: number): number;
13
+ /** Deterministic hypotenuse: sqrt is IEEE-correctly-rounded, hypot is not. */
14
+ export declare function dhypot(x: number, y: number): number;
15
+ /** Deterministic natural logarithm. */
16
+ export declare function dlog(x: number): number;
17
+ /** Deterministic base-10 logarithm. */
18
+ export declare const dlog10: (x: number) => number;
19
+ /** Deterministic base-2 logarithm. */
20
+ export declare const dlog2: (x: number) => number;
@@ -14,6 +14,8 @@ export declare const clamp: (v: number, lo: number, hi: number) => number;
14
14
  export declare const lerp: (a: number, b: number, t: number) => number;
15
15
  export declare const invLerp: (a: number, b: number, v: number) => number;
16
16
  export declare const remap: (v: number, a0: number, a1: number, b0: number, b1: number) => number;
17
+ /** Hermite smoothstep: 0 below `edge0`, 1 above `edge1`, eased in between. */
18
+ export declare const smoothstep: (edge0: number, edge1: number, v: number) => number;
17
19
  export declare const TAU: number;
18
20
  export declare const deg2rad: (d: number) => number;
19
21
  export declare const rad2deg: (r: number) => number;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './core/math';
2
+ export * from './core/dmath';
2
3
  export * from './core/rng';
3
4
  export * from './core/clock';
4
5
  export * from './core/events';
@@ -6,8 +7,10 @@ export * from './core/hash';
6
7
  export * from './scene/node';
7
8
  export { Node as Node2D } from './scene/node';
8
9
  export * from './scene/nodes';
10
+ export * from './scene/pool';
9
11
  export * from './scene/tween';
10
12
  export * from './scene/particles';
13
+ export * from './scene/floatingText';
11
14
  export * from './scene/registry';
12
15
  export * from './input/actions';
13
16
  export * from './input/source';
@@ -16,28 +19,61 @@ export * from './physics/aabb';
16
19
  export * from './physics/platformer';
17
20
  export * from './physics/spatialHash';
18
21
  export * from './physics/raycast';
22
+ export * from './physics/rigidBody';
23
+ export * from './physics/rigidCollide';
24
+ export * from './physics/rigidJoints';
25
+ export * from './physics/rigidStep';
26
+ export * from './physics/rigidQueries';
19
27
  export * from './render/commands';
20
28
  export * from './render/renderer';
21
29
  export * from './render/svgString';
22
30
  export * from './render/svg';
23
31
  export * from './render/canvas';
24
32
  export * from './render/headless';
33
+ export * from './render/nineSlice';
25
34
  export * from './art/palette';
26
35
  export * from './art/shapes';
36
+ export * from './art/texture';
37
+ export * from './art/font5';
38
+ export * from './art/bitmapFont';
39
+ export * from './art/autotile';
40
+ export * from './procgen/grid';
41
+ export * from './procgen/scatter';
42
+ export * from './procgen/cave';
43
+ export * from './procgen/terrain';
44
+ export * from './procgen/rooms';
27
45
  export * from './audio/audio';
28
46
  export * from './ui/overlay';
29
47
  export * from './ui/settings';
30
48
  export * from './ui/shell';
49
+ export * from './ui/transition';
31
50
  export * from './verify/solver';
32
51
  export * from './verify/determinism';
33
52
  export * from './verify/playthrough';
34
53
  export * from './verify/capture';
35
54
  export * from './verify/driver';
36
55
  export * from './verify/bot';
56
+ export * from './verify/layout';
37
57
  export * from './verify/feel';
38
58
  export * from './verify/filmstrip';
59
+ export * from './logic/fsm';
60
+ export * from './logic/random';
61
+ export * from './logic/graph';
62
+ export * from './logic/history';
63
+ export * from './persist/storage';
64
+ export * from './persist/codec';
65
+ export * from './persist/save';
66
+ export * from './content/dsl';
67
+ export * from './net/players';
68
+ export * from './net/protocol';
69
+ export * from './net/transport';
70
+ export * from './net/inputBuffer';
71
+ export * from './net/lockstep';
72
+ export * from './net/rollback';
73
+ export * from './net/room';
74
+ export * from './net/browser';
39
75
  export * from './world';
40
76
  export * from './app/game';
41
77
  export * from './app/browser';
42
78
  /** Engine version. */
43
- export declare const VERSION = "0.1.0";
79
+ export declare const VERSION = "0.2.0";