1681-engine 0.5.9 → 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 (210) hide show
  1. package/CHANGELOG.md +92 -1
  2. package/README.md +15 -6
  3. package/dist/1681-engine.js +588 -465
  4. package/dist/ai/behaviorTree.js +198 -0
  5. package/dist/ai/buildTree.js +54 -0
  6. package/dist/ambient/life/Birds.js +73 -0
  7. package/dist/ambient/life/Butterflies.js +43 -0
  8. package/dist/ambient/life/ChimneySmoke.js +50 -0
  9. package/dist/ambient/life/Drift.js +75 -0
  10. package/dist/ambient/life/Fireflies.js +52 -0
  11. package/dist/ambient/life/lifeMotion.js +49 -0
  12. package/dist/assets/AssetLoader.js +115 -70
  13. package/dist/assets/Prepainter.js +61 -0
  14. package/dist/assets/workerPainter.js +39 -0
  15. package/dist/audio/spatial.js +118 -0
  16. package/dist/cjs/electron/electronLike.d.ts +55 -0
  17. package/dist/cjs/electron/electronLike.js +7 -0
  18. package/dist/cjs/electron/index.d.ts +14 -0
  19. package/dist/cjs/electron/index.js +20 -0
  20. package/dist/cjs/electron/lockdown.d.ts +33 -0
  21. package/dist/cjs/electron/lockdown.js +69 -0
  22. package/dist/cjs/electron/lockdownRules.d.ts +28 -0
  23. package/dist/cjs/electron/lockdownRules.js +61 -0
  24. package/dist/cjs/electron/sessionStore.d.ts +40 -0
  25. package/dist/cjs/electron/sessionStore.js +77 -0
  26. package/dist/cjs/electron/shellId.d.ts +22 -0
  27. package/dist/cjs/electron/shellId.js +31 -0
  28. package/dist/cjs/electron/webFiles.d.ts +13 -0
  29. package/dist/cjs/electron/webFiles.js +52 -0
  30. package/dist/cjs/electron/webPatcher.d.ts +83 -0
  31. package/dist/cjs/electron/webPatcher.js +223 -0
  32. package/dist/cjs/electron/webPointers.d.ts +19 -0
  33. package/dist/cjs/electron/webPointers.js +35 -0
  34. package/dist/cjs/package.json +1 -0
  35. package/dist/cjs/updates/index.d.ts +8 -0
  36. package/dist/cjs/updates/index.js +21 -0
  37. package/dist/cjs/updates/manifest.d.ts +73 -0
  38. package/dist/cjs/updates/manifest.js +67 -0
  39. package/dist/cjs/updates/pickUpdate.d.ts +42 -0
  40. package/dist/cjs/updates/pickUpdate.js +52 -0
  41. package/dist/cjs/updates/webPlan.d.ts +20 -0
  42. package/dist/cjs/updates/webPlan.js +34 -0
  43. package/dist/dialogue/DialogueRunner.js +173 -0
  44. package/dist/dialogue/expr.js +137 -0
  45. package/dist/dialogue/validateDialogue.js +52 -0
  46. package/dist/ecs/prefab.js +94 -0
  47. package/dist/electron/lockdown.js +31 -0
  48. package/dist/electron/lockdownRules.js +42 -0
  49. package/dist/electron/sessionStore.js +48 -0
  50. package/dist/electron/shellId.js +18 -0
  51. package/dist/electron/webFiles.js +36 -0
  52. package/dist/electron/webPatcher.js +153 -0
  53. package/dist/electron/webPointers.js +25 -0
  54. package/dist/electron.js +19 -0
  55. package/dist/input/Actions.js +58 -27
  56. package/dist/input/InputCapture.js +98 -0
  57. package/dist/input/Keymap.js +32 -26
  58. package/dist/input/haptics.js +50 -0
  59. package/dist/input/inputTape.js +68 -0
  60. package/dist/mobile/backButton.js +40 -0
  61. package/dist/mobile/capacitor.js +48 -0
  62. package/dist/mobile/screen.js +31 -0
  63. package/dist/mobile.js +12 -0
  64. package/dist/nav/flowField.js +58 -0
  65. package/dist/nav/steering.js +77 -0
  66. package/dist/net/ApiClient.js +96 -0
  67. package/dist/net/InterpolationBuffer.js +30 -23
  68. package/dist/net/LagCompensator.js +100 -0
  69. package/dist/net/Room.js +122 -43
  70. package/dist/net/RoomHost.js +13 -4
  71. package/dist/net/SpatialHash.js +66 -0
  72. package/dist/pixel/lifeArt.js +83 -0
  73. package/dist/pixel.js +63 -58
  74. package/dist/react/ConfirmButton.js +24 -0
  75. package/dist/react/baseCss.js +1 -0
  76. package/dist/react/labels.js +7 -6
  77. package/dist/react/useJsonSelector.js +13 -0
  78. package/dist/react.js +73 -68
  79. package/dist/tilemap/TilemapView.js +57 -32
  80. package/dist/tilemap/autotile.js +103 -0
  81. package/dist/tilemap/collision.js +29 -18
  82. package/dist/tilemap/ldtk.js +21 -0
  83. package/dist/tilemap/ldtkParse.js +191 -0
  84. package/dist/types/ai/behaviorTree.d.ts +133 -0
  85. package/dist/types/ai/behaviorTree.d.ts.map +1 -0
  86. package/dist/types/ai/buildTree.d.ts +55 -0
  87. package/dist/types/ai/buildTree.d.ts.map +1 -0
  88. package/dist/types/ambient/life/Birds.d.ts +50 -0
  89. package/dist/types/ambient/life/Birds.d.ts.map +1 -0
  90. package/dist/types/ambient/life/Butterflies.d.ts +32 -0
  91. package/dist/types/ambient/life/Butterflies.d.ts.map +1 -0
  92. package/dist/types/ambient/life/ChimneySmoke.d.ts +37 -0
  93. package/dist/types/ambient/life/ChimneySmoke.d.ts.map +1 -0
  94. package/dist/types/ambient/life/Drift.d.ts +58 -0
  95. package/dist/types/ambient/life/Drift.d.ts.map +1 -0
  96. package/dist/types/ambient/life/Fireflies.d.ts +43 -0
  97. package/dist/types/ambient/life/Fireflies.d.ts.map +1 -0
  98. package/dist/types/ambient/life/lifeMotion.d.ts +63 -0
  99. package/dist/types/ambient/life/lifeMotion.d.ts.map +1 -0
  100. package/dist/types/assets/AssetLoader.d.ts +20 -0
  101. package/dist/types/assets/AssetLoader.d.ts.map +1 -1
  102. package/dist/types/assets/Prepainter.d.ts +64 -0
  103. package/dist/types/assets/Prepainter.d.ts.map +1 -0
  104. package/dist/types/assets/workerPainter.d.ts +21 -0
  105. package/dist/types/assets/workerPainter.d.ts.map +1 -0
  106. package/dist/types/audio/spatial.d.ts +107 -0
  107. package/dist/types/audio/spatial.d.ts.map +1 -0
  108. package/dist/types/dialogue/DialogueRunner.d.ts +78 -0
  109. package/dist/types/dialogue/DialogueRunner.d.ts.map +1 -0
  110. package/dist/types/dialogue/expr.d.ts +12 -0
  111. package/dist/types/dialogue/expr.d.ts.map +1 -0
  112. package/dist/types/dialogue/script.d.ts +78 -0
  113. package/dist/types/dialogue/script.d.ts.map +1 -0
  114. package/dist/types/dialogue/validateDialogue.d.ts +21 -0
  115. package/dist/types/dialogue/validateDialogue.d.ts.map +1 -0
  116. package/dist/types/ecs/prefab.d.ts +84 -0
  117. package/dist/types/ecs/prefab.d.ts.map +1 -0
  118. package/dist/types/electron/electronLike.d.ts +56 -0
  119. package/dist/types/electron/electronLike.d.ts.map +1 -0
  120. package/dist/types/electron/index.d.ts +15 -0
  121. package/dist/types/electron/index.d.ts.map +1 -0
  122. package/dist/types/electron/lockdown.d.ts +34 -0
  123. package/dist/types/electron/lockdown.d.ts.map +1 -0
  124. package/dist/types/electron/lockdownRules.d.ts +29 -0
  125. package/dist/types/electron/lockdownRules.d.ts.map +1 -0
  126. package/dist/types/electron/sessionStore.d.ts +41 -0
  127. package/dist/types/electron/sessionStore.d.ts.map +1 -0
  128. package/dist/types/electron/shellId.d.ts +23 -0
  129. package/dist/types/electron/shellId.d.ts.map +1 -0
  130. package/dist/types/electron/webFiles.d.ts +14 -0
  131. package/dist/types/electron/webFiles.d.ts.map +1 -0
  132. package/dist/types/electron/webPatcher.d.ts +84 -0
  133. package/dist/types/electron/webPatcher.d.ts.map +1 -0
  134. package/dist/types/electron/webPointers.d.ts +20 -0
  135. package/dist/types/electron/webPointers.d.ts.map +1 -0
  136. package/dist/types/index.d.ts +30 -3
  137. package/dist/types/index.d.ts.map +1 -1
  138. package/dist/types/input/Actions.d.ts +14 -0
  139. package/dist/types/input/Actions.d.ts.map +1 -1
  140. package/dist/types/input/InputCapture.d.ts +53 -0
  141. package/dist/types/input/InputCapture.d.ts.map +1 -0
  142. package/dist/types/input/Keymap.d.ts +12 -6
  143. package/dist/types/input/Keymap.d.ts.map +1 -1
  144. package/dist/types/input/haptics.d.ts +42 -0
  145. package/dist/types/input/haptics.d.ts.map +1 -0
  146. package/dist/types/input/inputTape.d.ts +66 -0
  147. package/dist/types/input/inputTape.d.ts.map +1 -0
  148. package/dist/types/mobile/backButton.d.ts +30 -0
  149. package/dist/types/mobile/backButton.d.ts.map +1 -0
  150. package/dist/types/mobile/capacitor.d.ts +62 -0
  151. package/dist/types/mobile/capacitor.d.ts.map +1 -0
  152. package/dist/types/mobile/index.d.ts +4 -0
  153. package/dist/types/mobile/index.d.ts.map +1 -0
  154. package/dist/types/mobile/screen.d.ts +27 -0
  155. package/dist/types/mobile/screen.d.ts.map +1 -0
  156. package/dist/types/nav/flowField.d.ts +31 -0
  157. package/dist/types/nav/flowField.d.ts.map +1 -0
  158. package/dist/types/nav/steering.d.ts +47 -0
  159. package/dist/types/nav/steering.d.ts.map +1 -0
  160. package/dist/types/net/ApiClient.d.ts +74 -0
  161. package/dist/types/net/ApiClient.d.ts.map +1 -0
  162. package/dist/types/net/InterpolationBuffer.d.ts +5 -0
  163. package/dist/types/net/InterpolationBuffer.d.ts.map +1 -1
  164. package/dist/types/net/LagCompensator.d.ts +70 -0
  165. package/dist/types/net/LagCompensator.d.ts.map +1 -0
  166. package/dist/types/net/Room.d.ts +72 -3
  167. package/dist/types/net/Room.d.ts.map +1 -1
  168. package/dist/types/net/RoomHost.d.ts +8 -3
  169. package/dist/types/net/RoomHost.d.ts.map +1 -1
  170. package/dist/types/net/SpatialHash.d.ts +25 -0
  171. package/dist/types/net/SpatialHash.d.ts.map +1 -0
  172. package/dist/types/pixel/index.d.ts +1 -0
  173. package/dist/types/pixel/index.d.ts.map +1 -1
  174. package/dist/types/pixel/lifeArt.d.ts +63 -0
  175. package/dist/types/pixel/lifeArt.d.ts.map +1 -0
  176. package/dist/types/react/ConfirmButton.d.ts +26 -0
  177. package/dist/types/react/ConfirmButton.d.ts.map +1 -0
  178. package/dist/types/react/baseCss.d.ts +1 -1
  179. package/dist/types/react/baseCss.d.ts.map +1 -1
  180. package/dist/types/react/index.d.ts +2 -0
  181. package/dist/types/react/index.d.ts.map +1 -1
  182. package/dist/types/react/labels.d.ts +2 -0
  183. package/dist/types/react/labels.d.ts.map +1 -1
  184. package/dist/types/react/useJsonSelector.d.ts +13 -0
  185. package/dist/types/react/useJsonSelector.d.ts.map +1 -0
  186. package/dist/types/tilemap/TilemapView.d.ts +17 -0
  187. package/dist/types/tilemap/TilemapView.d.ts.map +1 -1
  188. package/dist/types/tilemap/autotile.d.ts +95 -0
  189. package/dist/types/tilemap/autotile.d.ts.map +1 -0
  190. package/dist/types/tilemap/collision.d.ts +8 -0
  191. package/dist/types/tilemap/collision.d.ts.map +1 -1
  192. package/dist/types/tilemap/ldtk.d.ts +97 -0
  193. package/dist/types/tilemap/ldtk.d.ts.map +1 -0
  194. package/dist/types/tilemap/ldtkParse.d.ts +26 -0
  195. package/dist/types/tilemap/ldtkParse.d.ts.map +1 -0
  196. package/dist/types/ui/ScrollView.d.ts.map +1 -1
  197. package/dist/types/updates/index.d.ts +9 -0
  198. package/dist/types/updates/index.d.ts.map +1 -0
  199. package/dist/types/updates/manifest.d.ts +74 -0
  200. package/dist/types/updates/manifest.d.ts.map +1 -0
  201. package/dist/types/updates/pickUpdate.d.ts +43 -0
  202. package/dist/types/updates/pickUpdate.d.ts.map +1 -0
  203. package/dist/types/updates/webPlan.d.ts +21 -0
  204. package/dist/types/updates/webPlan.d.ts.map +1 -0
  205. package/dist/ui/ScrollView.js +10 -8
  206. package/dist/updates/manifest.js +35 -0
  207. package/dist/updates/pickUpdate.js +27 -0
  208. package/dist/updates/webPlan.js +14 -0
  209. package/dist/updates.js +15 -0
  210. package/package.json +35 -3
