1681-engine 0.5.10 → 0.6.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 (81) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +12 -6
  3. package/dist/1681-engine.js +582 -522
  4. package/dist/ai/behaviorTree.js +198 -0
  5. package/dist/ai/buildTree.js +54 -0
  6. package/dist/audio/spatial.js +118 -0
  7. package/dist/cjs/electron/webPatcher.d.ts +4 -1
  8. package/dist/cjs/electron/webPatcher.js +9 -2
  9. package/dist/dialogue/DialogueRunner.js +173 -0
  10. package/dist/dialogue/expr.js +137 -0
  11. package/dist/dialogue/validateDialogue.js +52 -0
  12. package/dist/ecs/prefab.js +94 -0
  13. package/dist/electron/webPatcher.js +16 -10
  14. package/dist/input/Actions.js +58 -27
  15. package/dist/input/InputCapture.js +98 -0
  16. package/dist/input/Keymap.js +32 -26
  17. package/dist/input/inputTape.js +68 -0
  18. package/dist/mobile/backButton.js +40 -0
  19. package/dist/mobile/capacitor.js +48 -0
  20. package/dist/mobile/screen.js +31 -0
  21. package/dist/mobile.js +12 -0
  22. package/dist/nav/flowField.js +58 -0
  23. package/dist/nav/steering.js +77 -0
  24. package/dist/net/InterpolationBuffer.js +30 -23
  25. package/dist/net/LagCompensator.js +100 -0
  26. package/dist/net/Room.js +122 -43
  27. package/dist/net/RoomHost.js +13 -4
  28. package/dist/net/SpatialHash.js +66 -0
  29. package/dist/types/ai/behaviorTree.d.ts +133 -0
  30. package/dist/types/ai/behaviorTree.d.ts.map +1 -0
  31. package/dist/types/ai/buildTree.d.ts +55 -0
  32. package/dist/types/ai/buildTree.d.ts.map +1 -0
  33. package/dist/types/audio/spatial.d.ts +107 -0
  34. package/dist/types/audio/spatial.d.ts.map +1 -0
  35. package/dist/types/dialogue/DialogueRunner.d.ts +78 -0
  36. package/dist/types/dialogue/DialogueRunner.d.ts.map +1 -0
  37. package/dist/types/dialogue/expr.d.ts +12 -0
  38. package/dist/types/dialogue/expr.d.ts.map +1 -0
  39. package/dist/types/dialogue/script.d.ts +78 -0
  40. package/dist/types/dialogue/script.d.ts.map +1 -0
  41. package/dist/types/dialogue/validateDialogue.d.ts +21 -0
  42. package/dist/types/dialogue/validateDialogue.d.ts.map +1 -0
  43. package/dist/types/ecs/prefab.d.ts +84 -0
  44. package/dist/types/ecs/prefab.d.ts.map +1 -0
  45. package/dist/types/electron/webPatcher.d.ts +4 -1
  46. package/dist/types/electron/webPatcher.d.ts.map +1 -1
  47. package/dist/types/index.d.ts +15 -1
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/input/Actions.d.ts +14 -0
  50. package/dist/types/input/Actions.d.ts.map +1 -1
  51. package/dist/types/input/InputCapture.d.ts +53 -0
  52. package/dist/types/input/InputCapture.d.ts.map +1 -0
  53. package/dist/types/input/Keymap.d.ts +12 -6
  54. package/dist/types/input/Keymap.d.ts.map +1 -1
  55. package/dist/types/input/inputTape.d.ts +66 -0
  56. package/dist/types/input/inputTape.d.ts.map +1 -0
  57. package/dist/types/mobile/backButton.d.ts +30 -0
  58. package/dist/types/mobile/backButton.d.ts.map +1 -0
  59. package/dist/types/mobile/capacitor.d.ts +62 -0
  60. package/dist/types/mobile/capacitor.d.ts.map +1 -0
  61. package/dist/types/mobile/index.d.ts +4 -0
  62. package/dist/types/mobile/index.d.ts.map +1 -0
  63. package/dist/types/mobile/screen.d.ts +27 -0
  64. package/dist/types/mobile/screen.d.ts.map +1 -0
  65. package/dist/types/nav/flowField.d.ts +31 -0
  66. package/dist/types/nav/flowField.d.ts.map +1 -0
  67. package/dist/types/nav/steering.d.ts +47 -0
  68. package/dist/types/nav/steering.d.ts.map +1 -0
  69. package/dist/types/net/InterpolationBuffer.d.ts +5 -0
  70. package/dist/types/net/InterpolationBuffer.d.ts.map +1 -1
  71. package/dist/types/net/LagCompensator.d.ts +70 -0
  72. package/dist/types/net/LagCompensator.d.ts.map +1 -0
  73. package/dist/types/net/Room.d.ts +72 -3
  74. package/dist/types/net/Room.d.ts.map +1 -1
  75. package/dist/types/net/RoomHost.d.ts +8 -3
  76. package/dist/types/net/RoomHost.d.ts.map +1 -1
  77. package/dist/types/net/SpatialHash.d.ts +25 -0
  78. package/dist/types/net/SpatialHash.d.ts.map +1 -0
  79. package/dist/types/ui/ScrollView.d.ts.map +1 -1
  80. package/dist/ui/ScrollView.js +10 -8
  81. package/package.json +5 -1
