@datamoc/mw_games 0.2.0 → 0.3.1

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 (97) hide show
  1. package/README.md +395 -352
  2. package/dist/assets/Streaming.d.ts +10 -4
  3. package/dist/assets/Streaming.d.ts.map +1 -1
  4. package/dist/assets/Streaming.js +17 -6
  5. package/dist/assets/Streaming.js.map +1 -1
  6. package/dist/assets/index.d.ts +10 -2
  7. package/dist/assets/index.d.ts.map +1 -1
  8. package/dist/assets/index.js +8 -4
  9. package/dist/assets/index.js.map +1 -1
  10. package/dist/audio/Music.d.ts +10 -0
  11. package/dist/audio/Music.d.ts.map +1 -1
  12. package/dist/audio/Music.js +41 -3
  13. package/dist/audio/Music.js.map +1 -1
  14. package/dist/audio/Playable.d.ts +3 -0
  15. package/dist/audio/Playable.d.ts.map +1 -1
  16. package/dist/audio/Playable.js.map +1 -1
  17. package/dist/audio/Sound.d.ts.map +1 -1
  18. package/dist/audio/Sound.js +4 -1
  19. package/dist/audio/Sound.js.map +1 -1
  20. package/dist/audio/Synth.d.ts.map +1 -1
  21. package/dist/audio/Synth.js +3 -1
  22. package/dist/audio/Synth.js.map +1 -1
  23. package/dist/core/Feedback.d.ts +3 -9
  24. package/dist/core/Feedback.d.ts.map +1 -1
  25. package/dist/core/Feedback.js +6 -18
  26. package/dist/core/Feedback.js.map +1 -1
  27. package/dist/core/HttpTransport.d.ts +22 -0
  28. package/dist/core/HttpTransport.d.ts.map +1 -0
  29. package/dist/core/HttpTransport.js +33 -0
  30. package/dist/core/HttpTransport.js.map +1 -0
  31. package/dist/core/Multiplayer.d.ts +63 -0
  32. package/dist/core/Multiplayer.d.ts.map +1 -0
  33. package/dist/core/Multiplayer.js +73 -0
  34. package/dist/core/Multiplayer.js.map +1 -0
  35. package/dist/core/SaveSync.d.ts +17 -14
  36. package/dist/core/SaveSync.d.ts.map +1 -1
  37. package/dist/core/SaveSync.js +35 -31
  38. package/dist/core/SaveSync.js.map +1 -1
  39. package/dist/core/Stats.d.ts +33 -0
  40. package/dist/core/Stats.d.ts.map +1 -0
  41. package/dist/core/Stats.js +34 -0
  42. package/dist/core/Stats.js.map +1 -0
  43. package/dist/core/Telemetry.d.ts +34 -0
  44. package/dist/core/Telemetry.d.ts.map +1 -0
  45. package/dist/core/Telemetry.js +47 -0
  46. package/dist/core/Telemetry.js.map +1 -0
  47. package/dist/core/index.d.ts +6 -0
  48. package/dist/core/index.d.ts.map +1 -1
  49. package/dist/core/index.js +3 -0
  50. package/dist/core/index.js.map +1 -1
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +1 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/mw_games.global.js +10 -9
  56. package/dist/render/Camera.d.ts.map +1 -1
  57. package/dist/render/Camera.js +4 -2
  58. package/dist/render/Camera.js.map +1 -1
  59. package/dist/render/StatusVisuals.d.ts +46 -0
  60. package/dist/render/StatusVisuals.d.ts.map +1 -0
  61. package/dist/render/StatusVisuals.js +48 -0
  62. package/dist/render/StatusVisuals.js.map +1 -0
  63. package/dist/render/index.d.ts +2 -0
  64. package/dist/render/index.d.ts.map +1 -1
  65. package/dist/render/index.js +1 -0
  66. package/dist/render/index.js.map +1 -1
  67. package/dist/simulation/Scenario.d.ts +29 -0
  68. package/dist/simulation/Scenario.d.ts.map +1 -0
  69. package/dist/simulation/Scenario.js +25 -0
  70. package/dist/simulation/Scenario.js.map +1 -0
  71. package/dist/simulation/Turns.d.ts +29 -0
  72. package/dist/simulation/Turns.d.ts.map +1 -0
  73. package/dist/simulation/Turns.js +30 -0
  74. package/dist/simulation/Turns.js.map +1 -0
  75. package/dist/simulation/index.d.ts +5 -0
  76. package/dist/simulation/index.d.ts.map +1 -0
  77. package/dist/simulation/index.js +3 -0
  78. package/dist/simulation/index.js.map +1 -0
  79. package/dist/ui/Button.d.ts +18 -1
  80. package/dist/ui/Button.d.ts.map +1 -1
  81. package/dist/ui/Button.js +15 -5
  82. package/dist/ui/Button.js.map +1 -1
  83. package/dist/ui/Label.d.ts +8 -0
  84. package/dist/ui/Label.d.ts.map +1 -1
  85. package/dist/ui/Label.js +3 -0
  86. package/dist/ui/Label.js.map +1 -1
  87. package/dist/ui/StatsScreen.d.ts +25 -0
  88. package/dist/ui/StatsScreen.d.ts.map +1 -0
  89. package/dist/ui/StatsScreen.js +27 -0
  90. package/dist/ui/StatsScreen.js.map +1 -0
  91. package/dist/ui/index.d.ts +3 -1
  92. package/dist/ui/index.d.ts.map +1 -1
  93. package/dist/ui/index.js +1 -0
  94. package/dist/ui/index.js.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +147 -112
