hayao 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +123 -24
  2. package/bin/create-hayao.mjs +380 -0
  3. package/bin/hayao-mcp-cli.mjs +11 -0
  4. package/dist/app/browser.d.ts +65 -4
  5. package/dist/app/game.d.ts +59 -10
  6. package/dist/app/tuning.d.ts +68 -0
  7. package/dist/art/palette.d.ts +41 -0
  8. package/dist/audio/adaptive.d.ts +58 -0
  9. package/dist/audio/album.d.ts +16 -0
  10. package/dist/audio/analysis.d.ts +59 -0
  11. package/dist/audio/audio.d.ts +39 -1
  12. package/dist/audio/chord.d.ts +17 -0
  13. package/dist/audio/genres.d.ts +11 -0
  14. package/dist/audio/lint.d.ts +29 -0
  15. package/dist/audio/match.d.ts +38 -0
  16. package/dist/audio/music.d.ts +88 -0
  17. package/dist/audio/pcm.d.ts +54 -0
  18. package/dist/audio/quality.d.ts +28 -0
  19. package/dist/audio/reverb.d.ts +15 -0
  20. package/dist/audio/synth.d.ts +63 -0
  21. package/dist/audio/theory.d.ts +64 -0
  22. package/dist/audio/zzfx.d.ts +14 -0
  23. package/dist/content/campaign.d.ts +69 -0
  24. package/dist/content/generate.d.ts +78 -0
  25. package/dist/content/level.d.ts +93 -0
  26. package/dist/content/worldgraph.d.ts +73 -0
  27. package/dist/core/clock.d.ts +1 -1
  28. package/dist/core/dmath.d.ts +2 -0
  29. package/dist/core/projection.d.ts +36 -0
  30. package/dist/core/rng.d.ts +9 -0
  31. package/dist/hayao.global.js +200 -0
  32. package/dist/index.d.ts +39 -1
  33. package/dist/index.js +6558 -686
  34. package/dist/index.js.map +4 -4
  35. package/dist/index.min.js +200 -0
  36. package/dist/input/actions.d.ts +60 -6
  37. package/dist/input/gamepad.d.ts +101 -0
  38. package/dist/input/source.d.ts +133 -1
  39. package/dist/logic/coroutine.d.ts +68 -0
  40. package/dist/mcp.js +31225 -0
  41. package/dist/rasterize-worker-lite.mjs +13 -0
  42. package/dist/render/canvas.d.ts +6 -5
  43. package/dist/render/canvas2d-core.d.ts +9 -0
  44. package/dist/render/commands.d.ts +82 -2
  45. package/dist/render/paint.d.ts +41 -0
  46. package/dist/render/renderer.d.ts +47 -0
  47. package/dist/render/svg.d.ts +5 -1
  48. package/dist/render/svgString.d.ts +6 -2
  49. package/dist/render/webgl.d.ts +176 -0
  50. package/dist/scene/cameraController.d.ts +42 -0
  51. package/dist/scene/iso.d.ts +73 -0
  52. package/dist/scene/node.d.ts +98 -6
  53. package/dist/scene/nodes.d.ts +48 -0
  54. package/dist/scene/parallax.d.ts +15 -0
  55. package/dist/scene/particles.d.ts +19 -0
  56. package/dist/scene/verletChain.d.ts +76 -0
  57. package/dist/studio/mcpMain.d.ts +1 -0
  58. package/dist/studio/mcpServer.d.ts +2 -0
  59. package/dist/studio/record.d.ts +54 -0
  60. package/dist/studio/run.d.ts +78 -0
  61. package/dist/studio/session.d.ts +80 -0
  62. package/dist/studio/timeline.d.ts +35 -0
  63. package/dist/studio/vitePlugin.d.ts +6 -0
  64. package/dist/studio-plugin.js +228 -0
  65. package/dist/ui/overlay.d.ts +6 -0
  66. package/dist/ui/touch.d.ts +51 -0
  67. package/dist/verify/audioFilmstrip.d.ts +39 -0
  68. package/dist/verify/dom.d.ts +26 -0
  69. package/dist/verify/ethnography.d.ts +67 -0
  70. package/dist/verify/gates.d.ts +160 -0
  71. package/dist/verify/layout.d.ts +30 -3
  72. package/dist/verify/ramp.d.ts +40 -0
  73. package/dist/world.d.ts +109 -8
  74. package/dist-studio/assets/index-C7tty_Wo.js +109 -0
  75. package/dist-studio/assets/index-CM3tjRQo.css +1 -0
  76. package/dist-studio/index.html +18 -0
  77. package/docs/API.md +300 -23
  78. package/docs/CONVENTIONS.md +511 -0
  79. package/docs/EMBED.md +90 -0
  80. package/docs/QUICKSTART.md +141 -0
  81. package/docs/STUDIO.md +97 -0
  82. package/docs/VERIFICATION.md +256 -0
  83. package/package.json +40 -6