@@ -0,0 +1,66 @@
1
+ import type { Actions } from './Actions.js';
2
+ /**
3
+ * A recording of what a player did, step by step, as plain JSON: attach it to a bug report and the bug plays again,
4
+ * or keep it as a replay. Only changes are kept (an action's value on the step it changed), so a minute of play is a
5
+ * few hundred numbers, not thousands.
6
+ */
7
+ export interface InputTape {
8
+ version: 1;
9
+ /** The actions, in the order `changes` refers to them. */
10
+ actions: string[];
11
+ /** Steps recorded. */
12
+ steps: number;
13
+ /** `[step, action index, value]`, in step order: from that step on the action reads `value` (0 up, 1 held). */
14
+ changes: [number, number, number][];
15
+ /** The game's own: a seed, a level, a build, whatever the replay needs to start the same. */
16
+ meta?: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * Records an `Actions`' every step until stopped.
20
+ *
21
+ * ```ts
22
+ * const rec = new InputRecorder(actions, { seed, level: 'forest-2' });
23
+ * // … play …
24
+ * const tape = rec.stop(); // JSON.stringify it into the bug report
25
+ * ```
26
+ */
27
+ export declare class InputRecorder<A extends string> {
28
+ private readonly meta?;
29
+ private readonly order;
30
+ private readonly index;
31
+ private readonly last;
32
+ private readonly changes;
33
+ private steps;
34
+ private readonly untap;
35
+ private stopped;
36
+ constructor(actions: Actions<A>, meta?: Record<string, unknown> | undefined);
37
+ /** Steps recorded so far. */
38
+ get length(): number;
39
+ /** Stops recording and returns the tape (the same one on every later call). */
40
+ stop(): InputTape;
41
+ private capture;
42
+ private note;
43
+ }
44
+ /**
45
+ * Plays a tape into an `Actions`: each `poll` takes the next step from it instead of the devices, and when the tape
46
+ * ends the devices answer again (`onEnd` says so).
47
+ *
48
+ * ```ts
49
+ * new InputPlayer(tape).playInto(actions, () => console.log('replay over'));
50
+ * ```
51
+ */
52
+ export declare class InputPlayer<A extends string = string> {
53
+ readonly tape: InputTape;
54
+ private step;
55
+ private next;
56
+ private readonly values;
57
+ constructor(tape: InputTape);
58
+ /** Steps played so far. */
59
+ get position(): number;
60
+ get done(): boolean;
61
+ /** The next step's values, or null once the tape is over. */
62
+ read(): ReadonlyMap<A, number> | null;
63
+ /** Drives `actions` from the tape until it ends; `onEnd` is called on the poll that finds it over. */
64
+ playInto<B extends string>(actions: Actions<B>, onEnd?: () => void): void;
65
+ }
66
+ //# sourceMappingURL=inputTape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputTape.d.ts","sourceRoot":"","sources":["../../../src/input/inputTape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,CAAC,CAAC;IACX,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,+GAA+G;IAC/G,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACpC,6FAA6F;IAC7F,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,qBAAa,aAAa,CAAC,CAAC,SAAS,MAAM;IAWvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IAVxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,OAAO,CAA0B;gBAGvC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;IAKjD,6BAA6B;IAC7B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,+EAA+E;IAC/E,IAAI,IAAI,SAAS;IAQjB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,IAAI;CAWb;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAKpC,QAAQ,CAAC,IAAI,EAAE,SAAS;IAJpC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAE1B,IAAI,EAAE,SAAS;IAUpC,2BAA2B;IAC3B,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,6DAA6D;IAC7D,IAAI,IAAI,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI;IAarC,sGAAsG;IACtG,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI;CAQ1E"}
@@ -0,0 +1,30 @@
1
+ /** A back handler: true when it used the press (closed a dialog, went up a menu), false to let the next one try. */
2
+ export type BackHandler = () => boolean;
3
+ /**
4
+ * Who answers Android's back button (and the back gesture): the most recently pushed handler first, then the ones
5
+ * under it, then `onRoot` when none took it. A dialog pushes "close me" when it opens and pops it when it closes; a
6
+ * pause menu pushes "resume"; the title screen leaves the press to `onRoot`, which by default asks for a second press
7
+ * to quit, so a stray swipe never ends a run.
8
+ */
9
+ export declare class BackStack {
10
+ private readonly onRoot;
11
+ private readonly handlers;
12
+ constructor(onRoot?: () => void);
13
+ /** Puts `handler` on top; the returned function takes it off (from anywhere in the stack). */
14
+ push(handler: BackHandler): () => void;
15
+ get size(): number;
16
+ /** A back press: offered from the top down; `onRoot` when none uses it. True when a handler used it. */
17
+ press(): boolean;
18
+ }
19
+ export interface DoubleBackOptions {
20
+ /** A second press within this (ms) quits (default 2000). */
21
+ withinMs?: number;
22
+ /** The first press: tell the player ("press back again to quit"). */
23
+ notify: () => void;
24
+ /** The second: leave (Capacitor's `App.exitApp`, or `minimizeApp` to keep the game alive in the background). */
25
+ exit: () => void;
26
+ now?: () => number;
27
+ }
28
+ /** An `onRoot` that quits on a second back press soon after the first, and tells the player after the first. */
29
+ export declare function doubleBackToExit(options: DoubleBackOptions): () => void;
30
+ //# sourceMappingURL=backButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backButton.d.ts","sourceRoot":"","sources":["../../../src/mobile/backButton.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC;AAExC;;;;;GAKG;AACH,qBAAa,SAAS;IAGR,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAEjB,MAAM,GAAE,MAAM,IAAsB;IAEjE,8FAA8F;IAC9F,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI;IAQtC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,wGAAwG;IACxG,KAAK,IAAI,OAAO;CAOjB;AAED,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,gHAAgH;IAChH,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,gHAAgH;AAChH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAcvE"}
@@ -0,0 +1,62 @@
1
+ import type { BackStack } from './backButton.js';
2
+ /** What a plugin's `addListener` resolves to. */
3
+ export interface ListenerHandle {
4
+ remove(): Promise<void> | void;
5
+ }
6
+ /** The slice of `@capacitor/app`'s `App` the engine uses. */
7
+ export interface AppLike {
8
+ addListener(event: 'backButton', cb: (e: {
9
+ canGoBack: boolean;
10
+ }) => void): Promise<ListenerHandle> | ListenerHandle;
11
+ exitApp(): Promise<void> | void;
12
+ minimizeApp?(): Promise<void> | void;
13
+ getInfo?(): Promise<{
14
+ version: string;
15
+ build: string;
16
+ }>;
17
+ }
18
+ /**
19
+ * Sends Android's back button to a `BackStack`. Capacitor's own default (the web view going back in its history,
20
+ * then closing the app) is replaced as soon as a listener is added, so the stack decides everything. Resolves to
21
+ * the uninstall.
22
+ */
23
+ export declare function installBackButton(app: AppLike, stack: BackStack): Promise<() => void>;
24
+ /**
25
+ * The app's native build as a number, for `pickWebUpdate`'s `nativeBuild`: Android's versionCode, iOS's
26
+ * CFBundleVersion (its first number, for "508.1"). `Infinity` where there is no native layer (a browser), so every
27
+ * web update fits; 0 when the build cannot be read, so none that needs native code does.
28
+ */
29
+ export declare function nativeBuildOf(app: Pick<AppLike, 'getInfo'> | null | undefined): Promise<number>;
30
+ /** Options for `capacitorConfig`: what differs between games. */
31
+ export interface MobileConfigOptions {
32
+ /** Reverse-domain id, as the stores know it ("com.example.mygame"). */
33
+ appId: string;
34
+ appName: string;
35
+ /** The built web, relative to the project (default 'dist'). */
36
+ webDir?: string;
37
+ /** Behind the web view while it loads, and under the safe-area bands (default black). */
38
+ backgroundColor?: string;
39
+ /** Let Chrome's and Safari's inspectors attach (default false: on for a debug build only). */
40
+ debug?: boolean;
41
+ /** Anything else, laid over the defaults (`plugins`, `server`, …). */
42
+ extra?: Record<string, unknown>;
43
+ }
44
+ /**
45
+ * A `capacitor.config.ts` for a game, with the defaults a game wants rather than a web page's:
46
+ *
47
+ * - the page is served from `https://localhost` (Android) so cookies, `crypto.subtle` and service workers behave as on
48
+ * the web, and nothing is fetched over plain http;
49
+ * - the web view does not scroll, bounce or zoom on iOS, and draws edge to edge (`contentInset: 'never'`, mind the
50
+ * safe area: `safeAreaInsets`);
51
+ * - the splash screen stays until the game hides it (`SplashScreen.hide()` once the first scene draws), so the player
52
+ * never sees a blank web view;
53
+ * - `@capgo/capacitor-updater` does not update on its own: the engine's `pickWebUpdate` decides.
54
+ *
55
+ * ```ts
56
+ * // capacitor.config.ts
57
+ * import { capacitorConfig } from '1681-engine/mobile';
58
+ * export default capacitorConfig({ appId: 'com.example.mygame', appName: 'My Game', debug: process.env.DEBUG === '1' });
59
+ * ```
60
+ */
61
+ export declare function capacitorConfig(options: MobileConfigOptions): Record<string, unknown>;
62
+ //# sourceMappingURL=capacitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capacitor.d.ts","sourceRoot":"","sources":["../../../src/mobile/capacitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQ9C,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAChC;AAED,6DAA6D;AAC7D,MAAM,WAAW,OAAO;IACtB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;IACpH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACrC,OAAO,CAAC,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAK3F;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CASrG;AAED,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBrF"}
@@ -0,0 +1,4 @@
1
+ export { BackStack, doubleBackToExit, type BackHandler, type DoubleBackOptions } from './backButton.js';
2
+ export { installBackButton, nativeBuildOf, capacitorConfig, type AppLike, type ListenerHandle, type MobileConfigOptions } from './capacitor.js';
3
+ export { safeAreaInsets, keepScreenOn, type Insets, type WakeLockLike } from './screen.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mobile/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC7I,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,27 @@
1
+ /** Px kept clear at each edge: the notch, the rounded corners, the home bar. */
2
+ export interface Insets {
3
+ top: number;
4
+ right: number;
5
+ bottom: number;
6
+ left: number;
7
+ }
8
+ /**
9
+ * The safe-area insets as the browser has them now (CSS `env(safe-area-inset-*)`), read through a hidden probe
10
+ * element: what a canvas HUD must keep its buttons out of when the web view is drawn edge to edge. Zeros where
11
+ * there is no document or no notch.
12
+ */
13
+ export declare function safeAreaInsets(doc?: Document | undefined): Insets;
14
+ /** The slice of the Screen Wake Lock API used, so a test can hand in its own. */
15
+ export interface WakeLockLike {
16
+ request(type: 'screen'): Promise<{
17
+ release(): Promise<void>;
18
+ addEventListener?(type: 'release', cb: () => void): void;
19
+ }>;
20
+ }
21
+ /**
22
+ * Keeps the screen on while the game runs (a phone dims and locks during a cut scene or a long think otherwise),
23
+ * through the Screen Wake Lock API. The browser drops the lock whenever the page is hidden, so it is asked for again
24
+ * each time the page comes back. Resolves to the release; does nothing where the API is missing or refuses.
25
+ */
26
+ export declare function keepScreenOn(wakeLock?: WakeLockLike | undefined, doc?: Pick<Document, 'visibilityState' | 'addEventListener' | 'removeEventListener'> | undefined): Promise<() => void>;
27
+ //# sourceMappingURL=screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../../src/mobile/screen.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,QAAQ,GAAG,SAA+B,GAAG,MAAM,CAatF;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,gBAAgB,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1H;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,QAAQ,GAAE,YAAY,GAAG,SAAmG,EAC5H,GAAG,GAAE,IAAI,CAAC,QAAQ,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,qBAAqB,CAAC,GAAG,SAA+B,GACpH,OAAO,CAAC,MAAM,IAAI,CAAC,CA2BrB"}
@@ -0,0 +1,31 @@
1
+ import { type NavGrid, type NavOptions, type TilePoint } from './navGrid.js';
2
+ /**
3
+ * Which way to step from every tile to reach the nearest goal: one search for a whole crowd. A hundred monsters
4
+ * chasing one player cost one flow field a re-plan, where a hundred `findPath`s would cost a hundred searches.
5
+ */
6
+ export interface FlowField {
7
+ readonly width: number;
8
+ readonly height: number;
9
+ /** Walking cost from each tile (row-major) to its nearest goal, `Infinity` where none can be reached. */
10
+ readonly costs: Float32Array;
11
+ /** The step to take from each tile, an index into `NAV_STEPS`; -1 on a goal and where nothing is reached. */
12
+ readonly steps: Int8Array;
13
+ /** The step from tile (tx, ty) as [dx, dy], or null on a goal, a wall, outside the grid or out of reach. */
14
+ stepAt(tx: number, ty: number): readonly [number, number] | null;
15
+ /** The cost from (tx, ty) to its nearest goal (`Infinity` out of reach or outside the grid). */
16
+ costAt(tx: number, ty: number): number;
17
+ }
18
+ /**
19
+ * A flow field towards `goals` (any number: every door out of a burning house), by the same steps, corner rules,
20
+ * costs and end-only tiles `findPath` takes, so a body following it walks the route `findPath` would have found.
21
+ * `maxCost` stops the spread (bodies further off stand still or wander).
22
+ */
23
+ export declare function flowField(g: NavGrid, goals: readonly TilePoint[], options?: NavOptions & {
24
+ maxCost?: number;
25
+ }): FlowField;
26
+ /**
27
+ * The tiles a body at `from` walks by following the field, to the goal (both ends included), or null when `from` is
28
+ * out of reach. For drawing the route, and for tests; a body steps one tile at a time with `stepAt`.
29
+ */
30
+ export declare function followField(field: FlowField, from: TilePoint, maxSteps?: number): TilePoint[] | null;
31
+ //# sourceMappingURL=flowField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowField.d.ts","sourceRoot":"","sources":["../../../src/nav/flowField.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtG;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yGAAyG;IACzG,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,6GAA6G;IAC7G,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,4GAA4G;IAC5G,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACjE,gGAAgG;IAChG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,GAAE,UAAU,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,SAAS,CA0D7H;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,SAA6B,GAAG,SAAS,EAAE,GAAG,IAAI,CAaxH"}
@@ -0,0 +1,47 @@
1
+ export interface SteerVec {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ /** A body as steering sees it. */
6
+ export interface SteerAgent {
7
+ x: number;
8
+ y: number;
9
+ vx: number;
10
+ vy: number;
11
+ }
12
+ /** Full speed straight at a point. */
13
+ export declare function seek(agent: SteerAgent, tx: number, ty: number, maxSpeed: number): SteerVec;
14
+ /** Full speed straight away from a point, within `panic` px of it (default always). */
15
+ export declare function flee(agent: SteerAgent, tx: number, ty: number, maxSpeed: number, panic?: number): SteerVec;
16
+ /** Like `seek`, but slowing inside `slowRadius` px so it stops on the point instead of overshooting. */
17
+ export declare function arrive(agent: SteerAgent, tx: number, ty: number, maxSpeed: number, slowRadius: number): SteerVec;
18
+ /**
19
+ * Away from neighbours closer than `radius` px, harder the closer they are: bodies of a crowd stop stacking on one
20
+ * tile. A neighbour exactly on top is pushed off along a direction picked from the pair's places, so two bodies
21
+ * spawned on one spot still part.
22
+ */
23
+ export declare function separation(agent: SteerAgent, neighbours: Iterable<SteerAgent>, radius: number, strength: number): SteerVec;
24
+ /** Towards the average heading of neighbours within `radius` (a flock turning together). */
25
+ export declare function alignment(agent: SteerAgent, neighbours: Iterable<SteerAgent>, radius: number): SteerVec;
26
+ /** Towards the middle of neighbours within `radius` (a flock keeping together). */
27
+ export declare function cohesion(agent: SteerAgent, neighbours: Iterable<SteerAgent>, radius: number, maxSpeed: number): SteerVec;
28
+ /** What `followFlow` reads: a flow field (`flowField.ts`). */
29
+ export interface StepSource {
30
+ stepAt(tx: number, ty: number): readonly [number, number] | null;
31
+ costAt(tx: number, ty: number): number;
32
+ }
33
+ /**
34
+ * Full speed along a flow field from the tile the body stands on. On a goal it brakes to a stop, so a crowd gathers
35
+ * there rather than coasting past. Pushed off the field (into a wall, off the grid, by a crowd's shoving), it heads
36
+ * for the cheapest tile around it that is on the field, so it is never stranded; with none within two tiles, it
37
+ * brakes.
38
+ */
39
+ export declare function followFlow(agent: SteerAgent, field: StepSource, tileSize: number, maxSpeed: number): SteerVec;
40
+ /** Weighs steering forces, sums them and caps the sum at `maxForce` (px/s of change a step). */
41
+ export declare function combineSteering(maxForce: number, ...weighted: [SteerVec, number][]): SteerVec;
42
+ /**
43
+ * One step of steering applied to a body: velocity changed by `force × dt`, capped at `maxSpeed`, position moved.
44
+ * For bodies with no physics of their own (birds, a crowd of extras); a physics body takes the force instead.
45
+ */
46
+ export declare function applySteering(agent: SteerAgent, force: SteerVec, maxSpeed: number, dt: number): void;
47
+ //# sourceMappingURL=steering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steering.d.ts","sourceRoot":"","sources":["../../../src/nav/steering.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,kCAAkC;AAClC,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAUD,sCAAsC;AACtC,wBAAgB,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAE1F;AAED,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAW,GAAG,QAAQ,CAK5G;AAED,wGAAwG;AACxG,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAMhH;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CA0B1H;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,CAWvG;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAWxH;AAED,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACjE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CACxC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAuB7G;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,GAAG,QAAQ,CAa7F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAUpG"}
@@ -26,6 +26,11 @@ export declare class InterpolationBuffer {
26
26
  delayMs?: number, maxExtrapolateMs?: number, keep?: number);
27
27
  /** Records every body of a snapshot taken at `time`. */
28
28
  push(time: number, updates: readonly EntityUpdate[]): void;
29
+ /**
30
+ * The server time bodies are drawn at when it is `now` (`serverNow()`): what a shot should carry, so a server with
31
+ * lag compensation judges it against the picture the player aimed at.
32
+ */
33
+ viewTime(now: number): number;
29
34
  /** Forgets a body (it left view). */
30
35
  remove(id: number): void;
31
36
  has(id: number): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"InterpolationBuffer.d.ts","sourceRoot":"","sources":["../../../src/net/InterpolationBuffer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,MAAM,WAAW,iBAAiB;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAI5B,yGAAyG;IACzG,QAAQ,CAAC,OAAO;IAChB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;;IAGrD,yGAAyG;IAChG,OAAO,SAAM,EACL,gBAAgB,SAAM,EACtB,IAAI,SAAK;IAG5B,wDAAwD;IACxD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAe1D,qCAAqC;IACrC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIxB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,GAAG,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAI/B,+FAA+F;IAC/F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;CAqB1D"}
1
+ {"version":3,"file":"InterpolationBuffer.d.ts","sourceRoot":"","sources":["../../../src/net/InterpolationBuffer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,MAAM,WAAW,iBAAiB;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAI5B,yGAAyG;IACzG,QAAQ,CAAC,OAAO;IAChB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;;IAGrD,yGAAyG;IAChG,OAAO,SAAM,EACL,gBAAgB,SAAM,EACtB,IAAI,SAAK;IAG5B,wDAAwD;IACxD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAe1D;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI7B,qCAAqC;IACrC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIxB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,GAAG,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAI/B,+FAA+F;IAC/F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;CAqB1D"}
@@ -0,0 +1,70 @@
1
+ /** A body's position at one recorded moment. */
2
+ export interface RewoundBody {
3
+ id: number;
4
+ x: number;
5
+ y: number;
6
+ }
7
+ /** A body a ray passed through: `along` is how far from the ray's start it was met (px). */
8
+ export interface RewoundHit extends RewoundBody {
9
+ along: number;
10
+ }
11
+ /**
12
+ * Lag compensation for a server: where every body was a moment ago, so a shot is judged against what the shooter saw.
13
+ * A client draws other bodies `delayMs` in the past between two snapshots (`InterpolationBuffer`); the server records
14
+ * the same positions at the same times, when each snapshot goes out, and blends between them the same way, so a
15
+ * rewind lands on the very picture the shooter aimed at, not on where the target has walked to since.
16
+ *
17
+ * How far back a client may ask is capped (`clampTime`): a client that claims an old time to shoot round a corner
18
+ * gets no further back than `maxRewindMs`.
19
+ *
20
+ * ```ts
21
+ * // when snapshots go out (RoomHost does this for a Room made with `rewindMs`)
22
+ * lag.record(now, players);
23
+ * // a client's shot, sent with `at: buffer.viewTime(serverNow())`
24
+ * const at = LagCompensator.clampTime(msg.at, now, 250);
25
+ * const hit = lag.alongRay(at, x, y, dx, dy, range, 6, shooterId)[0];
26
+ * ```
27
+ */
28
+ export declare class LagCompensator {
29
+ readonly keepMs: number;
30
+ private readonly frames;
31
+ /** `keepMs`: how much history is kept; at least the longest rewind allowed plus a snapshot interval. */
32
+ constructor(keepMs?: number);
33
+ /** The latest recorded time, or -Infinity before any. */
34
+ get newest(): number;
35
+ get oldest(): number;
36
+ /** Records every body's position at `time`. A time not after the last one recorded replaces that frame. */
37
+ record(time: number, bodies: Iterable<{
38
+ readonly id: number;
39
+ readonly x: number;
40
+ readonly y: number;
41
+ }>): void;
42
+ /** Forgets a body everywhere in the history (it left the game, not just the view). */
43
+ forget(id: number): void;
44
+ clear(): void;
45
+ /**
46
+ * A time a client claims it saw, kept within `[now - maxRewindMs, now]`: the window a rewind is honoured in. A
47
+ * claim that is not a finite number is taken as `now` (no rewind).
48
+ */
49
+ static clampTime(claimed: unknown, now: number, maxRewindMs: number): number;
50
+ /**
51
+ * Where body `id` was at `time`, blended between the frames around it as `InterpolationBuffer` blends; null when
52
+ * the history has no frame of it. Before the oldest frame it is where it was then; after the newest, where it is.
53
+ */
54
+ at(id: number, time: number): {
55
+ x: number;
56
+ y: number;
57
+ } | null;
58
+ /** Every body in the history at `time`. */
59
+ all(time: number): RewoundBody[];
60
+ /** The bodies whose centre was within `radius` of (x, y) at `time`, nearest first; `except` (the shooter) left out. */
61
+ near(time: number, x: number, y: number, radius: number, except?: number): RewoundBody[];
62
+ /**
63
+ * A hitscan at `time`: the bodies a ray from (x, y) along (dx, dy) passed within `radius` of, before `range` px, in
64
+ * the order it met them. `except` (the shooter) is left out. Walls are the game's to check against the first hit.
65
+ */
66
+ alongRay(time: number, x: number, y: number, dx: number, dy: number, range: number, radius: number, except?: number): RewoundHit[];
67
+ /** The two frames around `time` and how far between them it is (0 at the first, 1 at the second). */
68
+ private around;
69
+ }
70
+ //# sourceMappingURL=LagCompensator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LagCompensator.d.ts","sourceRoot":"","sources":["../../../src/net/LagCompensator.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,4FAA4F;AAC5F,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAc;IAIb,QAAQ,CAAC,MAAM;IAH3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IAEtC,wGAAwG;gBACnF,MAAM,SAAO;IAElC,yDAAyD;IACzD,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,2GAA2G;IAC3G,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAY7G,sFAAsF;IACtF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIxB,KAAK,IAAI,IAAI;IAIb;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;IAK5E;;;OAGG;IACH,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAM7D,2CAA2C;IAC3C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE;IAahC,uHAAuH;IACvH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE;IAKxF;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAqBlI,qGAAqG;IACrG,OAAO,CAAC,MAAM;CAaf"}
@@ -1,7 +1,9 @@
1
1
  import type { InputStep, MovementParams } from './constants.js';
2
+ import { LagCompensator } from './LagCompensator.js';
2
3
  import { type CollisionGrid, type MoverState } from './movement.js';
3
4
  import { type Snapshot } from './protocol.js';
4
5
  export interface RoomPlayer<D = unknown> {
6
+ readonly kind: 'player';
5
7
  readonly id: number;
6
8
  readonly mover: MoverState;
7
9
  /** The game's own data for this player (sent to others in `sp` when they first see it). */
@@ -19,7 +21,39 @@ export interface RoomPlayer<D = unknown> {
19
21
  /** Refused inputs (nonsense numbers, a flood): a game may kick past some count. */
20
22
  strikes: number;
21
23
  }
22
- export interface RoomOptions<D> {
24
+ /**
25
+ * A body the server moves itself (a monster, a projectile, a crate): no input, no prediction, sent to players beside
26
+ * them in the same snapshots. The game writes `x`, `y` and the rest as its own simulation runs.
27
+ */
28
+ export interface RoomBody<B = unknown> {
29
+ readonly kind: 'body';
30
+ readonly id: number;
31
+ x: number;
32
+ y: number;
33
+ aim: number;
34
+ anim: number;
35
+ hp: number;
36
+ flags: number;
37
+ /** The game's own description of it, sent in `sp` when a player first sees it. */
38
+ data: B;
39
+ }
40
+ /** Anything a snapshot carries: a player or a body the server moves. */
41
+ export type RoomSeen<D = unknown, B = unknown> = RoomPlayer<D> | RoomBody<B>;
42
+ /** Server bodies get ids from here up unless given one, far above the ids `RoomHost` hands players. */
43
+ export declare const BODY_ID_BASE: number;
44
+ /** Who a player is sent: the bodies near it, not the whole room. */
45
+ export interface RoomInterest<D, B = unknown> {
46
+ /** A body comes into view within this many px of the player (centre to centre). */
47
+ radius: number;
48
+ /**
49
+ * And leaves only past `radius + leaveMargin` (default a quarter of `radius`), so a body walking along the edge is
50
+ * not spawned and removed on every snapshot.
51
+ */
52
+ leaveMargin?: number;
53
+ /** A further test, for what distance alone does not decide (a team's own always, a hidden enemy never). */
54
+ canSee?: (viewer: RoomPlayer<D>, other: RoomSeen<D, B>) => boolean;
55
+ }
56
+ export interface RoomOptions<D, B = unknown> {
23
57
  grid: CollisionGrid;
24
58
  /** Movement rules a new player starts with. */
25
59
  params: MovementParams;
@@ -35,6 +69,13 @@ export interface RoomOptions<D> {
35
69
  backlog?: number;
36
70
  /** Called after each step a player takes (set aim, anim, flags from it). */
37
71
  onStep?: (player: RoomPlayer<D>, step: InputStep) => void;
72
+ /** Send each player only the bodies near it (default: everyone to everyone). */
73
+ interest?: RoomInterest<D, B>;
74
+ /**
75
+ * Keep this many ms of positions for lag compensation (`history`), recorded as each round of snapshots goes out
76
+ * (`record`). Off by default.
77
+ */
78
+ rewindMs?: number;
38
79
  }
39
80
  /**
40
81
  * An authoritative room for a server: players send input steps, the room applies them with the same `stepMover` the
@@ -44,17 +85,38 @@ export interface RoomOptions<D> {
44
85
  * Every step a player sends is applied once, in order; a client cannot move faster by sending more (the queue is
45
86
  * capped and drained at most `maxStepsPerTick` a tick), and nonsense input is refused and counted.
46
87
  */
47
- export declare class Room<D = unknown> {
88
+ export declare class Room<D = unknown, B = unknown> {
48
89
  private readonly options;
49
90
  readonly players: Map<number, RoomPlayer<D>>;
91
+ /** The bodies the server moves (`addBody`). */
92
+ readonly bodies: Map<number, RoomBody<B>>;
50
93
  tick: number;
94
+ /** Past positions for judging shots against what a client saw, when the room was made with `rewindMs`. */
95
+ readonly history: LagCompensator | null;
96
+ /** How far back a client's claimed view time is honoured (`rewindMs`, 0 when off). */
97
+ readonly rewindMs: number;
51
98
  private readonly known;
99
+ private readonly hash;
100
+ private readonly nearby;
52
101
  private readonly maxQueue;
53
102
  private readonly maxSteps;
54
103
  private readonly backlog;
55
- constructor(options: RoomOptions<D>);
104
+ private nextBodyId;
105
+ constructor(options: RoomOptions<D, B>);
56
106
  join(id: number, data: D): RoomPlayer<D>;
57
107
  leave(id: number): void;
108
+ /**
109
+ * Records where every body is now, at server time `time`, into `history`; `RoomHost` calls it as each round of
110
+ * snapshots goes out, so the history holds exactly the positions clients interpolate between.
111
+ */
112
+ record(time: number): void;
113
+ /**
114
+ * Adds a body the server moves, at (x, y), with the game's `data` (what players are told it looks like). Its id
115
+ * is `id`, or the next from `BODY_ID_BASE` up; an id a player or body has is refused.
116
+ */
117
+ addBody(data: B, x: number, y: number, id?: number): RoomBody<B>;
118
+ /** Takes a body out: players who saw it get it in `rm` with their next snapshot. */
119
+ removeBody(id: number): void;
58
120
  /**
59
121
  * Input steps `seq`, `seq + 1`, … from a player. Steps already received are skipped (a resend), any step with
60
122
  * numbers no client sends refuses the whole message, and steps past a full queue are dropped. Returns false when
@@ -63,7 +125,14 @@ export declare class Room<D = unknown> {
63
125
  input(id: number, seq: number, steps: readonly InputStep[]): boolean;
64
126
  /** One simulation step for everyone. */
65
127
  step(): void;
128
+ /**
129
+ * Moves a player by hand (a teleport, a respawn), and files it in the interest grid at once. Writing `mover.x/y`
130
+ * directly also works; the room then finds it at its new place from the next step.
131
+ */
132
+ place(id: number, x: number, y: number): void;
66
133
  /** The snapshot player `id` should get now. `time` is the server's wall clock (ms). */
67
134
  snapshotFor(id: number, time: number): Snapshot | null;
135
+ /** What `me` is sent: itself always; with interest, the rest in range (a known one until past the margin). */
136
+ private visibleTo;
68
137
  }
69
138
  //# sourceMappingURL=Room.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Room.d.ts","sourceRoot":"","sources":["../../../src/net/Room.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAA0B,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,2FAA2F;IAC3F,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,cAAc,CAAC;IACvB,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;IAC5B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,aAAa,CAAC;IACpB,+CAA+C;IAC/C,MAAM,EAAE,cAAc,CAAC;IACvB,uCAAuC;IACvC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,iGAAiG;IACjG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qHAAqH;IACrH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;;;GAOG;AACH,qBAAa,IAAI,CAAC,CAAC,GAAG,OAAO;IAQf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,QAAQ,CAAC,OAAO,6BAAoC;IACpD,IAAI,SAAK;IACT,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAEJ,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAMpD,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAqBxC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKvB;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO;IAkCpE,wCAAwC;IACxC,IAAI,IAAI,IAAI;IAeZ,uFAAuF;IACvF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;CA2BvD"}
1
+ {"version":3,"file":"Room.d.ts","sourceRoot":"","sources":["../../../src/net/Room.ts"],"names":[],"mappings":"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"}
@@ -39,8 +39,8 @@ export interface RoomHostOptions<D> {
39
39
  * host.start();
40
40
  * ```
41
41
  */
42
- export declare class RoomHost<D = unknown> {
43
- readonly room: Room<D>;
42
+ export declare class RoomHost<D = unknown, B = unknown> {
43
+ readonly room: Room<D, B>;
44
44
  private readonly options;
45
45
  private readonly sockets;
46
46
  private readonly joined;
@@ -51,7 +51,7 @@ export declare class RoomHost<D = unknown> {
51
51
  private readonly stepMs;
52
52
  private readonly snapEvery;
53
53
  private readonly now;
54
- constructor(room: Room<D>, options?: RoomHostOptions<D>);
54
+ constructor(room: Room<D, B>, options?: RoomHostOptions<D>);
55
55
  /** A socket connected; returns the id its messages and its close are reported under. */
56
56
  connect(socket: HostSocket): number;
57
57
  /** A message arrived from socket `id` (text as a string, binary as bytes). */
@@ -68,6 +68,11 @@ export declare class RoomHost<D = unknown> {
68
68
  stop(): void;
69
69
  /** One step, and the snapshots when they are due. Exposed for tests and for servers that drive their own loop. */
70
70
  stepOnce(): void;
71
+ /**
72
+ * The server time a client's claimed view time (`InterpolationBuffer.viewTime`, sent with a shot) is judged at:
73
+ * kept within the room's `rewindMs` of now, so no claim reaches further back. `now` with no history kept.
74
+ */
75
+ rewindTime(claimed: unknown): number;
71
76
  private strike;
72
77
  private checkStrikes;
73
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RoomHost.d.ts","sourceRoot":"","sources":["../../../src/net/RoomHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAiB,aAAa,EAAY,MAAM,YAAY,CAAC;AAEzE,uGAAuG;AACvG,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kGAAkG;IAClG,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,CAAC,CAAC;IACzC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ,CAAC,CAAC,GAAG,OAAO;IAY7B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAGxB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EACL,OAAO,GAAE,eAAe,CAAC,CAAC,CAAM;IAOnD,wFAAwF;IACxF,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAMnC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI;IAyClE,0BAA0B;IAC1B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQ5B,wDAAwD;IACxD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI;IAOlF,0GAA0G;IAC1G,KAAK,IAAI,IAAI;IAgBb,IAAI,IAAI,IAAI;IAKZ,kHAAkH;IAClH,QAAQ,IAAI,IAAI;IAUhB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,YAAY;CAOrB"}
1
+ {"version":3,"file":"RoomHost.d.ts","sourceRoot":"","sources":["../../../src/net/RoomHost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAiB,aAAa,EAAY,MAAM,YAAY,CAAC;AAEzE,uGAAuG;AACvG,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kGAAkG;IAClG,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,CAAC,CAAC;IACzC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IAY1C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAGxB,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EACR,OAAO,GAAE,eAAe,CAAC,CAAC,CAAM;IAOnD,wFAAwF;IACxF,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAMnC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI;IAyClE,0BAA0B;IAC1B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQ5B,wDAAwD;IACxD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI;IAOlF,0GAA0G;IAC1G,KAAK,IAAI,IAAI;IAgBb,IAAI,IAAI,IAAI;IAKZ,kHAAkH;IAClH,QAAQ,IAAI,IAAI;IAYhB;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM;IAIpC,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,YAAY;CAOrB"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Points sorted into square cells, for asking who is near a spot without looking at everyone: a room's interest
3
+ * management, a server's hit test. Moving a point within its cell costs a write; across cells, a set delete and add.
4
+ * Pure and transport-free, so the same class serves the client.
5
+ */
6
+ export declare class SpatialHash {
7
+ readonly cellSize: number;
8
+ private readonly cells;
9
+ private readonly entries;
10
+ /** `cellSize`: world px per cell. About the radius queries ask for keeps a query to a 3×3 block. */
11
+ constructor(cellSize: number);
12
+ get size(): number;
13
+ has(id: number): boolean;
14
+ /** Puts `id` at (x, y), or moves it there. */
15
+ set(id: number, x: number, y: number): void;
16
+ delete(id: number): void;
17
+ clear(): void;
18
+ /** The ids within `radius` of (x, y), edge included, pushed onto `out` in no set order. */
19
+ near(x: number, y: number, radius: number, out?: number[]): number[];
20
+ /** The ids inside the box from (x0, y0) to (x1, y1), edges included. */
21
+ inRect(x0: number, y0: number, x1: number, y1: number, out?: number[]): number[];
22
+ private visit;
23
+ private unlink;
24
+ }
25
+ //# sourceMappingURL=SpatialHash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpatialHash.d.ts","sourceRoot":"","sources":["../../../src/net/SpatialHash.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,qBAAa,WAAW;IAKV,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAJrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IAEpD,oGAAoG;gBAC/E,QAAQ,EAAE,MAAM;IAIrC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAe3C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAOxB,KAAK,IAAI,IAAI;IAKb,2FAA2F;IAC3F,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IAUxE,wEAAwE;IACxE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IAOpF,OAAO,CAAC,KAAK;IAkBb,OAAO,CAAC,MAAM;CAMf"}
@@ -1 +1 @@
1
- {"version":3,"file":"ScrollView.d.ts","sourceRoot":"","sources":["../../../src/ui/ScrollView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA8F,MAAM,SAAS,CAAC;AAEhI,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,MAAO,YAAW,SAAS;IAmB7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAlBpC,QAAQ,CAAC,OAAO,8CAAmB;IACnC,uGAAuG;IACvG,cAAc,UAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkD;IACzE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;gBAEhB,OAAO,EAAE,iBAAiB;IAsBvD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,6EAA6E;IAC7E,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAOjC,kEAAkE;IAClE,OAAO,IAAI,IAAI;IAKf,yDAAyD;IACzD,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAQ5B,6FAA6F;IAC7F,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAO9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAQlB,OAAO,IAAI,IAAI;IAKxB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,OAAO;IAef,SAAS,CAAC,MAAM,IAAI,IAAI;CA0BzB"}
1
+ {"version":3,"file":"ScrollView.d.ts","sourceRoot":"","sources":["../../../src/ui/ScrollView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA8F,MAAM,SAAS,CAAC;AAEhI,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,MAAO,YAAW,SAAS;IAmB7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAlBpC,QAAQ,CAAC,OAAO,8CAAmB;IACnC,uGAAuG;IACvG,cAAc,UAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkD;IACzE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;gBAEhB,OAAO,EAAE,iBAAiB;IAsBvD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,6EAA6E;IAC7E,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAOjC,kEAAkE;IAClE,OAAO,IAAI,IAAI;IAUf,yDAAyD;IACzD,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAQ5B,6FAA6F;IAC7F,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAO9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAQlB,OAAO,IAAI,IAAI;IAKxB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,OAAO;IAef,SAAS,CAAC,MAAM,IAAI,IAAI;CA0BzB"}