@@ -0,0 +1,73 @@
1
+ import { Signal } from './Signal.js';
2
+ const OPEN = 1; //WebSocket.OPEN, kept as a literal so this file needs no DOM/Node WebSocket type import
3
+ /**
4
+ * A client for this project's own reference lockstep server (`tools/multiplayer-server.mjs`):
5
+ * the transport, identity, and tick-authority piece item 129 asked for, without `mwg`
6
+ * inventing a full prediction/rollback netcode a real game would likely want to replace
7
+ * anyway. Every connected client submits at most one input per tick; the server broadcasts a
8
+ * `tick` message once every client in its room has submitted for that tick, or once a
9
+ * configured timeout elapses, substituting `null` for whoever is still missing - lockstep,
10
+ * the model turn-driven and RTS-shaped games have used for this for decades, chosen because
11
+ * it needs no client-side prediction to already be correct. A game wanting prediction or
12
+ * interpolation on top of this still can: `onTick` hands over the same ordered, authoritative
13
+ * input stream a prediction layer would reconcile against.
14
+ *
15
+ * This is deliberately the only network surface in `mwg` that assumes a server exists rather
16
+ * than a game's own endpoint - `FeedbackClient`/`NewsClient`/`SaveSyncClient` all assume the
17
+ * opposite, a one-shot request/response a game points at its own backend. Real-time
18
+ * multiplayer has no one-shot shape to hide a server behind, which is why `mwg` ships a
19
+ * reference one (`tools/multiplayer-server.mjs`) rather than assuming a game brings its own,
20
+ * the same reasoning that decided this the moment the item was sized.
21
+ */
22
+ export class LockstepClient {
23
+ constructor(options) {
24
+ this.onWelcome = new Signal();
25
+ this.onTick = new Signal();
26
+ this.onClose = new Signal();
27
+ this.socket = null;
28
+ this._id = null;
29
+ if (!options.url)
30
+ throw new Error('lockstep client url is required');
31
+ this.url = options.url;
32
+ this.createSocket = options.create ?? ((url) => new WebSocket(url));
33
+ }
34
+ /** this client's own id, assigned by the server's `welcome` message; `null` before it arrives */
35
+ get id() {
36
+ return this._id;
37
+ }
38
+ get connected() {
39
+ return this.socket !== null && this.socket.readyState === OPEN;
40
+ }
41
+ connect() {
42
+ if (this.socket)
43
+ throw new Error('lockstep client is already connected');
44
+ const socket = this.createSocket(this.url);
45
+ socket.onmessage = (event) => this.handleMessage(event.data);
46
+ socket.onclose = () => {
47
+ this.socket = null;
48
+ this._id = null;
49
+ this.onClose.dispatch();
50
+ };
51
+ this.socket = socket;
52
+ }
53
+ /** submits this client's input for the current tick; a no-op before the connection is open */
54
+ submitInput(payload) {
55
+ if (!this.connected)
56
+ return;
57
+ this.socket?.send(JSON.stringify({ type: 'input', payload }));
58
+ }
59
+ close() {
60
+ this.socket?.close();
61
+ }
62
+ handleMessage(raw) {
63
+ const message = JSON.parse(raw);
64
+ if (message.type === 'welcome' && message.id !== undefined) {
65
+ this._id = message.id;
66
+ this.onWelcome.dispatch({ id: message.id });
67
+ }
68
+ else if (message.type === 'tick' && message.tick !== undefined && message.inputs !== undefined) {
69
+ this.onTick.dispatch({ tick: message.tick, inputs: message.inputs });
70
+ }
71
+ }
72
+ }
73
+ //# sourceMappingURL=Multiplayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Multiplayer.js","sourceRoot":"","sources":["../../src/core/Multiplayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAarC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,wFAAwF;AAcxG;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,cAAc;IAU1B,YAAY,OAA8B;QATjC,cAAS,GAAG,IAAI,MAAM,EAAkB,CAAC;QACzC,WAAM,GAAG,IAAI,MAAM,EAAa,CAAC;QACjC,YAAO,GAAG,IAAI,MAAM,EAAQ,CAAC;QAE9B,WAAM,GAAyB,IAAI,CAAC;QAGpC,QAAG,GAAkB,IAAI,CAAC;QAGjC,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrE,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,CAA6B,CAAC,CAAC;IACjG,CAAC;IAED,iGAAiG;IACjG,IAAI,EAAE;QACL,OAAO,IAAI,CAAC,GAAG,CAAC;IACjB,CAAC;IAED,IAAI,SAAS;QACZ,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI,CAAC;IAChE,CAAC;IAED,OAAO;QACN,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;YACrB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,8FAA8F;IAC9F,WAAW,CAAC,OAAgB;QAC3B,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IACtB,CAAC;IAEO,aAAa,CAAC,GAAW;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmF,CAAC;QAClH,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;IACF,CAAC;CACD"}
@@ -1,26 +1,29 @@
1
+ import { HttpTransport, type HttpTransportOptions } from './HttpTransport.ts';
1
2
  /**
2
- * Uploads/downloads one `SaveSystem.exportSlot` payload to a game's own HTTPS endpoint - the
3
+ * Uploads/downloads `SaveSystem.exportSlot` payloads to a game's own HTTPS endpoint - the
3
4
  * server half of item 124's save export/import, following `FeedbackClient`'s own shape
4
5
  * (an injectable transport a game points at its own endpoint) rather than a second pattern:
5
6
  * `mwg` ships no backend and assumes none here either.
7
+ *
8
+ * Every call names a `slot`, the same name `SaveSystem.exportSlot`/`importSlot` already use
9
+ * locally, so a player with several named profiles (`SaveSystem` supports as many as a game
10
+ * creates) can sync each independently against one endpoint rather than the endpoint holding
11
+ * only ever one save. `list()` is the seam a profile picker reads before choosing which slot
12
+ * to `download`.
6
13
  */
