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
package/dist/net/Room.js CHANGED
@@ -1,21 +1,38 @@
1
- import { createMover as l, stepMover as p } from "./movement.js";
2
- import { moverToWire as c } from "./protocol.js";
3
- class g {
1
+ import { LagCompensator as f } from "./LagCompensator.js";
2
+ import { createMover as m, stepMover as p } from "./movement.js";
3
+ import { moverToWire as y } from "./protocol.js";
4
+ import { SpatialHash as b } from "./SpatialHash.js";
5
+ const g = 1 << 24;
6
+ class M {
4
7
  constructor(e) {
5
8
  this.options = e, this.maxQueue = e.maxQueue ?? 30, this.maxSteps = e.maxStepsPerTick ?? 3, this.backlog = e.backlog ?? 4;
9
+ const i = e.interest;
10
+ if (i && !(i.radius > 0)) throw new Error(`Room: interest.radius must be above 0 (got ${i.radius})`);
11
+ this.hash = i ? new b(i.radius) : null, this.rewindMs = Math.max(0, e.rewindMs ?? 0), this.history = this.rewindMs > 0 ? new f(this.rewindMs + 200) : null;
6
12
  }
7
13
  options;
8
14
  players = /* @__PURE__ */ new Map();
15
+ /** The bodies the server moves (`addBody`). */
16
+ bodies = /* @__PURE__ */ new Map();
9
17
  tick = 0;
18
+ /** Past positions for judging shots against what a client saw, when the room was made with `rewindMs`. */
19
+ history;
20
+ /** How far back a client's claimed view time is honoured (`rewindMs`, 0 when off). */
21
+ rewindMs;
10
22
  known = /* @__PURE__ */ new Map();
23
+ hash;
24
+ nearby = [];
11
25
  maxQueue;
12
26
  maxSteps;
13
27
  backlog;
28
+ nextBodyId = g;
14
29
  join(e, i) {
15
30
  if (this.players.has(e)) throw new Error(`Room: player ${e} has already joined`);
16
- const r = this.options.spawn(e, i), t = {
31
+ if (this.bodies.has(e)) throw new Error(`Room: id ${e} is a body's`);
32
+ const t = this.options.spawn(e, i), s = {
33
+ kind: "player",
17
34
  id: e,
18
- mover: l(r.x, r.y),
35
+ mover: m(t.x, t.y),
19
36
  data: i,
20
37
  params: { ...this.options.params },
21
38
  ack: -1,
@@ -26,66 +43,128 @@ class g {
26
43
  flags: 0,
27
44
  strikes: 0
28
45
  };
29
- return this.players.set(e, t), this.known.set(e, /* @__PURE__ */ new Set()), t;
46
+ return this.players.set(e, s), this.known.set(e, /* @__PURE__ */ new Set()), this.hash?.set(e, t.x, t.y), s;
30
47
  }
31
48
  leave(e) {
32
- this.players.delete(e), this.known.delete(e);
49
+ this.players.delete(e), this.known.delete(e), this.hash?.delete(e), this.history?.forget(e);
50
+ }
51
+ /**
52
+ * Records where every body is now, at server time `time`, into `history`; `RoomHost` calls it as each round of
53
+ * snapshots goes out, so the history holds exactly the positions clients interpolate between.
54
+ */
55
+ record(e) {
56
+ if (!this.history) return;
57
+ const i = [...this.players.values()].map((t) => ({ id: t.id, x: t.mover.x, y: t.mover.y }));
58
+ for (const t of this.bodies.values()) i.push({ id: t.id, x: t.x, y: t.y });
59
+ this.history.record(e, i);
60
+ }
61
+ /**
62
+ * Adds a body the server moves, at (x, y), with the game's `data` (what players are told it looks like). Its id
63
+ * is `id`, or the next from `BODY_ID_BASE` up; an id a player or body has is refused.
64
+ */
65
+ addBody(e, i, t, s) {
66
+ let o = s;
67
+ if (o === void 0) {
68
+ for (; this.bodies.has(this.nextBodyId) || this.players.has(this.nextBodyId); ) this.nextBodyId++;
69
+ o = this.nextBodyId++;
70
+ } else if (this.bodies.has(o) || this.players.has(o)) throw new Error(`Room: id ${o} is taken`);
71
+ if (!Number.isInteger(o) || o < 0 || o > 2147483647) throw new Error(`Room: a body id must fit an i32 (got ${o})`);
72
+ const a = { kind: "body", id: o, x: i, y: t, aim: 0, anim: 0, hp: 0, flags: 0, data: e };
73
+ return this.bodies.set(o, a), this.hash?.set(o, i, t), a;
74
+ }
75
+ /** Takes a body out: players who saw it get it in `rm` with their next snapshot. */
76
+ removeBody(e) {
77
+ this.bodies.delete(e) && (this.hash?.delete(e), this.history?.forget(e));
33
78
  }
34
79
  /**
35
80
  * Input steps `seq`, `seq + 1`, … from a player. Steps already received are skipped (a resend), any step with
36
81
  * numbers no client sends refuses the whole message, and steps past a full queue are dropped. Returns false when
37
82
  * something was refused.
38
83
  */
39
- input(e, i, r) {
40
- const t = this.players.get(e);
41
- if (!t) return !1;
42
- if (!Number.isInteger(i) || i < 0 || !Array.isArray(r) || r.length > this.maxQueue)
43
- return t.strikes++, !1;
44
- for (const s of r)
45
- if (!Array.isArray(s) || s.length !== 3 || !s.every((u) => Number.isFinite(u)) || Math.abs(s[0]) > 1 || Math.abs(s[1]) > 1 || !Number.isInteger(s[2]))
46
- return t.strikes++, !1;
47
- const n = t.ack + 1 + t.queue.length, o = i - n;
48
- if (o > this.maxQueue)
49
- return t.strikes++, !1;
50
- for (let s = 0; s < o; s++) t.queue.push([0, 0, 0]);
51
- let h = !0;
52
- for (let s = 0; s < r.length; s++)
53
- if (!(i + s < n)) {
54
- if (t.queue.length >= this.maxQueue) {
55
- h = !1, t.strikes++;
84
+ input(e, i, t) {
85
+ const s = this.players.get(e);
86
+ if (!s) return !1;
87
+ if (!Number.isInteger(i) || i < 0 || !Array.isArray(t) || t.length > this.maxQueue)
88
+ return s.strikes++, !1;
89
+ for (const r of t)
90
+ if (!Array.isArray(r) || r.length !== 3 || !r.every((h) => Number.isFinite(h)) || Math.abs(r[0]) > 1 || Math.abs(r[1]) > 1 || !Number.isInteger(r[2]))
91
+ return s.strikes++, !1;
92
+ const o = s.ack + 1 + s.queue.length, a = i - o;
93
+ if (a > this.maxQueue)
94
+ return s.strikes++, !1;
95
+ for (let r = 0; r < a; r++) s.queue.push([0, 0, 0]);
96
+ let u = !0;
97
+ for (let r = 0; r < t.length; r++)
98
+ if (!(i + r < o)) {
99
+ if (s.queue.length >= this.maxQueue) {
100
+ u = !1, s.strikes++;
56
101
  break;
57
102
  }
58
- t.queue.push([r[s][0], r[s][1], r[s][2]]);
103
+ s.queue.push([t[r][0], t[r][1], t[r][2]]);
59
104
  }
60
- return h;
105
+ return u;
61
106
  }
62
107
  /** One simulation step for everyone. */
63
108
  step() {
64
109
  this.tick++;
65
110
  for (const e of this.players.values()) {
66
- const i = e.queue.length;
67
- if (i === 0) continue;
68
- const r = i > this.backlog ? Math.min(this.maxSteps, i - this.backlog + 1) : 1;
69
- for (let t = 0; t < r; t++) {
70
- const n = e.queue.shift();
71
- p(e.mover, n, e.params, this.options.grid), e.ack++, this.options.onStep?.(e, n);
111
+ const i = e.queue.length, t = i > this.backlog ? Math.min(this.maxSteps, i - this.backlog + 1) : Math.min(1, i);
112
+ for (let s = 0; s < t; s++) {
113
+ const o = e.queue.shift();
114
+ p(e.mover, o, e.params, this.options.grid), e.ack++, this.options.onStep?.(e, o);
72
115
  }
116
+ this.hash?.set(e.id, e.mover.x, e.mover.y);
73
117
  }
118
+ if (this.hash) for (const e of this.bodies.values()) this.hash.set(e.id, e.x, e.y);
119
+ }
120
+ /**
121
+ * Moves a player by hand (a teleport, a respawn), and files it in the interest grid at once. Writing `mover.x/y`
122
+ * directly also works; the room then finds it at its new place from the next step.
123
+ */
124
+ place(e, i, t) {
125
+ const s = this.players.get(e), o = this.bodies.get(e);
126
+ if (s)
127
+ s.mover.x = i, s.mover.y = t;
128
+ else if (o)
129
+ o.x = i, o.y = t;
130
+ else return;
131
+ this.hash?.set(e, i, t);
74
132
  }
75
133
  /** The snapshot player `id` should get now. `time` is the server's wall clock (ms). */
76
134
  snapshotFor(e, i) {
77
- const r = this.players.get(e), t = this.known.get(e);
78
- if (!r || !t) return null;
79
- const n = [], o = [], h = /* @__PURE__ */ new Set();
80
- for (const a of this.players.values())
81
- h.add(a.id), n.push([a.id, a.mover.x, a.mover.y, a.aim, a.anim, a.hp, a.flags]), t.has(a.id) || (t.add(a.id), o.push({ id: a.id, data: a.data }));
82
- const s = [];
83
- for (const a of t)
84
- h.has(a) || (t.delete(a), s.push(a));
85
- const u = { t: "snap", tick: this.tick, time: i, ack: r.ack, me: c(r.mover), up: n };
86
- return o.length && (u.sp = o), s.length && (u.rm = s), u;
135
+ const t = this.players.get(e), s = this.known.get(e);
136
+ if (!t || !s) return null;
137
+ const o = [], a = [], u = /* @__PURE__ */ new Set();
138
+ for (const n of this.visibleTo(t, s)) {
139
+ u.add(n.id);
140
+ const c = d(n);
141
+ o.push([n.id, c.x, c.y, n.aim, n.anim, n.hp, n.flags]), s.has(n.id) || (s.add(n.id), a.push({ id: n.id, data: n.data }));
142
+ }
143
+ const r = [];
144
+ for (const n of s)
145
+ u.has(n) || (s.delete(n), r.push(n));
146
+ const h = { t: "snap", tick: this.tick, time: i, ack: t.ack, me: y(t.mover), up: o };
147
+ return a.length && (h.sp = a), r.length && (h.rm = r), h;
148
+ }
149
+ /** What `me` is sent: itself always; with interest, the rest in range (a known one until past the margin). */
150
+ visibleTo(e, i) {
151
+ const t = this.options.interest;
152
+ if (!t || !this.hash) return [...this.players.values(), ...this.bodies.values()];
153
+ const s = t.radius, o = s + (t.leaveMargin ?? s / 4), a = this.nearby;
154
+ a.length = 0, this.hash.near(e.mover.x, e.mover.y, o, a);
155
+ const u = [e];
156
+ for (const r of a) {
157
+ if (r === e.id) continue;
158
+ const h = this.players.get(r) ?? this.bodies.get(r);
159
+ if (!h) continue;
160
+ const n = d(h);
161
+ (n.x - e.mover.x) ** 2 + (n.y - e.mover.y) ** 2 > s * s && !i.has(r) || t.canSee && !t.canSee(e, h) || u.push(h);
162
+ }
163
+ return u.sort((r, h) => r.id - h.id);
87
164
  }
88
165
  }
166
+ const d = (l) => l.kind === "player" ? l.mover : l;
89
167
  export {
90
- g as Room
168
+ g as BODY_ID_BASE,
169
+ M as Room
91
170
  };
@@ -1,5 +1,6 @@
1
- import { encodeSnapshotFrame as n } from "./snapshotCodec.js";
2
- class p {
1
+ import { LagCompensator as n } from "./LagCompensator.js";
2
+ import { encodeSnapshotFrame as r } from "./snapshotCodec.js";
3
+ class c {
3
4
  constructor(t, s = {}) {
4
5
  this.room = t, this.options = s, this.stepMs = 1e3 / (s.stepRate ?? 60), this.snapEvery = Math.max(1, Math.round((s.stepRate ?? 60) / (s.snapRate ?? 20))), this.now = s.now ?? Date.now;
5
6
  }
@@ -64,7 +65,7 @@ class p {
64
65
  /** Sends one of the game's own messages to a player. */
65
66
  sendTo(t, s) {
66
67
  const e = this.sockets.get(t);
67
- e && (s.t === "snap" && s.up?.length ? e.send(n(s)) : e.send(JSON.stringify(s)));
68
+ e && (s.t === "snap" && s.up?.length ? e.send(r(s)) : e.send(JSON.stringify(s)));
68
69
  }
69
70
  /** Starts stepping on a timer. Steps are counted by the clock, so a late timer runs the steps it owes. */
70
71
  start() {
@@ -81,11 +82,19 @@ class p {
81
82
  stepOnce() {
82
83
  if (this.room.step(), this.room.tick % this.snapEvery !== 0) return;
83
84
  const t = this.now();
85
+ this.room.record(t);
84
86
  for (const s of this.joined) {
85
87
  const e = this.room.snapshotFor(s, t);
86
88
  e && this.sendTo(s, e);
87
89
  }
88
90
  }
91
+ /**
92
+ * The server time a client's claimed view time (`InterpolationBuffer.viewTime`, sent with a shot) is judged at:
93
+ * kept within the room's `rewindMs` of now, so no claim reaches further back. `now` with no history kept.
94
+ */
95
+ rewindTime(t) {
96
+ return n.clampTime(t, this.now(), this.room.rewindMs);
97
+ }
89
98
  strike(t) {
90
99
  const s = this.room.players.get(t);
91
100
  s && (s.strikes++, this.checkStrikes(t));
@@ -96,5 +105,5 @@ class p {
96
105
  }
97
106
  }
98
107
  export {
99
- p as RoomHost
108
+ c as RoomHost
100
109
  };
@@ -0,0 +1,66 @@
1
+ const z = (a, t) => (a + 32768) * 65536 + (t + 32768);
2
+ class M {
3
+ /** `cellSize`: world px per cell. About the radius queries ask for keeps a query to a 3×3 block. */
4
+ constructor(t) {
5
+ if (this.cellSize = t, !(t > 0)) throw new Error(`SpatialHash: cellSize must be above 0 (got ${t})`);
6
+ }
7
+ cellSize;
8
+ cells = /* @__PURE__ */ new Map();
9
+ entries = /* @__PURE__ */ new Map();
10
+ get size() {
11
+ return this.entries.size;
12
+ }
13
+ has(t) {
14
+ return this.entries.has(t);
15
+ }
16
+ /** Puts `id` at (x, y), or moves it there. */
17
+ set(t, s, e) {
18
+ const l = z(Math.floor(s / this.cellSize), Math.floor(e / this.cellSize)), i = this.entries.get(t);
19
+ if (i) {
20
+ if (i.x = s, i.y = e, i.cell === l) return;
21
+ this.unlink(t, i.cell), i.cell = l;
22
+ } else this.entries.set(t, { x: s, y: e, cell: l });
23
+ let n = this.cells.get(l);
24
+ n || this.cells.set(l, n = /* @__PURE__ */ new Set()), n.add(t);
25
+ }
26
+ delete(t) {
27
+ const s = this.entries.get(t);
28
+ s && (this.unlink(t, s.cell), this.entries.delete(t));
29
+ }
30
+ clear() {
31
+ this.cells.clear(), this.entries.clear();
32
+ }
33
+ /** The ids within `radius` of (x, y), edge included, pushed onto `out` in no set order. */
34
+ near(t, s, e, l = []) {
35
+ const i = e * e;
36
+ return this.visit(t - e, s - e, t + e, s + e, (n, c) => {
37
+ const r = c.x - t, o = c.y - s;
38
+ r * r + o * o <= i && l.push(n);
39
+ }), l;
40
+ }
41
+ /** The ids inside the box from (x0, y0) to (x1, y1), edges included. */
42
+ inRect(t, s, e, l, i = []) {
43
+ return this.visit(t, s, e, l, (n, c) => {
44
+ c.x >= t && c.x <= e && c.y >= s && c.y <= l && i.push(n);
45
+ }), i;
46
+ }
47
+ visit(t, s, e, l, i) {
48
+ const n = this.cellSize, c = Math.floor(t / n), r = Math.floor(s / n), o = Math.floor(e / n), S = Math.floor(l / n);
49
+ if ((o - c + 1) * (S - r + 1) > this.entries.size) {
50
+ for (const [h, f] of this.entries) i(h, f);
51
+ return;
52
+ }
53
+ for (let h = r; h <= S; h++)
54
+ for (let f = c; f <= o; f++) {
55
+ const g = this.cells.get(z(f, h));
56
+ if (g) for (const u of g) i(u, this.entries.get(u));
57
+ }
58
+ }
59
+ unlink(t, s) {
60
+ const e = this.cells.get(s);
61
+ e && (e.delete(t), e.size === 0 && this.cells.delete(s));
62
+ }
63
+ }
64
+ export {
65
+ M as SpatialHash
66
+ };
@@ -0,0 +1,83 @@
1
+ const F = {
2
+ leaves: [
3
+ ["#7ab04e", "#3d7a36"],
4
+ ["#d8a040", "#9a5a24"]
5
+ ],
6
+ seed: ["#f6f2e4", "#c8c2b0"],
7
+ ember: ["#ffd070", "#ff8a30", "#a8481c"],
8
+ mote: ["rgba(206,226,240,0.85)", "rgba(150,180,206,0.7)"],
9
+ ash: [
10
+ ["#8a827c", "#5c5652"],
11
+ ["#6e6864", "#3e3a38"]
12
+ ],
13
+ butterflies: [
14
+ ["#f0d050", "#9a7420"],
15
+ ["#f4f0e8", "#a8a098"],
16
+ ["#e8883a", "#8a4418"],
17
+ ["#9ab8f0", "#465e9c"]
18
+ ],
19
+ butterflyBody: "#3a2a20",
20
+ bird: "#2c2830",
21
+ birdShadow: ["rgba(0,0,0,0.3)", "rgba(0,0,0,0.22)"],
22
+ firefly: ["#f0ff9a", "#a8c850"],
23
+ smoke: ["#b8b0b4", "#9a9296", "#7c7478"]
24
+ }, v = 6;
25
+ function L(o, e) {
26
+ const l = Number.parseInt(o.replace("#", ""), 16);
27
+ return `rgba(${l >> 16 & 255},${l >> 8 & 255},${l & 255},${e})`;
28
+ }
29
+ function M(o, e = F.smoke) {
30
+ const l = [[2, 2], [3, 3], [4, 3], [5, 4], [6, 5], [7, 5]], [s, n] = l[Math.max(0, Math.min(v - 1, o))], [b, _, p] = e, h = [], y = (s - 1) / 2, u = (n - 1) / 2, S = o < 3 ? 0.9 : o < 5 ? 0.78 : 0.62;
31
+ for (let a = 0; a < n; a++)
32
+ for (let r = 0; r < s; r++) {
33
+ const $ = (r - y) / (s / 2), m = (a - u) / (n / 2), g = (r * 7 + a * 13 + o * 5) % 5 / 10;
34
+ if ($ * $ + m * m > 0.85 + g || o >= v - 2 && (r + a + o) % 2) continue;
35
+ const w = $ + m, d = w < -0.35 ? b : w > 0.45 ? p : _;
36
+ h.push([r, a, d.startsWith("#") ? L(d, S) : d]);
37
+ }
38
+ return { w: s, h: n, px: h };
39
+ }
40
+ function f(o, e, l, s, n) {
41
+ if (o.exists(e)) return e;
42
+ const b = document.createElement("canvas");
43
+ b.width = l, b.height = s;
44
+ const _ = b.getContext("2d");
45
+ for (const [p, h, y] of n)
46
+ _.fillStyle = y, _.fillRect(p, h, 1, 1);
47
+ return o.addCanvas(e, b), e;
48
+ }
49
+ function R(o, e = {}, l = "life_") {
50
+ const s = { ...F, ...e }, n = (c) => l + c, b = s.leaves.map(([c, t], i) => [
51
+ f(o, n(`leaf_${i}_0`), 2, 1, [[0, 0, c], [1, 0, t]]),
52
+ f(o, n(`leaf_${i}_1`), 2, 2, [[0, 0, c], [1, 1, t]]),
53
+ f(o, n(`leaf_${i}_2`), 1, 2, [[0, 0, c], [0, 1, t]])
54
+ ]), _ = [
55
+ [f(o, n("seed_0"), 1, 1, [[0, 0, s.seed[0]]])],
56
+ [f(o, n("seed_1"), 2, 1, [[0, 0, s.seed[0]], [1, 0, s.seed[1]]])]
57
+ ], p = [s.ember.map((c, t) => f(o, n(`ember_${t}`), 1, 1, [[0, 0, c]]))], h = s.mote.map((c, t) => [f(o, n(`mote_${t}`), 1, 1, [[0, 0, c]])]), y = s.ash.map(
58
+ ([c, t], i) => (
59
+ // the first flake flat, the next tumbling on its corner
60
+ [f(o, n(`ash_${i}`), 2, i % 2 ? 2 : 1, i % 2 ? [[0, 0, c], [1, 1, t]] : [[0, 0, c], [1, 0, t]])]
61
+ )
62
+ ), u = s.butterflyBody, S = s.butterflies.map(([c, t], i) => [
63
+ f(o, n(`fly_${i}_o`), 3, 2, [[0, 0, c], [2, 0, c], [1, 0, u], [0, 1, t], [1, 1, u], [2, 1, t]]),
64
+ f(o, n(`fly_${i}_c`), 3, 2, [[1, 0, c], [1, 1, t]])
65
+ ]), a = s.bird, r = [
66
+ f(o, n("bird_0"), 5, 2, [[0, 0, a], [4, 0, a], [1, 1, a], [2, 1, a], [3, 1, a]]),
67
+ f(o, n("bird_1"), 5, 2, [[0, 1, a], [1, 0, a], [2, 0, a], [3, 0, a], [4, 1, a]])
68
+ ], [$, m] = s.birdShadow, g = f(o, n("bird_shadow"), 3, 1, [[0, 0, m], [1, 0, $], [2, 0, m]]), [w, d] = s.firefly, A = [
69
+ f(o, n("firefly_dim"), 3, 3, [[1, 1, d]]),
70
+ f(o, n("firefly_lit"), 3, 3, [[1, 1, w], [0, 1, d], [2, 1, d], [1, 0, d], [1, 2, d]])
71
+ ], C = [];
72
+ for (let c = 0; c < v; c++) {
73
+ const { w: t, h: i, px: E } = M(c, s.smoke);
74
+ C.push(f(o, n(`puff_${c}`), t, i, E));
75
+ }
76
+ return { leaves: b, seeds: _, embers: p, motes: h, ash: y, butterflies: S, bird: r, birdShadow: g, firefly: A, puffs: C };
77
+ }
78
+ export {
79
+ F as LIFE_COLORS,
80
+ v as PUFF_FRAMES,
81
+ R as paintLifeArt,
82
+ M as puffPixels
83
+ };
package/dist/pixel.js CHANGED
@@ -1,96 +1,101 @@
1
1
  import { PixelLayer as i, addPixelLayer as a } from "./pixel/PixelLayer.js";
2
- import { CLEAR as s, PixelGrid as x, PixelWindow as m, channels as l, hash2 as f, hex as h, makeSheet as n, mix as P, mulberry32 as d, rgba as R, shade as g, toCss as A, upscale as L, valueNoise as u, withAlpha as E } from "./pixel/pixel.js";
3
- import { P as T } from "./pixel/palette.js";
4
- import { art as M, groundShadow as O, litOutline as _, shadeBox as v } from "./pixel/propKit.js";
5
- import { MATERIALS as k, RAMP_FACTOR_SET as w, hueShade as y, outlineOf as C, ramp as W, rampTones as H, registerMaterial as N } from "./pixel/ramp.js";
6
- import { selectiveOutline as D } from "./pixel/selout.js";
2
+ import { CLEAR as x, PixelGrid as s, PixelWindow as m, channels as l, hash2 as f, hex as h, makeSheet as n, mix as P, mulberry32 as R, rgba as L, shade as d, toCss as A, upscale as g, valueNoise as E, withAlpha as S } from "./pixel/pixel.js";
3
+ import { P as M } from "./pixel/palette.js";
4
+ import { art as T, groundShadow as _, litOutline as c, shadeBox as v } from "./pixel/propKit.js";
5
+ import { MATERIALS as C, RAMP_FACTOR_SET as F, hueShade as k, outlineOf as w, ramp as y, rampTones as W, registerMaterial as H } from "./pixel/ramp.js";
6
+ import { selectiveOutline as b } from "./pixel/selout.js";
7
7
  import { chevronPixels as G, discRuns as V, ellipsePixels as q, ringPixels as B } from "./pixel/aimPixels.js";
8
- import { PixelMarks as j } from "./pixel/PixelMarks.js";
8
+ import { PixelMarks as X } from "./pixel/PixelMarks.js";
9
9
  import * as e from "./pixel/pixelPaint.js";
10
10
  import * as r from "./pixel/pixelTone.js";
11
- import { registerHandRamps as J } from "./pixel/pixelTone.js";
12
- import { paintLayer as Q } from "./pixel/paintLayer.js";
11
+ import { registerHandRamps as z } from "./pixel/pixelTone.js";
12
+ import { paintLayer as K } from "./pixel/paintLayer.js";
13
13
  import { Evaporation as Y } from "./pixel/Evaporation.js";
14
14
  import { NO_PIXELS as $, PackedPixels as ee, spritePixels as re } from "./pixel/spritePixels.js";
15
15
  import { CARRIED_LIGHT as oe, Darkness as ie, LAMP_DIM as ae, WARM_LIGHT as pe } from "./pixel/Darkness.js";
16
- import { carriedLight as xe, clearLights as me, flashLight as le, liveLights as fe } from "./pixel/lights.js";
16
+ import { carriedLight as se, clearLights as me, flashLight as le, liveLights as fe } from "./pixel/lights.js";
17
17
  import { NightShade as ne } from "./pixel/NightShade.js";
18
- import { WEATHER_COLORS as de, mistPixels as Re, paintWeatherArt as ge } from "./pixel/weatherArt.js";
19
- import { buildRig as Le, complement as ue, gustAt as Ee, swayOffset as Se, tileHash as Te, trunkPivot as ce } from "./pixel/swayRig.js";
20
- import { SwayProp as Oe, opaqueRowWidths as _e } from "./pixel/SwayProp.js";
21
- import { heartbeat as Ie, vignetteMask as ke } from "./pixel/vignetteMask.js";
22
- import { DitherVignette as ye, ditherVignette as Ce } from "./pixel/DitherVignette.js";
23
- import { FRAME_CORNERS as He, FRAME_TWINS as Ne, frameSvg as be, nineSlice as De } from "./pixel/nineSlice.js";
18
+ import { LIFE_COLORS as Re, PUFF_FRAMES as Le, paintLifeArt as de, puffPixels as Ae } from "./pixel/lifeArt.js";
19
+ import { WEATHER_COLORS as Ee, mistPixels as Se, paintWeatherArt as ue } from "./pixel/weatherArt.js";
20
+ import { buildRig as Oe, complement as Te, gustAt as _e, swayOffset as ce, tileHash as ve, trunkPivot as Ie } from "./pixel/swayRig.js";
21
+ import { SwayProp as Fe, opaqueRowWidths as ke } from "./pixel/SwayProp.js";
22
+ import { heartbeat as ye, vignetteMask as We } from "./pixel/vignetteMask.js";
23
+ import { DitherVignette as Ne, ditherVignette as be } from "./pixel/DitherVignette.js";
24
+ import { FRAME_CORNERS as Ge, FRAME_TWINS as Ve, frameSvg as qe, nineSlice as Be } from "./pixel/nineSlice.js";
24
25
  export {
25
26
  oe as CARRIED_LIGHT,
26
- s as CLEAR,
27
+ x as CLEAR,
27
28
  ie as Darkness,
28
- ye as DitherVignette,
29
+ Ne as DitherVignette,
29
30
  Y as Evaporation,
30
- He as FRAME_CORNERS,
31
- Ne as FRAME_TWINS,
31
+ Ge as FRAME_CORNERS,
32
+ Ve as FRAME_TWINS,
32
33
  ae as LAMP_DIM,
33
- k as MATERIALS,
34
+ Re as LIFE_COLORS,
35
+ C as MATERIALS,
34
36
  $ as NO_PIXELS,
35
37
  ne as NightShade,
36
- T as PALETTE,
38
+ M as PALETTE,
39
+ Le as PUFF_FRAMES,
37
40
  ee as PackedPixels,
38
- x as PixelGrid,
41
+ s as PixelGrid,
39
42
  i as PixelLayer,
40
- j as PixelMarks,
43
+ X as PixelMarks,
41
44
  m as PixelWindow,
42
- w as RAMP_FACTOR_SET,
43
- Oe as SwayProp,
45
+ F as RAMP_FACTOR_SET,
46
+ Fe as SwayProp,
44
47
  pe as WARM_LIGHT,
45
- de as WEATHER_COLORS,
48
+ Ee as WEATHER_COLORS,
46
49
  a as addPixelLayer,
47
- M as art,
48
- Le as buildRig,
49
- xe as carriedLight,
50
+ T as art,
51
+ Oe as buildRig,
52
+ se as carriedLight,
50
53
  l as channels,
51
54
  G as chevronPixels,
52
55
  me as clearLights,
53
- ue as complement,
56
+ Te as complement,
54
57
  V as discRuns,
55
- Ce as ditherVignette,
58
+ be as ditherVignette,
56
59
  q as ellipsePixels,
57
60
  le as flashLight,
58
- be as frameSvg,
59
- O as groundShadow,
60
- Ee as gustAt,
61
+ qe as frameSvg,
62
+ _ as groundShadow,
63
+ _e as gustAt,
61
64
  f as hash2,
62
- Ie as heartbeat,
65
+ ye as heartbeat,
63
66
  h as hex,
64
- y as hueShade,
65
- _ as litOutline,
67
+ k as hueShade,
68
+ c as litOutline,
66
69
  fe as liveLights,
67
70
  n as makeSheet,
68
- Re as mistPixels,
71
+ Se as mistPixels,
69
72
  P as mix,
70
- d as mulberry32,
71
- De as nineSlice,
72
- _e as opaqueRowWidths,
73
- C as outlineOf,
74
- Q as paintLayer,
75
- ge as paintWeatherArt,
73
+ R as mulberry32,
74
+ Be as nineSlice,
75
+ ke as opaqueRowWidths,
76
+ w as outlineOf,
77
+ K as paintLayer,
78
+ de as paintLifeArt,
79
+ ue as paintWeatherArt,
76
80
  e as pixelPaint,
77
81
  r as pixelTone,
78
- W as ramp,
79
- H as rampTones,
80
- J as registerHandRamps,
81
- N as registerMaterial,
82
- R as rgba,
82
+ Ae as puffPixels,
83
+ y as ramp,
84
+ W as rampTones,
85
+ z as registerHandRamps,
86
+ H as registerMaterial,
87
+ L as rgba,
83
88
  B as ringPixels,
84
- D as selectiveOutline,
85
- g as shade,
89
+ b as selectiveOutline,
90
+ d as shade,
86
91
  v as shadeBox,
87
92
  re as spritePixels,
88
- Se as swayOffset,
89
- Te as tileHash,
93
+ ce as swayOffset,
94
+ ve as tileHash,
90
95
  A as toCss,
91
- ce as trunkPivot,
92
- L as upscale,
93
- u as valueNoise,
94
- ke as vignetteMask,
95
- E as withAlpha
96
+ Ie as trunkPivot,
97
+ g as upscale,
98
+ E as valueNoise,
99
+ We as vignetteMask,
100
+ S as withAlpha
96
101
  };
@@ -0,0 +1,24 @@
1
+ import { jsx as g } from "react/jsx-runtime";
2
+ import { useState as A } from "react";
3
+ import { useCx as C, useUi as L } from "./UiProvider.js";
4
+ import { DEFAULT_LABELS as h } from "./labels.js";
5
+ function U({ armKey: n = "armed", armed: s, setArmed: t, onConfirm: c, label: e, armedLabel: l, title: u, className: m = "small", disabled: a }) {
6
+ const f = C(), p = L(), [b, d] = A(null), r = t !== void 0, x = r ? s ?? null : b, i = r ? t : d, o = x === n;
7
+ return /* @__PURE__ */ g(
8
+ "button",
9
+ {
10
+ type: "button",
11
+ className: f("e1681-button", m, o ? "e1681-armed primary" : "ghost"),
12
+ disabled: a,
13
+ onClick: () => {
14
+ if (!o) return i(n);
15
+ c(), i(null);
16
+ },
17
+ title: u,
18
+ children: o ? l ?? p.labels.confirmAgain ?? h.confirmAgain : e
19
+ }
20
+ );
21
+ }
22
+ export {
23
+ U as ConfirmButton
24
+ };
@@ -86,6 +86,7 @@ const o = `
86
86
  .e1681-button { padding: 2px 10px; cursor: pointer; min-height: 28px; }
87
87
  .e1681-button.small { padding: 0 6px; min-height: 22px; }
88
88
  .e1681-button.ghost { background-color: transparent; }
89
+ .e1681-button.e1681-armed { color: var(--e1681-accent); }
89
90
  .e1681-button:hover:not(:disabled) { color: var(--e1681-accent); }
90
91
  /* pressed: the same face a pixel lower, never a transform */
91
92
  .e1681-button:active:not(:disabled) { padding-top: 3px; padding-bottom: 1px; }
@@ -1,4 +1,4 @@
1
- const n = {
1
+ const c = {
2
2
  close: "Close {0}",
3
3
  closeHint: "Close (Esc)",
4
4
  backToList: "‹ List",
@@ -6,12 +6,13 @@ const n = {
6
6
  cancel: "Cancel",
7
7
  ok: "OK",
8
8
  quantity: "Quantity",
9
- choose: "Choose"
9
+ choose: "Choose",
10
+ confirmAgain: "Sure?"
10
11
  };
11
- function l(e, ...t) {
12
- return e.replace(/\{(\d+)\}/g, (o, c) => t[Number(c)] ?? o);
12
+ function i(e, ...n) {
13
+ return e.replace(/\{(\d+)\}/g, (o, t) => n[Number(t)] ?? o);
13
14
  }
14
15
  export {
15
- n as DEFAULT_LABELS,
16
- l as fillLabel
16
+ c as DEFAULT_LABELS,
17
+ i as fillLabel
17
18
  };
@@ -0,0 +1,13 @@
1
+ import { useMemo as n } from "react";
2
+ import { useStore as s } from "./useStore.js";
3
+ function u(o, r) {
4
+ const e = o((t) => JSON.stringify(r(t)) ?? "null");
5
+ return n(() => JSON.parse(e), [e]);
6
+ }
7
+ function m(o, r) {
8
+ return u((e) => s(o, e), r);
9
+ }
10
+ export {
11
+ u as useJsonSelector,
12
+ m as useStoreJson
13
+ };