@@ -1,12 +1,50 @@
1
- /** action name → physical key codes (KeyboardEvent.code strings). */
2
- export type InputMap = Record<string, string[]>;
1
+ /**
2
+ * action name → physical bindings (KeyboardEvent.code strings, or pointer
3
+ * buttons `mouse.left` / `mouse.right` / `mouse.middle`). Readonly on both
4
+ * levels so `as const` maps type-check without casts.
5
+ */
6
+ export type InputMap = Readonly<Record<string, readonly string[]>>;
7
+ /** Per-frame analog axis values (already quantized). Recorded into the log + hash. */
8
+ export type AxisFrame = Record<string, number>;
3
9
  export declare class InputState {
4
10
  private down;
5
11
  private prev;
6
- /** Analog axes (e.g. from gamepad or pointer), sampled per step. */
12
+ /**
13
+ * Analog axes (e.g. from pointer or gamepad), sampled per step. By default
14
+ * these are LIVE host samples — NOT hashed or logged (see PointerSource).
15
+ * Axes passed to `beginFrame`'s second argument are the exception: they are
16
+ * *logged* (enter `getState()` → hash, and the input log), so quantized analog
17
+ * aim replays bit-exactly. Both live and logged values read back via `axis()`.
18
+ */
7
19
  readonly axes: Map<string, number>;
8
- /** Engine: set which actions are down this step (from live input or replay). */
9
- beginFrame(actionsDown: Iterable<string>): void;
20
+ /** The subset of axes that are part of the deterministic log + hash this frame. */
21
+ private logged;
22
+ /**
23
+ * Action names some source has declared it can produce (null until the first
24
+ * declareActions call). Headless worlds with no sources never declare, so
25
+ * they never warn — the typo guard only arms once a host wires real input.
26
+ */
27
+ private declared;
28
+ /** Unknown names already warned about (warn ONCE per name). */
29
+ private warned;
30
+ /**
31
+ * Register action names a source (or input map) can produce. The browser
32
+ * driver declares the keyboard map's actions and the pointer's `mouse.*`
33
+ * buttons; custom sources should declare theirs too. Once at least one
34
+ * declaration has happened, querying an undeclared action name via
35
+ * `isDown`/`justPressed`/`justReleased` logs a console.warn ONCE per name —
36
+ * catching `isDown('jmup')` typos that would otherwise fail silently.
37
+ */
38
+ declareActions(names: Iterable<string>): void;
39
+ /** Warn-once guard for querying an action no source has declared. O(1). */
40
+ private checkDeclared;
41
+ /**
42
+ * Engine: set which actions are down this step (from live input or replay).
43
+ * `axes` (optional) are QUANTIZED analog values that become part of the log
44
+ * and hash for this frame — use `snapAxis`/`quantizeAngle` at the host edge so
45
+ * replay and lockstep netplay reproduce analog aim exactly.
46
+ */
47
+ beginFrame(actionsDown: Iterable<string>, axes?: AxisFrame): void;
10
48
  isDown(action: string): boolean;
11
49
  justPressed(action: string): boolean;
12
50
  justReleased(action: string): boolean;
@@ -16,25 +54,41 @@ export declare class InputState {
16
54
  getState(): {
17
55
  down: string[];
18
56
  prev: string[];
57
+ axes?: Array<[string, number]>;
19
58
  };
20
59
  setState(s: {
21
60
  down: string[];
22
61
  prev: string[];
62
+ axes?: Array<[string, number]>;
23
63
  }): void;
24
64
  }
65
+ /**
66
+ * Quantize an analog value into `buckets` levels across `[min,max]` and return
67
+ * the SNAPPED value (deterministic — same input → same output on every engine).
68
+ * Feed the result through `beginFrame`'s axes so analog input is replay-exact.
69
+ */
70
+ export declare function snapAxis(value: number, buckets: number, min?: number, max?: number): number;
71
+ /** Snap an angle (radians) to the nearest of `buckets` evenly-spaced headings. */
72
+ export declare function quantizeAngle(radians: number, buckets: number): number;
25
73
  /** Map a set of raw key codes to the set of actions they trigger. */
26
74
  export declare function keysToActions(map: InputMap, keysDown: Set<string>): string[];
27
75
  /** Records the per-frame action-down sets → a compact, replayable input log. */
28
76
  export declare class InputRecorder {
29
77
  private frames;
30
- record(actionsDown: string[]): void;
78
+ private axes;
79
+ private anyAxes;
80
+ record(actionsDown: string[], axes?: AxisFrame): void;
31
81
  get length(): number;
32
82
  toLog(): InputLog;
33
83
  }
34
84
  export interface InputLog {
35
85
  frames: string[][];
86
+ /** Optional per-frame quantized analog axes, aligned to `frames` (absent = none). */
87
+ axes?: Array<AxisFrame | undefined>;
36
88
  }
37
89
  /** Replays a recorded log: returns the action set for frame index i. */
38
90
  export declare function frameActions(log: InputLog, i: number): string[];
91
+ /** The logged analog axes for frame index i, or undefined if none were recorded. */
92
+ export declare function frameAxes(log: InputLog, i: number): AxisFrame | undefined;
39
93
  /** A default set of common bindings games can start from. */
40
94
  export declare const DEFAULT_INPUT_MAP: InputMap;
@@ -0,0 +1,101 @@
1
+ import { type InputState } from './actions';
2
+ import type { KeyboardSource } from './source';
3
+ /** A mapping from Gamepad.buttons indices to action names. */
4
+ export type GamepadMap = Record<number, string>;
5
+ export interface GamepadSourceOptions {
6
+ /**
7
+ * Gamepad index (0–3). Default 0.
8
+ * Tip: listen for the 'gamepadconnected' event to discover the right index;
9
+ * the first connected controller is always index 0.
10
+ */
11
+ index?: number;
12
+ /**
13
+ * Bucket count for quantizing analog stick values (±1 range).
14
+ * Higher = finer precision, larger axes log. Default 128 (~0.016 resolution).
15
+ */
16
+ stickBuckets?: number;
17
+ /**
18
+ * Bucket count for quantizing trigger values (0..1 range).
19
+ * Default 64 (~0.016 resolution).
20
+ */
21
+ triggerBuckets?: number;
22
+ /**
23
+ * Deadzone magnitude radius (0..1) applied to each stick independently before
24
+ * quantization. Values below this threshold are snapped to zero; above it the
25
+ * range is remapped to 0..1 so the output always uses the full scale.
26
+ * Default 0.12 (typical hardware drift).
27
+ */
28
+ deadzone?: number;
29
+ /**
30
+ * Gamepad button index → action name. Pressed buttons call
31
+ * `keyboard.setHeld(action, on)` so they enter the SAME deterministic action
32
+ * log as keys and are covered by record/replay + netplay.
33
+ * Defaults to DEFAULT_GAMEPAD_MAP (face buttons + d-pad mapped to common actions).
34
+ */
35
+ buttonMap?: GamepadMap;
36
+ /**
37
+ * The KeyboardSource to drive actions through. Required for button→action
38
+ * routing. Typically `handle.input` from the GameHandle returned by runBrowser.
39
+ */
40
+ keyboard?: KeyboardSource;
41
+ }
42
+ /**
43
+ * Axis keys written each step:
44
+ *
45
+ * gamepad.lx / gamepad.ly — left stick, ±1 (after deadzone + quantize)
46
+ * gamepad.rx / gamepad.ry — right stick, ±1
47
+ * gamepad.lt / gamepad.rt — left / right trigger, 0..1
48
+ * gamepad.dpad.x — d-pad horizontal, −1 / 0 / +1
49
+ * gamepad.dpad.y — d-pad vertical, −1 / 0 / +1 (−1 = up)
50
+ *
51
+ * Read them in the sim with `world.input.axis('gamepad.lx')`.
52
+ *
53
+ * Determinism note: axes written by sample() are live host samples and are NOT
54
+ * part of the string input log or world.hash() by default. Buttons routed
55
+ * through a KeyboardSource DO enter the log. For bit-exact analog replay, pass
56
+ * quantized axes as the second argument to world.step(actions, axes) — they
57
+ * then enter getState() → hash and InputRecorder.
58
+ */
59
+ export declare class GamepadSource {
60
+ private index;
61
+ private stickBuckets;
62
+ private triggerBuckets;
63
+ private deadzone;
64
+ private buttonMap;
65
+ private keyboard;
66
+ /** Track which buttons were pressed last frame to detect edge transitions. */
67
+ private prevPressed;
68
+ constructor(opts?: GamepadSourceOptions);
69
+ /**
70
+ * Write the current gamepad state into InputState axes (quantized). Call once
71
+ * per fixed step, before world.advance() — the browser driver does this via
72
+ * the `sources` option in RunOptions. See also: runBrowser, GameHandle.
73
+ */
74
+ sample(input: InputState): void;
75
+ /**
76
+ * Release all held actions and clean up. Call when removing the source or
77
+ * switching gamepads.
78
+ */
79
+ dispose(): void;
80
+ }
81
+ /**
82
+ * Standard Gamepad API button mapping for a "Standard Gamepad" layout
83
+ * (Xbox / PlayStation / Switch Pro controllers).
84
+ *
85
+ * Index Xbox PlayStation Action
86
+ * 0 A Cross confirm
87
+ * 1 B Circle cancel
88
+ * 2 X Square action2
89
+ * 3 Y Triangle action
90
+ * 8 Select/View Share restart (hold to restart)
91
+ * 9 Start/Menu Options confirm (pause menu OK)
92
+ * 12 D-pad up up
93
+ * 13 D-pad down down
94
+ * 14 D-pad left left
95
+ * 15 D-pad right right
96
+ *
97
+ * Triggers (6/7) and bumpers (4/5) are intentionally omitted — their analog
98
+ * values are exposed as gamepad.lt / gamepad.rt axes instead, and games can
99
+ * add them to a custom map if they want discrete actions.
100
+ */
101
+ export declare const DEFAULT_GAMEPAD_MAP: GamepadMap;
@@ -1,8 +1,25 @@
1
- import { type InputMap } from './actions';
1
+ import { type InputMap, type InputState } from './actions';
2
+ /**
3
+ * Minimal contract for a host-side input source. Implement this to feed any
4
+ * hardware (gamepad, MIDI, accelerometer, on-screen controls) into the sim's
5
+ * InputState.axes without touching the engine internals.
6
+ *
7
+ * The engine samples each registered source once per fixed step, BEFORE
8
+ * world.advance(). Register via RunOptions.sources or GameHandle.addSource().
9
+ */
10
+ export interface InputSource {
11
+ /** Write quantized values into input.axes. Called before each world.advance(). */
12
+ sample(input: InputState): void;
13
+ /** Optional cleanup (remove listeners, release held actions). */
14
+ dispose?(): void;
15
+ }
16
+ import type { Vec2 } from '../core/math';
2
17
  export declare class KeyboardSource {
3
18
  private keysDown;
4
19
  /** Virtual action taps (from DOM buttons etc.) pending consumption by a step. */
5
20
  private pressed;
21
+ /** Sustained virtual actions (held on-screen controls) — persist until released. */
22
+ private held;
6
23
  private map;
7
24
  private target;
8
25
  private onDown;
@@ -11,6 +28,8 @@ export declare class KeyboardSource {
11
28
  constructor(map: InputMap, target?: Document | HTMLElement);
12
29
  /** The actions currently held down, as a stable sorted array. */
13
30
  currentActions(): string[];
31
+ /** The action names this source's map can produce (for InputState.declareActions). */
32
+ actionNames(): string[];
14
33
  /**
15
34
  * Virtually tap an action (DOM button, touch control). The tap is held until
16
35
  * at least one fixed step has sampled it — the driver calls clearPressed()
@@ -20,6 +39,119 @@ export declare class KeyboardSource {
20
39
  press(action: string): void;
21
40
  /** Consume pending virtual taps (driver-called after ≥1 step ran). */
22
41
  clearPressed(): void;
42
+ /**
43
+ * Sustain an action for as long as an on-screen control is held. Unlike
44
+ * `press()` (an edge-shaped tap cleared after one step), a held action models
45
+ * STATE: it stays in `currentActions()` every step until `setHeld(action,
46
+ * false)` / `releaseHeld(action)`. This is what a virtual joystick or
47
+ * hold-to-fire button wants — set it on drag-start, clear it on release, no
48
+ * re-press-per-frame. Held actions still flow through the same deterministic
49
+ * action set as keys.
50
+ */
51
+ setHeld(action: string, on?: boolean): void;
52
+ /** Release a sustained action (sugar for `setHeld(action, false)`). */
53
+ releaseHeld(action: string): void;
54
+ /** Drop all sustained actions (e.g. on blur / control teardown). */
55
+ clearHeld(): void;
23
56
  setMap(map: InputMap): void;
24
57
  dispose(): void;
25
58
  }
59
+ /** A live pointer reading, in DESIGN space (already un-letterboxed). */
60
+ export interface PointerReadout {
61
+ /** Design-space x. Falls outside 0..width when the pointer leaves the letterbox. */
62
+ x: number;
63
+ y: number;
64
+ /** Is the primary pointer/finger currently pressed? */
65
+ down: boolean;
66
+ /** Has the pointer ever produced an event (else x/y are a neutral 0,0)? */
67
+ active: boolean;
68
+ /** Stable per-touch id (from PointerEvent.pointerId). Absent on the primary hover readout. */
69
+ id?: number;
70
+ }
71
+ /** What a PointerSource needs from a renderer: the DOM node + the design mapper. */
72
+ export interface PointerTarget {
73
+ readonly element?: HTMLElement | SVGElement;
74
+ toDesign?(clientX: number, clientY: number): Vec2;
75
+ }
76
+ export interface PointerSourceOptions {
77
+ /**
78
+ * Allow the browser's context menu on the attached element. Default false —
79
+ * the menu is suppressed (preventDefault on 'contextmenu') so right-click is
80
+ * usable as game input.
81
+ */
82
+ contextMenu?: boolean;
83
+ /**
84
+ * Route pointer buttons into the deterministic action pipeline: while a
85
+ * button is held, `sample()` calls `keyboard.setHeld('mouse.left' |
86
+ * 'mouse.right' | 'mouse.middle')` so buttons enter the SAME actionsDown log
87
+ * as keys — `justPressed('mouse.right')` works, replays stay exact, and an
88
+ * inputMap can bind them (`shield: ['mouse.right']`). The browser driver
89
+ * wires this to the GameHandle's KeyboardSource automatically.
90
+ */
91
+ keyboard?: KeyboardSource;
92
+ }
93
+ /** The pointer-button action names PointerSource can hold (via its keyboard). */
94
+ export declare const MOUSE_ACTIONS: readonly ["mouse.left", "mouse.right", "mouse.middle"];
95
+ /**
96
+ * Continuous pointer / touch input — the sibling of KeyboardSource for games
97
+ * driven by *where* the cursor is (slice, aim, drag, point-and-click, placement).
98
+ * It listens on the renderer's canvas, converts every event to DESIGN space via
99
+ * `renderer.toDesign`, and each fixed step writes the sample into InputState's
100
+ * analog axes: `pointer.x`, `pointer.y`, `pointer.down` (1/0), plus
101
+ * `pointer.right` / `pointer.middle` (1/0) for the secondary mouse buttons
102
+ * (the context menu is suppressed by default so right-click is usable — see
103
+ * PointerSourceOptions). Sim code then reads `world.input.axis('pointer.x')`
104
+ * — no out-of-engine letterbox glue. With `keyboard` wired, buttons also enter
105
+ * the action pipeline as `mouse.left/right/middle` (bindable in an inputMap,
106
+ * `justPressed`-able, replay-exact).
107
+ *
108
+ * Determinism note: axes are host-sampled live input and are NOT part of the
109
+ * string input log or the world hash. `sample()` QUANTIZES design coords to a
110
+ * 1/8-px grid so a recorded axes log (Studio sessions) replays bit-exactly —
111
+ * the sim only ever sees the quantized values, live or replayed. For lockstep
112
+ * netplay, still prefer discrete actions (map a tap to a key via
113
+ * KeyboardSource.press, or snap position to a grid cell). See docs/CONVENTIONS.md.
114
+ *
115
+ * Multitouch: `read()`/`sample()` describe the PRIMARY pointer (mouse or the
116
+ * most-recent finger) and are byte-identical to the single-pointer past. For
117
+ * dual-stick / multi-finger schemes, read every live touch via `readAll()` —
118
+ * each carries its stable `id` so you can tell which finger is which.
119
+ */
120
+ export declare class PointerSource {
121
+ private clientX;
122
+ private clientY;
123
+ private isDown;
124
+ private rightDown;
125
+ private middleDown;
126
+ private seen;
127
+ /** Live touches by pointerId (a pressed finger/button); excludes hover-only mouse. */
128
+ private touches;
129
+ private target;
130
+ private el;
131
+ private keyboard;
132
+ private onMove;
133
+ private onDown;
134
+ private onUp;
135
+ private onCtx;
136
+ constructor(target: PointerTarget, opts?: PointerSourceOptions);
137
+ /** The current pointer position in design space + press state. */
138
+ read(): PointerReadout;
139
+ /**
140
+ * Every live (pressed) touch in DESIGN space, ordered by ascending id so the
141
+ * array is stable to iterate. Empty when nothing is pressed. This is the
142
+ * multitouch channel — a dual-stick game reads `left = touches[0]`, etc., or
143
+ * partitions by screen half.
144
+ */
145
+ readAll(): PointerReadout[];
146
+ /**
147
+ * Write the current sample into an InputState's axes as pointer.x / pointer.y /
148
+ * pointer.down, plus pointer.right / pointer.middle (0/1) for the secondary
149
+ * buttons. Call once per step (the browser driver does this before advance).
150
+ *
151
+ * When a keyboard is wired (PointerSourceOptions.keyboard), buttons are ALSO
152
+ * held as actions `mouse.left` / `mouse.right` / `mouse.middle` so they flow
153
+ * through the same deterministic actionsDown log as keys.
154
+ */
155
+ sample(input: InputState): void;
156
+ dispose(): void;
157
+ }
@@ -0,0 +1,68 @@
1
+ /** Resumes after `seconds` of accumulated sim time. */
2
+ export declare function sleep(seconds: number): Wait;
3
+ /** Resumes on the first step where `cond()` is true (checked once per step). */
4
+ export declare function waitFor(cond: () => boolean): Wait;
5
+ /** Resumes on the next step — yields one frame (e.g. to let a spawn render). */
6
+ export declare function nextStep(): Wait;
7
+ /**
8
+ * Resumes when ANY of the waits completes; the yield expression evaluates to
9
+ * the winning index (ties resolve to the lowest index — deterministic).
10
+ */
11
+ export declare function race(...waits: Wait[]): Wait;
12
+ /** Resumes when ALL of the waits have completed. */
13
+ export declare function all(...waits: Wait[]): Wait;
14
+ interface SleepWait {
15
+ kind: 'sleep';
16
+ left: number;
17
+ }
18
+ interface CondWait {
19
+ kind: 'cond';
20
+ cond: () => boolean;
21
+ /** Memo so a satisfied condition is never re-evaluated (inside all()). */
22
+ met?: boolean;
23
+ }
24
+ interface NextWait {
25
+ kind: 'next';
26
+ passed?: boolean;
27
+ }
28
+ interface RaceWait {
29
+ kind: 'race';
30
+ waits: Wait[];
31
+ }
32
+ interface AllWait {
33
+ kind: 'all';
34
+ waits: Wait[];
35
+ }
36
+ /** What a coroutine may yield. A `CoroutineHandle` yield joins that runner. */
37
+ export type Wait = SleepWait | CondWait | NextWait | RaceWait | AllWait | CoroutineHandle;
38
+ /** A started coroutine. `done` flips when it returns, throws, or is stopped. */
39
+ export interface CoroutineHandle {
40
+ readonly name: string;
41
+ readonly done: boolean;
42
+ /** Kill the runner (its generator's finally blocks run via `return()`). */
43
+ stop(): void;
44
+ }
45
+ /**
46
+ * A deterministic scheduler for many concurrent coroutines. Owns no clock —
47
+ * call `step(dt)` from wherever your game advances (typically a brain node's
48
+ * `onUpdate`), so coroutine time IS sim time.
49
+ */
50
+ export declare class Coroutines {
51
+ private runners;
52
+ /** Started since the last step boundary; adopted at the top of the next step. */
53
+ private pending;
54
+ private nextId;
55
+ /**
56
+ * Register a coroutine. Its body runs (up to the first yield) on the NEXT
57
+ * `step()` — never immediately, and never mid-step. Pass a `name` so a
58
+ * thrown generator warns something better than "co3".
59
+ */
60
+ start(gen: () => Generator<Wait, void, number>, name?: string): CoroutineHandle;
61
+ /** Advance every live runner by one fixed step, in insertion order. */
62
+ step(dt: number): void;
63
+ /** Stop every runner (including ones not yet adopted). */
64
+ stopAll(): void;
65
+ /** Live runner count (started-but-not-yet-stepped ones included). */
66
+ get active(): number;
67
+ }
68
+ export {};