7
- export interface SaveSyncOptions {
8
- endpoint: string;
9
- timeoutMs?: number;
10
- fetch?: typeof globalThis.fetch;
11
- }
14
+ export type SaveSyncOptions = HttpTransportOptions;
12
15
  export interface SaveSyncResponse {
13
16
  ok: boolean;
14
17
  status: number;
15
18
  }
16
- export declare class SaveSyncClient {
17
- private readonly endpoint;
18
- private readonly timeoutMs;
19
- private readonly fetchFn;
19
+ export declare class SaveSyncClient extends HttpTransport {
20
20
  constructor(options: SaveSyncOptions);
21
- /** sends an `exportSlot` payload to the endpoint, replacing whatever it held before */
22
- upload(payload: string): Promise<SaveSyncResponse>;
23
- /** fetches whatever payload the endpoint currently holds, ready for `SaveSystem.importSlot` */
24
- download(): Promise<string>;
21
+ /** sends an `exportSlot` payload for `slot` to the endpoint, replacing whatever it held before */
22
+ upload(slot: string, payload: string): Promise<SaveSyncResponse>;
23
+ /** fetches whatever payload the endpoint currently holds for `slot`, ready for `SaveSystem.importSlot` */
24
+ download(slot: string): Promise<string>;
25
+ /** every slot name this endpoint currently holds for this player, for a profile picker to list */
26
+ list(): Promise<string[]>;
27
+ private slotUrl;
25
28
  }
26
29
  //# sourceMappingURL=SaveSync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SaveSync.d.ts","sourceRoot":"","sources":["../../src/core/SaveSync.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAElD,YAAY,OAAO,EAAE,eAAe,EAOnC;IAED,uFAAuF;IACjF,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgBvD;IAED,+FAA+F;IACzF,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAYhC;CACD"}
1
+ {"version":3,"file":"SaveSync.d.ts","sourceRoot":"","sources":["../../src/core/SaveSync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,cAAe,SAAQ,aAAa;IAChD,YAAY,OAAO,EAAE,eAAe,EAEnC;IAED,kGAAkG;IAC5F,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAarE;IAED,0GAA0G;IACpG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5C;IAED,kGAAkG;IAC5F,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAU9B;IAED,OAAO,CAAC,OAAO;CAIf"}
@@ -1,52 +1,56 @@
1
- export class SaveSyncClient {
1
+ import { HttpTransport } from './HttpTransport.js';
2
+ export class SaveSyncClient extends HttpTransport {
2
3
  constructor(options) {
3
- if (!options.endpoint)
4
- throw new Error('save sync endpoint is required');
5
- this.endpoint = options.endpoint;
6
- this.timeoutMs = options.timeoutMs ?? 10000;
7
- if (!(this.timeoutMs > 0))
8
- throw new Error('save sync timeout must be positive');
9
- this.fetchFn = options.fetch ?? globalThis.fetch;
10
- if (!this.fetchFn)
11
- throw new Error('fetch is unavailable; provide SaveSyncOptions.fetch');
4
+ super(options, 'save sync');
12
5
  }
13
- /** sends an `exportSlot` payload to the endpoint, replacing whatever it held before */
14
- async upload(payload) {
6
+ /** sends an `exportSlot` payload for `slot` to the endpoint, replacing whatever it held before */
7
+ async upload(slot, payload) {
8
+ if (!slot)
9
+ throw new Error('save sync slot name is required');
15
10
  if (!payload)
16
11
  throw new Error('save sync payload cannot be empty');
17
- const controller = new AbortController();
18
- const timer = setTimeout(() => controller.abort(), this.timeoutMs);
19
- try {
20
- const response = await this.fetchFn(this.endpoint, {
12
+ return this.withTimeout(async (signal) => {
13
+ const response = await this.fetchFn(this.slotUrl(slot), {
21
14
  method: 'POST',
22
15
  headers: { 'content-type': 'application/json' },
23
16
  body: JSON.stringify({ payload }),
24
- signal: controller.signal,
17
+ signal,
25
18
  });
26
19
  if (!response.ok)
27
20
  throw new Error(`save upload failed with HTTP ${response.status}`);
28
21
  return { ok: true, status: response.status };
29
- }
30
- finally {
31
- clearTimeout(timer);
32
- }
22
+ });
33
23
  }
34
- /** fetches whatever payload the endpoint currently holds, ready for `SaveSystem.importSlot` */
35
- async download() {
36
- const controller = new AbortController();
37
- const timer = setTimeout(() => controller.abort(), this.timeoutMs);
38
- try {
39
- const response = await this.fetchFn(this.endpoint, { signal: controller.signal });
24
+ /** fetches whatever payload the endpoint currently holds for `slot`, ready for `SaveSystem.importSlot` */
25
+ async download(slot) {
26
+ if (!slot)
27
+ throw new Error('save sync slot name is required');
28
+ return this.withTimeout(async (signal) => {
29
+ const response = await this.fetchFn(this.slotUrl(slot), { signal });
40
30
  if (!response.ok)
41
31
  throw new Error(`save download failed with HTTP ${response.status}`);
42
32
  const data = (await response.json());
43
33
  if (typeof data.payload !== 'string')
44
34
  throw new Error('save download response missing a string payload');
45
35
  return data.payload;
46
- }
47
- finally {
48
- clearTimeout(timer);
49
- }
36
+ });
37
+ }
38
+ /** every slot name this endpoint currently holds for this player, for a profile picker to list */
39
+ async list() {
40
+ return this.withTimeout(async (signal) => {
41
+ const response = await this.fetchFn(this.endpoint, { signal });
42
+ if (!response.ok)
43
+ throw new Error(`save slot list failed with HTTP ${response.status}`);
44
+ const data = (await response.json());
45
+ if (!Array.isArray(data.slots) || !data.slots.every((slot) => typeof slot === 'string')) {
46
+ throw new Error('save slot list response missing a string array of slots');
47
+ }
48
+ return data.slots;
49
+ });
50
+ }
51
+ slotUrl(slot) {
52
+ const separator = this.endpoint.includes('?') ? '&' : '?';
53
+ return `${this.endpoint}${separator}slot=${encodeURIComponent(slot)}`;
50
54
  }
51
55
  }
52
56
  //# sourceMappingURL=SaveSync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SaveSync.js","sourceRoot":"","sources":["../../src/core/SaveSync.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,cAAc;IAK1B,YAAY,OAAwB;QACnC,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACzE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;QAC5C,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACjF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC3F,CAAC;IAED,uFAAuF;IACvF,KAAK,CAAC,MAAM,CAAC,OAAe;QAC3B,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACnE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAClD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;gBACjC,MAAM,EAAE,UAAU,CAAC,MAAM;aACzB,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACrF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9C,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACF,CAAC;IAED,+FAA+F;IAC/F,KAAK,CAAC,QAAQ;QACb,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACvF,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA0B,CAAC;YAC9D,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACzG,OAAO,IAAI,CAAC,OAAO,CAAC;QACrB,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACF,CAAC;CACD"}
1
+ {"version":3,"file":"SaveSync.js","sourceRoot":"","sources":["../../src/core/SaveSync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type SaveStorage } from './Save.ts';
2
+ export interface PlayerStatsOptions<T, S = T> {
3
+ /** namespaces the stored total, so two games sharing an origin never collide */
4
+ namespace: string;
5
+ storage?: SaveStorage;
6
+ /** the total before anything has ever been recorded */
7
+ initial: T;
8
+ /** folds one run's own summary into the running total */
9
+ combine: (total: T, summary: S) => T;
10
+ }
11
+ /**
12
+ * A lifetime running total, folded in one run at a time - distinct from `RunHistory`, which
13
+ * keeps individual runs and may drop the oldest ones once its own `limit` is reached.
14
+ * Deriving a lifetime total by reducing over `RunHistory.all()` would silently undercount the
15
+ * moment that limit trims anything, so this keeps its own persisted total instead, updated
16
+ * incrementally by `record` and never recomputed from a list that can shrink. A game wanting
17
+ * both a "last 50 runs" list and a true lifetime total uses both classes side by side, each
18
+ * for the question it actually answers.
19
+ */
20
+ export declare class PlayerStats<T, S = T> {
21
+ private readonly storage;
22
+ private readonly key;
23
+ private readonly initial;
24
+ private readonly combine;
25
+ constructor(options: PlayerStatsOptions<T, S>);
26
+ /** the current lifetime total, `initial` if nothing has been recorded yet */
27
+ get(): T;
28
+ /** folds `summary` into the running total and persists it, returning the new total */
29
+ record(summary: S): T;
30
+ /** back to `initial`, as a new save file or a "reset stats" option should */
31
+ reset(): void;
32
+ }
33
+ //# sourceMappingURL=Stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stats.d.ts","sourceRoot":"","sources":["../../src/core/Stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7D,MAAM,WAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC;IAC3C,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,uDAAuD;IACvD,OAAO,EAAE,CAAC,CAAC;IACX,yDAAyD;IACzD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,qBAAa,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAI;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IAEtD,YAAY,OAAO,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,EAK5C;IAED,6EAA6E;IAC7E,GAAG,IAAI,CAAC,CAGP;IAED,sFAAsF;IACtF,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAIpB;IAED,6EAA6E;IAC7E,KAAK,IAAI,IAAI,CAEZ;CACD"}
@@ -0,0 +1,34 @@
1
+ import { defaultStorage } from './Save.js';
2
+ /**
3
+ * A lifetime running total, folded in one run at a time - distinct from `RunHistory`, which
4
+ * keeps individual runs and may drop the oldest ones once its own `limit` is reached.
5
+ * Deriving a lifetime total by reducing over `RunHistory.all()` would silently undercount the
6
+ * moment that limit trims anything, so this keeps its own persisted total instead, updated
7
+ * incrementally by `record` and never recomputed from a list that can shrink. A game wanting
8
+ * both a "last 50 runs" list and a true lifetime total uses both classes side by side, each
9
+ * for the question it actually answers.
10
+ */
11
+ export class PlayerStats {
12
+ constructor(options) {
13
+ this.storage = options.storage ?? defaultStorage();
14
+ this.key = `mwg-stats:${options.namespace}`;
15
+ this.initial = options.initial;
16
+ this.combine = options.combine;
17
+ }
18
+ /** the current lifetime total, `initial` if nothing has been recorded yet */
19
+ get() {
20
+ const raw = this.storage.read(this.key);
21
+ return raw ? JSON.parse(raw) : this.initial;
22
+ }
23
+ /** folds `summary` into the running total and persists it, returning the new total */
24
+ record(summary) {
25
+ const next = this.combine(this.get(), summary);
26
+ this.storage.write(this.key, JSON.stringify(next));
27
+ return next;
28
+ }
29
+ /** back to `initial`, as a new save file or a "reset stats" option should */
30
+ reset() {
31
+ this.storage.remove(this.key);
32
+ }
33
+ }
34
+ //# sourceMappingURL=Stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stats.js","sourceRoot":"","sources":["../../src/core/Stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAoB,MAAM,WAAW,CAAC;AAY7D;;;;;;;;GAQG;AACH,MAAM,OAAO,WAAW;IAMvB,YAAY,OAAiC;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;QACnD,IAAI,CAAC,GAAG,GAAG,aAAa,OAAO,CAAC,SAAS,EAAE,CAAC;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,CAAC;IAED,6EAA6E;IAC7E,GAAG;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IACpD,CAAC;IAED,sFAAsF;IACtF,MAAM,CAAC,OAAU;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACb,CAAC;IAED,6EAA6E;IAC7E,KAAK;QACJ,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;CACD"}
@@ -0,0 +1,34 @@
1
+ import { HttpTransport, type HttpTransportOptions } from './HttpTransport.ts';
2
+ export interface TelemetryEvent {
3
+ name: string;
4
+ properties?: Record<string, string | number | boolean | null>;
5
+ }
6
+ export type TelemetryOptions = HttpTransportOptions;
7
+ export interface TelemetryResponse {
8
+ ok: boolean;
9
+ status: number;
10
+ }
11
+ /**
12
+ * An injectable transport for automatic, structured analytics events - `FeedbackClient`'s own
13
+ * shape, for a different question. `FeedbackClient` is a player choosing to write something
14
+ * and send it; this is a game deciding on its own to send something, which needs its own
15
+ * consent gate before anything ever leaves the machine, unlike a player action that already
16
+ * consents to sending just by happening. `send` is a silent no-op, not an error, until
17
+ * `setConsent(true)` has been called - automatic collection defaults off.
18
+ *
19
+ * `mwg` stays opinion-free on what an event actually contains, what "consent" means for a
20
+ * given game (a settings toggle, a first-launch prompt, a platform-level flag), and where the
21
+ * endpoint lives - the same boundary `FeedbackClient`'s own doc comment draws for manual
22
+ * reports.
23
+ */
24
+ export declare class TelemetryClient extends HttpTransport {
25
+ private consented;
26
+ constructor(options: TelemetryOptions);
27
+ /** whether `send` will actually transmit anything right now */
28
+ get hasConsent(): boolean;
29
+ /** a game calls this from its own consent flow; not read from anywhere `mwg` controls */
30
+ setConsent(granted: boolean): void;
31
+ /** sends one structured event; a silent no-op before consent is granted, not an error */
32
+ send(event: TelemetryEvent): Promise<TelemetryResponse | null>;
33
+ }
34
+ //# sourceMappingURL=Telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Telemetry.d.ts","sourceRoot":"","sources":["../../src/core/Telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;CAC9D;AAED,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,SAAQ,aAAa;IACjD,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,OAAO,EAAE,gBAAgB,EAEpC;IAED,+DAA+D;IAC/D,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,yFAAyF;IACzF,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEjC;IAED,yFAAyF;IACnF,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAcnE;CACD"}
@@ -0,0 +1,47 @@
1
+ import { HttpTransport } from './HttpTransport.js';
2
+ /**
3
+ * An injectable transport for automatic, structured analytics events - `FeedbackClient`'s own
4
+ * shape, for a different question. `FeedbackClient` is a player choosing to write something
5
+ * and send it; this is a game deciding on its own to send something, which needs its own
6
+ * consent gate before anything ever leaves the machine, unlike a player action that already
7
+ * consents to sending just by happening. `send` is a silent no-op, not an error, until
8
+ * `setConsent(true)` has been called - automatic collection defaults off.
9
+ *
10
+ * `mwg` stays opinion-free on what an event actually contains, what "consent" means for a
11
+ * given game (a settings toggle, a first-launch prompt, a platform-level flag), and where the
12
+ * endpoint lives - the same boundary `FeedbackClient`'s own doc comment draws for manual
13
+ * reports.
14
+ */
15
+ export class TelemetryClient extends HttpTransport {
16
+ constructor(options) {
17
+ super(options, 'telemetry');
18
+ this.consented = false;
19
+ }
20
+ /** whether `send` will actually transmit anything right now */
21
+ get hasConsent() {
22
+ return this.consented;
23
+ }
24
+ /** a game calls this from its own consent flow; not read from anywhere `mwg` controls */
25
+ setConsent(granted) {
26
+ this.consented = granted;
27
+ }
28
+ /** sends one structured event; a silent no-op before consent is granted, not an error */
29
+ async send(event) {
30
+ if (!event.name)
31
+ throw new Error('telemetry event needs a name');
32
+ if (!this.consented)
33
+ return null;
34
+ return this.withTimeout(async (signal) => {
35
+ const response = await this.fetchFn(this.endpoint, {
36
+ method: 'POST',
37
+ headers: { 'content-type': 'application/json' },
38
+ body: JSON.stringify(event),
39
+ signal,
40
+ });
41
+ if (!response.ok)
42
+ throw new Error(`telemetry request failed with HTTP ${response.status}`);
43
+ return { ok: true, status: response.status };
44
+ });
45
+ }
46
+ }
47
+ //# sourceMappingURL=Telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Telemetry.js","sourceRoot":"","sources":["../../src/core/Telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,oBAAoB,CAAC;AAc9E;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,eAAgB,SAAQ,aAAa;IAGjD,YAAY,OAAyB;QACpC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAHrB,cAAS,GAAG,KAAK,CAAC;IAI1B,CAAC;IAED,+DAA+D;IAC/D,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,SAAS,CAAC;IACvB,CAAC;IAED,yFAAyF;IACzF,UAAU,CAAC,OAAgB;QAC1B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,yFAAyF;IACzF,KAAK,CAAC,IAAI,CAAC,KAAqB;QAC/B,IAAI,CAAC,KAAK,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAEjC,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAClD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;gBAC3B,MAAM;aACN,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9C,CAAC,CAAC,CAAC;IACJ,CAAC;CACD"}
@@ -17,8 +17,14 @@ export type { SaveMeta, SaveData, SaveStorage, SaveSystemOptions } from './Save.
17
17
  export { scramble, unscramble } from './Scramble.ts';
18
18
  export { SaveSyncClient } from './SaveSync.ts';
19
19
  export type { SaveSyncOptions, SaveSyncResponse } from './SaveSync.ts';
20
+ export { LockstepClient } from './Multiplayer.ts';
21
+ export type { WebSocketLike, LockstepClientOptions, TickEvent } from './Multiplayer.ts';
20
22
  export { RunHistory } from './RunHistory.ts';
21
23
  export type { RunHistoryEntry, RunHistoryOptions } from './RunHistory.ts';
24
+ export { PlayerStats } from './Stats.ts';
25
+ export type { PlayerStatsOptions } from './Stats.ts';
26
+ export { TelemetryClient } from './Telemetry.ts';
27
+ export type { TelemetryEvent, TelemetryOptions, TelemetryResponse } from './Telemetry.ts';
22
28
  export { NewsClient, NewsSeenTracker } from './News.ts';
23
29
  export type { NewsItem, NewsOptions, NewsSeenOptions } from './News.ts';
24
30
  export { checkSize, checkNoControlCharacters, sanitizeInboundText, validateSchema } from './Sanitize.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,10 @@ export { Generator } from './Random.js';
10
10
  export { SaveSystem } from './Save.js';
11
11
  export { scramble, unscramble } from './Scramble.js';
12
12
  export { SaveSyncClient } from './SaveSync.js';
13
+ export { LockstepClient } from './Multiplayer.js';
13
14
  export { RunHistory } from './RunHistory.js';
15
+ export { PlayerStats } from './Stats.js';
16
+ export { TelemetryClient } from './Telemetry.js';
14
17
  export { NewsClient, NewsSeenTracker } from './News.js';
15
18
  export { checkSize, checkNoControlCharacters, sanitizeInboundText, validateSchema } from './Sanitize.js';
16
19
  export { Collection } from './Collection.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEzG,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEhG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEzG,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEhG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.d.ts CHANGED
@@ -20,4 +20,5 @@ export * as Rpg from './rpg/index.ts';
20
20
  export * as Battle from './battle/index.ts';
21
21
  export * as Board from './board/index.ts';
22
22
  export * as Audio from './audio/index.ts';
23
+ export * as Simulation from './simulation/index.ts';
23
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -20,4 +20,5 @@ export * as Rpg from './rpg/index.js';
20
20
  export * as Battle from './battle/index.js';
21
21
  export * as Board from './board/index.js';
22
22
  export * as Audio from './audio/index.js';
23
+ export * as Simulation from './simulation/index.js';
23
24
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC"}