@@ -0,0 +1,42 @@
1
+ /**
2
+ * A buzz on a phone, per kind of moment (a crit landed, a blow taken, a level gained), rate-limited per kind: a cleave
3
+ * clipping six bodies is one buzz, a damage-over-time one buzz, not a stutter. How it buzzes is injected: the default
4
+ * is the Vibration API (`navigator.vibrate`, Android browsers; iOS Safari has none, so it is a silent no-op there), and
5
+ * a native shell hands in its own plugin's call instead.
6
+ */
7
+ /** The shortest gap (ms) between two buzzes of each kind; a kind not named is never held back. */
8
+ export type HapticGaps<K extends string> = Readonly<Partial<Record<K, number>>>;
9
+ /** One buzz per kind per its gap. Pure, so the rule is tested. */
10
+ export declare class HapticGate<K extends string> {
11
+ private readonly gaps;
12
+ private readonly last;
13
+ constructor(gaps?: HapticGaps<K>);
14
+ /** Whether a buzz of `kind` at `now` (ms) may go; one that may is counted. */
15
+ allow(kind: K, now: number): boolean;
16
+ /** Forgets every kind's last buzz. */
17
+ reset(): void;
18
+ }
19
+ export interface HapticsOptions<K extends string> {
20
+ gaps?: HapticGaps<K>;
21
+ /** Plays a buzz. A promise it returns may reject: that is swallowed, a buzz is never worth an error. */
22
+ play?: (kind: K) => unknown;
23
+ /** The Vibration API pattern per kind for the default `play` (ms, or on/off ms; default 15). */
24
+ patterns?: Readonly<Partial<Record<K, number | readonly number[]>>>;
25
+ /** Asked before every buzz (the player's setting, a native-only rule). Default: always. */
26
+ enabled?: () => boolean;
27
+ /** Default `performance.now`. */
28
+ now?: () => number;
29
+ }
30
+ /** The default player: `navigator.vibrate`, where there is one. */
31
+ export declare function vibrate(pattern: number | readonly number[]): boolean;
32
+ /** Buzzes through `play` when `enabled` and the kind's gap has passed. */
33
+ export declare class Haptics<K extends string> {
34
+ private readonly gate;
35
+ private readonly play;
36
+ private readonly enabled;
37
+ private readonly now;
38
+ constructor(options?: HapticsOptions<K>);
39
+ /** Buzzes for `kind`; returns whether it was let through (not whether the device did buzz). */
40
+ buzz(kind: K): boolean;
41
+ }
42
+ //# sourceMappingURL=haptics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"haptics.d.ts","sourceRoot":"","sources":["../../../src/input/haptics.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,kGAAkG;AAClG,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;AAEhF,kEAAkE;AAClE,qBAAa,UAAU,CAAC,CAAC,SAAS,MAAM;IAG1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;gBAEhB,IAAI,GAAE,UAAU,CAAC,CAAC,CAAuB;IAEtE,8EAA8E;IAC9E,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAQpC,sCAAsC;IACtC,KAAK,IAAI,IAAI;CAGd;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,MAAM;IAC9C,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACrB,wGAAwG;IACxG,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;IAC5B,gGAAgG;IAChG,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACxB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAID,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,OAAO,CAQpE;AAED,0EAA0E;AAC1E,qBAAa,OAAO,CAAC,CAAC,SAAS,MAAM;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,OAAO,GAAE,cAAc,CAAC,CAAC,CAAM;IAO3C,+FAA+F;IAC/F,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO;CAUvB"}
@@ -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"}
@@ -0,0 +1,74 @@
1
+ export type ApiMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
+ /**
3
+ * A request that failed. `status` 0 with `code` `'NETWORK'` is no answer at all (offline, a timeout, a refused
4
+ * connection), `'ABORTED'` the caller's own abort; otherwise `status` is the server's, and `code`, `message` and
5
+ * `details` are what `describe` read off its body.
6
+ */
7
+ export declare class ApiError extends Error {
8
+ readonly status: number;
9
+ readonly code: string;
10
+ readonly details?: Readonly<Record<string, string>> | undefined;
11
+ /** The parsed body of the failed answer, when there was one. */
12
+ readonly body?: unknown | undefined;
13
+ constructor(status: number, code: string, message: string, details?: Readonly<Record<string, string>> | undefined,
14
+ /** The parsed body of the failed answer, when there was one. */
15
+ body?: unknown | undefined);
16
+ }
17
+ /** What a failed answer's body says: the default reads `code`, `message` and `details` off a JSON object. */
18
+ export interface ApiErrorInfo {
19
+ code?: string;
20
+ message?: string;
21
+ details?: Readonly<Record<string, string>>;
22
+ }
23
+ /**
24
+ * What a refresh came back with: a new access token, `'refused'` (the session is over), or null (the server could not
25
+ * be asked — keep the session and let the next call try again).
26
+ */
27
+ export type RefreshResult = string | 'refused' | null;
28
+ export interface ApiClientOptions {
29
+ /** The server's origin (`https://host`), or one chosen per path (accounts on one server, the game on another). */
30
+ baseUrl: string | ((path: string) => string);
31
+ /** Headers sent with every request (a client version, a platform), asked afresh each time. */
32
+ headers?: () => Readonly<Record<string, string>>;
33
+ /** Default 10 s; a request may give its own. */
34
+ timeoutMs?: number;
35
+ /** Turns a failed answer's parsed body into its code, message and details (a game rendering its own messages). */
36
+ describe?: (body: unknown, status: number) => ApiErrorInfo;
37
+ /** The message of a request that got no answer (default `'Cannot reach the server.'`). */
38
+ networkMessage?: string | (() => string);
39
+ /** Paths a 401 never refreshes for (the login and refresh calls themselves). */
40
+ noRefresh?: (path: string) => boolean;
41
+ /** Called with every failed answer before it is thrown (an "update required" gate reading `status` or `code`). */
42
+ onError?: (error: ApiError) => void;
43
+ /** Called for answers with one status before it is thrown: `{ 426: (e) => showUpdate(e.details) }`. */
44
+ onStatus?: Readonly<Record<number, (error: ApiError) => void>>;
45
+ /** Default the global `fetch`. */
46
+ fetch?: typeof fetch;
47
+ }
48
+ export interface ApiRequestOptions {
49
+ timeoutMs?: number;
50
+ /** The caller's own abort; its abort throws an `ApiError` with code `'ABORTED'`. */
51
+ signal?: AbortSignal;
52
+ /** Headers for this request alone, over the client's. */
53
+ headers?: Readonly<Record<string, string>>;
54
+ }
55
+ /**
56
+ * A small JSON REST client: a bearer token, JSON in and out, a timeout on every call, and a session kept alive — a 401
57
+ * is refreshed once (`refresher`) and the request sent again. A game keeps its endpoints and types in a subclass or
58
+ * beside it; nothing here knows any route.
59
+ */
60
+ export declare class ApiClient {
61
+ private readonly options;
62
+ private token;
63
+ /** A new access token for a 401 (`RefreshResult`); unset, a 401 is simply thrown. */
64
+ refresher: (() => Promise<RefreshResult>) | null;
65
+ /** Called when a 401 ends the session: no refresher, a refused refresh, or a retry refused again. */
66
+ onUnauthorized: (() => void) | null;
67
+ constructor(options: ApiClientOptions);
68
+ get accessToken(): string | null;
69
+ setToken(token: string | null): void;
70
+ /** Sends a request and returns its parsed JSON body (undefined for an empty or unreadable one). */
71
+ request<T>(method: ApiMethod, path: string, body?: unknown, options?: ApiRequestOptions, retried?: boolean): Promise<T>;
72
+ private networkError;
73
+ }
74
+ //# sourceMappingURL=ApiClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiClient.d.ts","sourceRoot":"","sources":["../../../src/net/ApiClient.ts"],"names":[],"mappings":"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"}
@@ -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"}