hayao 0.1.0 → 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/README.md +127 -21
- package/bin/create-hayao.mjs +380 -0
- package/bin/hayao-mcp-cli.mjs +11 -0
- package/dist/app/browser.d.ts +33 -2
- package/dist/app/game.d.ts +47 -2
- package/dist/app/tuning.d.ts +68 -0
- package/dist/art/autotile.d.ts +77 -0
- package/dist/art/bitmapFont.d.ts +113 -0
- package/dist/art/font5.d.ts +11 -0
- package/dist/art/palette.d.ts +79 -0
- package/dist/art/texture.d.ts +78 -0
- package/dist/audio/adaptive.d.ts +58 -0
- package/dist/audio/album.d.ts +16 -0
- package/dist/audio/analysis.d.ts +59 -0
- package/dist/audio/audio.d.ts +28 -0
- package/dist/audio/chord.d.ts +17 -0
- package/dist/audio/genres.d.ts +11 -0
- package/dist/audio/lint.d.ts +29 -0
- package/dist/audio/match.d.ts +38 -0
- package/dist/audio/music.d.ts +88 -0
- package/dist/audio/pcm.d.ts +54 -0
- package/dist/audio/quality.d.ts +28 -0
- package/dist/audio/reverb.d.ts +15 -0
- package/dist/audio/synth.d.ts +56 -0
- package/dist/audio/theory.d.ts +64 -0
- package/dist/content/campaign.d.ts +69 -0
- package/dist/content/dsl.d.ts +61 -0
- package/dist/content/generate.d.ts +78 -0
- package/dist/content/level.d.ts +93 -0
- package/dist/content/worldgraph.d.ts +73 -0
- package/dist/core/dmath.d.ts +22 -0
- package/dist/core/math.d.ts +2 -0
- package/dist/hayao.global.js +15 -0
- package/dist/index.d.ts +66 -1
- package/dist/index.js +9174 -835
- package/dist/index.js.map +4 -4
- package/dist/index.min.js +15 -0
- package/dist/input/source.d.ts +52 -1
- package/dist/logic/fsm.d.ts +85 -0
- package/dist/logic/graph.d.ts +88 -0
- package/dist/logic/history.d.ts +54 -0
- package/dist/logic/random.d.ts +32 -0
- package/dist/mcp.js +31225 -0
- package/dist/net/browser.d.ts +37 -0
- package/dist/net/inputBuffer.d.ts +27 -0
- package/dist/net/lockstep.d.ts +79 -0
- package/dist/net/players.d.ts +27 -0
- package/dist/net/protocol.d.ts +100 -0
- package/dist/net/rollback.d.ts +89 -0
- package/dist/net/room.d.ts +78 -0
- package/dist/net/transport.d.ts +78 -0
- package/dist/persist/codec.d.ts +4 -0
- package/dist/persist/save.d.ts +32 -0
- package/dist/persist/storage.d.ts +46 -0
- package/dist/physics/rigidBody.d.ts +104 -0
- package/dist/physics/rigidCollide.d.ts +16 -0
- package/dist/physics/rigidJoints.d.ts +65 -0
- package/dist/physics/rigidQueries.d.ts +15 -0
- package/dist/physics/rigidStep.d.ts +14 -0
- package/dist/procgen/cave.d.ts +21 -0
- package/dist/procgen/grid.d.ts +21 -0
- package/dist/procgen/rooms.d.ts +34 -0
- package/dist/procgen/scatter.d.ts +32 -0
- package/dist/procgen/terrain.d.ts +24 -0
- package/dist/rasterize-worker-lite.mjs +13 -0
- package/dist/render/canvas.d.ts +6 -1
- package/dist/render/commands.d.ts +46 -0
- package/dist/render/nineSlice.d.ts +32 -0
- package/dist/render/paint.d.ts +33 -0
- package/dist/render/renderer.d.ts +22 -0
- package/dist/render/svg.d.ts +4 -1
- package/dist/render/svgString.d.ts +6 -2
- package/dist/scene/cameraController.d.ts +42 -0
- package/dist/scene/floatingText.d.ts +51 -0
- package/dist/scene/node.d.ts +17 -4
- package/dist/scene/nodes.d.ts +14 -0
- package/dist/scene/parallax.d.ts +15 -0
- package/dist/scene/particles.d.ts +64 -0
- package/dist/scene/pool.d.ts +16 -0
- package/dist/scene/tween.d.ts +26 -0
- package/dist/studio/mcpMain.d.ts +1 -0
- package/dist/studio/mcpServer.d.ts +2 -0
- package/dist/studio/record.d.ts +54 -0
- package/dist/studio/run.d.ts +78 -0
- package/dist/studio/session.d.ts +80 -0
- package/dist/studio/timeline.d.ts +35 -0
- package/dist/studio/vitePlugin.d.ts +6 -0
- package/dist/studio-plugin.js +228 -0
- package/dist/ui/overlay.d.ts +6 -0
- package/dist/ui/transition.d.ts +107 -0
- package/dist/verify/audioFilmstrip.d.ts +39 -0
- package/dist/verify/ethnography.d.ts +67 -0
- package/dist/verify/gates.d.ts +160 -0
- package/dist/verify/layout.d.ts +66 -0
- package/dist/verify/ramp.d.ts +40 -0
- package/dist/world.d.ts +38 -2
- package/dist-studio/assets/index-C7tty_Wo.js +109 -0
- package/dist-studio/assets/index-CM3tjRQo.css +1 -0
- package/dist-studio/index.html +18 -0
- package/docs/API.md +473 -10
- package/docs/CONVENTIONS.md +223 -0
- package/docs/EMBED.md +85 -0
- package/docs/QUICKSTART.md +129 -0
- package/docs/STUDIO.md +97 -0
- package/docs/VERIFICATION.md +226 -0
- package/package.json +66 -11
package/dist/app/game.d.ts
CHANGED
|
@@ -2,6 +2,23 @@ import { World } from '../world';
|
|
|
2
2
|
import type { Node } from '../scene/node';
|
|
3
3
|
import { type InputLog, type InputMap } from '../input/actions';
|
|
4
4
|
import type { ClockConfig } from '../core/clock';
|
|
5
|
+
import { type TuningSpec, type TuningValues } from './tuning';
|
|
6
|
+
/**
|
|
7
|
+
* Optional boot splash. Rendered by the engine (so its colors are palette-guaranteed
|
|
8
|
+
* and never trip a contrast escape the way a hand-rolled loading <div> can) while
|
|
9
|
+
* `preload` runs. Pass `splash: false` to opt out entirely.
|
|
10
|
+
*/
|
|
11
|
+
export interface SplashConfig {
|
|
12
|
+
/** Splash headline (defaults to the game title). */
|
|
13
|
+
title?: string;
|
|
14
|
+
/** Background/foreground pair; defaults to a contrast-guaranteed pair from the game background. */
|
|
15
|
+
palette?: {
|
|
16
|
+
bg: string;
|
|
17
|
+
fg: string;
|
|
18
|
+
};
|
|
19
|
+
/** Keep the splash up at least this long, so a fast preload doesn't flash by. */
|
|
20
|
+
minDurationMs?: number;
|
|
21
|
+
}
|
|
5
22
|
export interface GameDefinition {
|
|
6
23
|
title: string;
|
|
7
24
|
width?: number;
|
|
@@ -14,11 +31,39 @@ export interface GameDefinition {
|
|
|
14
31
|
build(world: World): Node;
|
|
15
32
|
/** Optional compact probe snapshot for verification (defaults to World.probe). */
|
|
16
33
|
probe?(world: World): Record<string, unknown>;
|
|
34
|
+
/**
|
|
35
|
+
* Live-tunable parameters, declared once. Defaults ARE the config; Studio and
|
|
36
|
+
* tests override them via `createWorld(def, { tuning })` and the sim reads
|
|
37
|
+
* resolved values with `world.tune(key)`. Values are hashed + snapshotted.
|
|
38
|
+
*/
|
|
39
|
+
tuning?: TuningSpec;
|
|
40
|
+
/**
|
|
41
|
+
* Re-attach behaviors/controllers after `world.restore()` rebuilds the tree
|
|
42
|
+
* from data (closures do not survive a restore). One contract serves every
|
|
43
|
+
* carryover path: Studio knob changes, variant toggles, HMR, and net rollback.
|
|
44
|
+
*/
|
|
45
|
+
attach?(world: World): void;
|
|
46
|
+
/**
|
|
47
|
+
* Awaited before the world starts stepping — load fonts, sprite atlases, a
|
|
48
|
+
* SoundFont, anything async. The engine holds a splash on screen until it
|
|
49
|
+
* resolves, so there is no asset pop-in and no ungoverned pre-first-frame window.
|
|
50
|
+
*/
|
|
51
|
+
preload?(world: World): Promise<void>;
|
|
52
|
+
/** Boot splash config, or `false` to start on the first frame with no cover. */
|
|
53
|
+
splash?: SplashConfig | false;
|
|
17
54
|
}
|
|
18
55
|
/** Identity + defaults. Kept as a function so games read `export default defineGame({…})`. */
|
|
19
56
|
export declare function defineGame(def: GameDefinition): Required<Pick<GameDefinition, 'width' | 'height' | 'seed' | 'inputMap' | 'background'>> & GameDefinition;
|
|
20
|
-
|
|
21
|
-
|
|
57
|
+
export interface CreateWorldOptions {
|
|
58
|
+
seed?: number;
|
|
59
|
+
/** Overrides for declared tuning knobs (undeclared keys are dropped). */
|
|
60
|
+
tuning?: TuningValues;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Build a live, deterministic World from a game definition. No browser needed.
|
|
64
|
+
* `opts` as a bare number is the legacy seed override.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createWorld(def: GameDefinition, opts?: number | CreateWorldOptions): World;
|
|
22
67
|
/** The input map a game uses (with defaults applied). */
|
|
23
68
|
export declare function gameInputMap(def: GameDefinition): InputMap;
|
|
24
69
|
export interface HeadlessResult {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { GameDefinition } from './game';
|
|
2
|
+
export type TuningValue = number | string;
|
|
3
|
+
export type TuningValues = Record<string, TuningValue>;
|
|
4
|
+
export interface TuningKnob {
|
|
5
|
+
key: string;
|
|
6
|
+
/** Human label for panels; defaults to the key. */
|
|
7
|
+
label?: string;
|
|
8
|
+
type: 'number' | 'color' | 'enum';
|
|
9
|
+
default: TuningValue;
|
|
10
|
+
/** number knobs: slider bounds + resolution. */
|
|
11
|
+
min?: number;
|
|
12
|
+
max?: number;
|
|
13
|
+
step?: number;
|
|
14
|
+
/** enum knobs: the allowed values. */
|
|
15
|
+
options?: string[];
|
|
16
|
+
/** Panel grouping (e.g. 'jump', 'combat', 'style'). */
|
|
17
|
+
group?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Pure-view knob (palette, fonts): eligible for cheap re-apply without a sim
|
|
20
|
+
* rebuild. Sim-affecting knobs leave this unset.
|
|
21
|
+
*/
|
|
22
|
+
cosmetic?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface TuningSpec {
|
|
25
|
+
knobs: TuningKnob[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A named alternative of a game for A/B playtesting: a tuning preset and/or a
|
|
29
|
+
* definition patch. Tuning-only variants can hot-swap mid-play (rebuild with
|
|
30
|
+
* snapshot carryover); `patch` variants restart fresh.
|
|
31
|
+
*/
|
|
32
|
+
export interface Variant {
|
|
33
|
+
label: string;
|
|
34
|
+
tuning?: TuningValues;
|
|
35
|
+
patch?(def: GameDefinition): GameDefinition;
|
|
36
|
+
}
|
|
37
|
+
/** Sugar for declaring knobs without repeating the key in a label. */
|
|
38
|
+
export declare const knob: {
|
|
39
|
+
num(key: string, opts: {
|
|
40
|
+
default: number;
|
|
41
|
+
min?: number;
|
|
42
|
+
max?: number;
|
|
43
|
+
step?: number;
|
|
44
|
+
label?: string;
|
|
45
|
+
group?: string;
|
|
46
|
+
cosmetic?: boolean;
|
|
47
|
+
}): TuningKnob;
|
|
48
|
+
color(key: string, opts: {
|
|
49
|
+
default: string;
|
|
50
|
+
label?: string;
|
|
51
|
+
group?: string;
|
|
52
|
+
cosmetic?: boolean;
|
|
53
|
+
}): TuningKnob;
|
|
54
|
+
enumOf(key: string, opts: {
|
|
55
|
+
default: string;
|
|
56
|
+
options: string[];
|
|
57
|
+
label?: string;
|
|
58
|
+
group?: string;
|
|
59
|
+
cosmetic?: boolean;
|
|
60
|
+
}): TuningKnob;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a spec + overrides into concrete values: declared defaults first,
|
|
64
|
+
* overrides applied only for DECLARED keys (unknown keys are dropped — a typo'd
|
|
65
|
+
* override must not smuggle undeclared state into the hash), numbers clamped to
|
|
66
|
+
* [min, max] and coerced, enum values validated against `options`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveTuning(spec: TuningSpec | undefined, overrides?: TuningValues): TuningValues;
|
|
@@ -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;
|
package/dist/art/palette.d.ts
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
import type { Rng } from '../core/rng';
|
|
2
|
+
/**
|
|
3
|
+
* The Kentō swatch set — the single source of truth every named palette derives
|
|
4
|
+
* from. Neutrals form one continuous ground→ink ramp; each of the eight hues has
|
|
5
|
+
* a `Deep` tone (holds AA as a large mark on light washi) and a bright tone (pops
|
|
6
|
+
* on dark sumi/kuro). Traditional Japanese color names keep it on-brand. Pick a
|
|
7
|
+
* hue by name the way the site does, or use a ready-made `Palette` below.
|
|
8
|
+
*/
|
|
9
|
+
export declare const KENTO: {
|
|
10
|
+
readonly gofun: "#f7f1e2";
|
|
11
|
+
readonly washi: "#efe7d3";
|
|
12
|
+
readonly kinu: "#e4d8bd";
|
|
13
|
+
readonly line: "#d8cbac";
|
|
14
|
+
readonly kinako: "#b9a882";
|
|
15
|
+
readonly stone: "#6c6252";
|
|
16
|
+
readonly sumiSoft: "#494133";
|
|
17
|
+
readonly sumi: "#23201a";
|
|
18
|
+
readonly kuro: "#181820";
|
|
19
|
+
readonly yohaku: "#12121a";
|
|
20
|
+
readonly darkLine: "#2c2c36";
|
|
21
|
+
readonly shuDeep: "#b23a24";
|
|
22
|
+
readonly shu: "#d9583c";
|
|
23
|
+
readonly kakiDeep: "#bf6a1c";
|
|
24
|
+
readonly kaki: "#e79a49";
|
|
25
|
+
readonly koDeep: "#94741d";
|
|
26
|
+
readonly ko: "#e3c054";
|
|
27
|
+
readonly matsuDeep: "#4a7a3a";
|
|
28
|
+
readonly matsu: "#8bad52";
|
|
29
|
+
readonly asagiDeep: "#2c7a90";
|
|
30
|
+
readonly asagi: "#57bad2";
|
|
31
|
+
readonly aiDeep: "#2b4257";
|
|
32
|
+
readonly ai: "#5a86ad";
|
|
33
|
+
readonly fujiDeep: "#63548c";
|
|
34
|
+
readonly fuji: "#a091cf";
|
|
35
|
+
readonly sakuDeep: "#b0506e";
|
|
36
|
+
readonly saku: "#e097ac";
|
|
37
|
+
};
|
|
1
38
|
export interface Palette {
|
|
2
39
|
name: string;
|
|
3
40
|
bg: string;
|
|
@@ -10,11 +47,53 @@ export interface Palette {
|
|
|
10
47
|
warn: string;
|
|
11
48
|
/** A small ordered ramp for categorical fills. */
|
|
12
49
|
ramp: string[];
|
|
50
|
+
/** The full on-brand swatch set, for games that pick hues by name. */
|
|
51
|
+
swatches?: typeof KENTO;
|
|
13
52
|
}
|
|
53
|
+
/** Default light woodblock palette — washi ground, sumi ink, deep accents. */
|
|
14
54
|
export declare const MEADOW: Palette;
|
|
55
|
+
/** Dark counterpart — kuro ground, gofun ink, the same hues in their bright tone. */
|
|
15
56
|
export declare const DUSK: Palette;
|
|
57
|
+
/** Higher-key paper variant — brightest ground, same hue family. */
|
|
16
58
|
export declare const PAPER: Palette;
|
|
17
59
|
export declare const PALETTES: Record<string, Palette>;
|
|
18
60
|
/** Blend two hex colors (t in [0,1]). Deterministic, no allocations of note. */
|
|
19
61
|
export declare function mix(a: string, b: string, t: number): string;
|
|
20
62
|
export declare function withAlpha(hex: string, alpha: number): string;
|
|
63
|
+
export interface HSL {
|
|
64
|
+
/** Hue in degrees [0,360). */
|
|
65
|
+
h: number;
|
|
66
|
+
/** Saturation [0,1]. */
|
|
67
|
+
s: number;
|
|
68
|
+
/** Lightness [0,1]. */
|
|
69
|
+
l: number;
|
|
70
|
+
}
|
|
71
|
+
/** HSL → hex. h in degrees (wraps), s/l in [0,1]. */
|
|
72
|
+
export declare function hsl(h: number, s: number, l: number): string;
|
|
73
|
+
/** HSV/HSB → hex. h in degrees (wraps), s/v in [0,1]. */
|
|
74
|
+
export declare function hsv(h: number, s: number, v: number): string;
|
|
75
|
+
/** Decompose a hex color into HSL. Inverse of `hsl()`. */
|
|
76
|
+
export declare function hexToHsl(hex: string): HSL;
|
|
77
|
+
export interface DriftAmounts {
|
|
78
|
+
/** Max hue drift ± degrees. */
|
|
79
|
+
hue?: number;
|
|
80
|
+
/** Max saturation drift ± (absolute, [0,1]). */
|
|
81
|
+
sat?: number;
|
|
82
|
+
/** Max lightness drift ± (absolute, [0,1]). */
|
|
83
|
+
light?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Drift a color in HSL space by random amounts from `world.rng` (space-huggers'
|
|
87
|
+
* `mutate`). Deterministic given rng state; hue wraps, s/l clamp. Cosmetic, but
|
|
88
|
+
* because it consumes rng draws, call it in a stable order.
|
|
89
|
+
*/
|
|
90
|
+
export declare function mutateColor(rng: Rng, hex: string, amounts?: DriftAmounts): string;
|
|
91
|
+
/** Blend two hex colors in linear light (gamma-correct). t in [0,1]. */
|
|
92
|
+
export declare function mixLinear(a: string, b: string, t: number): string;
|
|
93
|
+
/**
|
|
94
|
+
* Sample a multi-stop gradient at t in [0,1], blending in linear light. Stops
|
|
95
|
+
* are evenly spaced hex colors (≥ 1). Great for procedural sky/heat/health ramps.
|
|
96
|
+
*/
|
|
97
|
+
export declare function sampleGradient(stops: readonly string[], t: number): string;
|
|
98
|
+
/** Materialize an n-color ramp from gradient stops (linear-light interpolation). */
|
|
99
|
+
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
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** A breakpoint on an RTPC curve: at input `x`, the target is `y`. */
|
|
2
|
+
export interface Breakpoint {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate a piecewise-linear RTPC curve at `x`. Breakpoints are sorted by x;
|
|
8
|
+
* values outside the range clamp to the nearest endpoint. This one primitive
|
|
9
|
+
* drives volume, filter cutoff, layer gain, and distance in a uniform, testable
|
|
10
|
+
* way — exactly what a Wwise RTPC / FMOD parameter automation does.
|
|
11
|
+
*/
|
|
12
|
+
export declare function evalCurve(curve: Breakpoint[], x: number): number;
|
|
13
|
+
export type DistanceModel = 'linear' | 'inverse' | 'exponential';
|
|
14
|
+
/**
|
|
15
|
+
* Distance-attenuation gain, matching the Web Audio PannerNode formulas exactly
|
|
16
|
+
* so an offline render agrees with live playback. `refDistance` is full volume,
|
|
17
|
+
* `maxDistance` is the floor, `rolloff` shapes the falloff.
|
|
18
|
+
*/
|
|
19
|
+
export declare function distanceGain(model: DistanceModel, distance: number, refDistance?: number, maxDistance?: number, rolloff?: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Equal-power stereo pan from a horizontal offset. dx is the source's screen-x
|
|
22
|
+
* relative to the listener; `spread` is the half-width at which pan saturates.
|
|
23
|
+
*/
|
|
24
|
+
export declare function panFromOffset(dx: number, spread?: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* A positional cue's mix: distance-attenuated gain × pan. Combines the two
|
|
27
|
+
* above into what a 2-D game actually needs. `hearing` is the cutoff beyond
|
|
28
|
+
* which the source is inaudible.
|
|
29
|
+
*/
|
|
30
|
+
export interface SpatialMix {
|
|
31
|
+
gain: number;
|
|
32
|
+
pan: number;
|
|
33
|
+
audible: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function spatialMix(dx: number, dy: number, hearing?: number, rolloff?: number): SpatialMix;
|
|
36
|
+
/** A named layer of vertical (stacked) adaptive music. */
|
|
37
|
+
export interface MusicLayer {
|
|
38
|
+
name: string;
|
|
39
|
+
/** The layer fades in from `fadeIn` and reaches full gain by `full` intensity. */
|
|
40
|
+
fadeIn: number;
|
|
41
|
+
full: number;
|
|
42
|
+
/** Optional cap on this layer's gain (default 1). */
|
|
43
|
+
maxGain?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Vertical layering: given an intensity value (0..1, e.g. combat proximity),
|
|
47
|
+
* return each layer's gain. Layers fade in at their thresholds so texture
|
|
48
|
+
* thickens with intensity without ever interrupting the flow — the single
|
|
49
|
+
* biggest adaptive-music payoff per line, and trivially deterministic.
|
|
50
|
+
*/
|
|
51
|
+
export declare function layerGains(layers: MusicLayer[], intensity: number): Record<string, number>;
|
|
52
|
+
/**
|
|
53
|
+
* A ducking envelope target: while a higher-priority bus (dialogue, a stinger)
|
|
54
|
+
* is active, the ducked bus drops by `amount` dB. Returns a linear gain the
|
|
55
|
+
* driver ramps toward with setTargetAtTime — deterministic because it's keyed
|
|
56
|
+
* off game state, not an audio sidechain.
|
|
57
|
+
*/
|
|
58
|
+
export declare function duckGain(active: boolean, amountDb?: number): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Song } from './music';
|
|
2
|
+
export interface AlbumTrack {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
intent: string;
|
|
6
|
+
make: () => Song;
|
|
7
|
+
}
|
|
8
|
+
/** The album. */
|
|
9
|
+
export declare const ALBUM: {
|
|
10
|
+
title: string;
|
|
11
|
+
subtitle: string;
|
|
12
|
+
concept: string;
|
|
13
|
+
tracks: AlbumTrack[];
|
|
14
|
+
};
|
|
15
|
+
/** Look up an album track by id. */
|
|
16
|
+
export declare function albumTrack(id: string): AlbumTrack | undefined;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Samples } from './pcm';
|
|
2
|
+
/** Amplitude ratio → decibels full-scale. Silence maps to -Infinity. */
|
|
3
|
+
export declare function dbfs(amp: number): number;
|
|
4
|
+
/** Root-mean-square level of a signal. */
|
|
5
|
+
export declare function rms(sig: Samples): number;
|
|
6
|
+
/** Peak absolute sample. */
|
|
7
|
+
export declare function peakAmp(sig: Samples): number;
|
|
8
|
+
/** Zero crossings per second — a rough "noisiness / brightness" proxy. */
|
|
9
|
+
export declare function zcr(sig: Samples, sampleRate?: number): number;
|
|
10
|
+
/**
|
|
11
|
+
* In-place iterative radix-2 Cooley–Tukey FFT. `re`/`im` length must be a power
|
|
12
|
+
* of two. Deterministic: twiddle factors come from dcos/dsin.
|
|
13
|
+
*/
|
|
14
|
+
export declare function fft(re: Float32Array, im: Float32Array): void;
|
|
15
|
+
/** Hann-windowed magnitude spectrum of a frame (length rounded down to pow2). */
|
|
16
|
+
export declare function magnitudeSpectrum(frame: Samples): Float32Array;
|
|
17
|
+
/**
|
|
18
|
+
* Spectral centroid (Hz) — the "center of mass" of the spectrum, a robust
|
|
19
|
+
* brightness measure. Averaged over Hann frames across the whole signal.
|
|
20
|
+
*/
|
|
21
|
+
export declare function spectralCentroid(sig: Samples, sampleRate?: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Spectral flux onset envelope: per-frame sum of positive magnitude increases.
|
|
24
|
+
* Peaks mark note onsets — the basis for tempo/onset-density features.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fluxEnvelope(sig: Samples, frameSize?: number, hop?: number): Float32Array;
|
|
27
|
+
/** Onsets per second — busy/percussive vs sparse/sustained. */
|
|
28
|
+
export declare function onsetDensity(sig: Samples, sampleRate?: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* Estimate tempo (BPM) by autocorrelating the flux onset envelope and picking
|
|
31
|
+
* the strongest lag in the 60–200 BPM band. Returns 0 if indeterminate.
|
|
32
|
+
*/
|
|
33
|
+
export declare function estimateTempo(sig: Samples, sampleRate?: number): number;
|
|
34
|
+
/** Crest factor in dB (peak / rms). Music usually lives ~8–20 dB; a tiny value
|
|
35
|
+
* means it's squashed/distorted, a huge value means it's mostly silence. */
|
|
36
|
+
export declare function crestFactorDb(sig: Samples): number;
|
|
37
|
+
/** Fractional energy in coarse frequency bands — the spectral balance of a mix.
|
|
38
|
+
* Returns fractions (summing to ~1) for sub / bass / lowMid / mid / high / air. */
|
|
39
|
+
export interface BandBalance {
|
|
40
|
+
sub: number;
|
|
41
|
+
bass: number;
|
|
42
|
+
lowMid: number;
|
|
43
|
+
mid: number;
|
|
44
|
+
high: number;
|
|
45
|
+
air: number;
|
|
46
|
+
}
|
|
47
|
+
export declare function bandBalance(sig: Samples, sampleRate?: number): BandBalance;
|
|
48
|
+
/** A compact numeric fingerprint of a signal — the audio-filmstrip's assertions. */
|
|
49
|
+
export interface AudioFeatures {
|
|
50
|
+
durationSec: number;
|
|
51
|
+
rms: number;
|
|
52
|
+
peakDb: number;
|
|
53
|
+
centroidHz: number;
|
|
54
|
+
zcr: number;
|
|
55
|
+
onsetDensity: number;
|
|
56
|
+
tempoBpm: number;
|
|
57
|
+
}
|
|
58
|
+
/** Extract the full feature vector used by verification. */
|
|
59
|
+
export declare function features(sig: Samples, sampleRate?: number): AudioFeatures;
|