@minicode-dev/chat 0.1.14 → 0.1.16

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 (66) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/chunks/{BattleshipGameCard-B-B4r7lK.js → BattleshipGameCard-DwNzgH5G.js} +199 -199
  3. package/dist/chunks/{BattleshipGameCard-B-B4r7lK.js.map → BattleshipGameCard-DwNzgH5G.js.map} +1 -1
  4. package/dist/chunks/{BattleshipPrototype-CmQd5AUO.js → BattleshipPrototype-CSQxQJt5.js} +52 -52
  5. package/dist/chunks/{BattleshipPrototype-CmQd5AUO.js.map → BattleshipPrototype-CSQxQJt5.js.map} +1 -1
  6. package/dist/chunks/{ChatContext-DQtKsR6Z.js → ChatContext-D5rqvt5D.js} +2 -2
  7. package/dist/chunks/{ChatContext-DQtKsR6Z.js.map → ChatContext-D5rqvt5D.js.map} +1 -1
  8. package/dist/chunks/RacingLiveGameCard-_wvhDRlG.js +1873 -0
  9. package/dist/chunks/RacingLiveGameCard-_wvhDRlG.js.map +1 -0
  10. package/dist/chunks/{TanksGameCard-CvTaF7tu.js → TanksGameCard-B-gtDYYg.js} +144 -144
  11. package/dist/chunks/{TanksGameCard-CvTaF7tu.js.map → TanksGameCard-B-gtDYYg.js.map} +1 -1
  12. package/dist/chunks/TanksLiveGameCard-DXgd1J2Y.js +1274 -0
  13. package/dist/chunks/TanksLiveGameCard-DXgd1J2Y.js.map +1 -0
  14. package/dist/chunks/{avalancheScene3d-Z5QyZIid.js → avalancheScene3d-CNbQyGUd.js} +51 -50
  15. package/dist/chunks/{avalancheScene3d-Z5QyZIid.js.map → avalancheScene3d-CNbQyGUd.js.map} +1 -1
  16. package/dist/chunks/cars-D1PM7mVD.js +340 -0
  17. package/dist/chunks/cars-D1PM7mVD.js.map +1 -0
  18. package/dist/chunks/{connect4Sounds-DmD-E9kX.js → connect4Sounds-3a6h7gUC.js} +8 -8
  19. package/dist/chunks/{connect4Sounds-DmD-E9kX.js.map → connect4Sounds-3a6h7gUC.js.map} +1 -1
  20. package/dist/chunks/createLiveScene-jrzyQogh.js +756 -0
  21. package/dist/chunks/createLiveScene-jrzyQogh.js.map +1 -0
  22. package/dist/chunks/createRaceScene-C-aw0dlX.js +291 -0
  23. package/dist/chunks/createRaceScene-C-aw0dlX.js.map +1 -0
  24. package/dist/chunks/{createTankScene-yhX6la27.js → createTankScene-6asWHKFf.js} +123 -125
  25. package/dist/chunks/{createTankScene-yhX6la27.js.map → createTankScene-6asWHKFf.js.map} +1 -1
  26. package/dist/chunks/fx-BNJvJYOu.js +871 -0
  27. package/dist/chunks/fx-BNJvJYOu.js.map +1 -0
  28. package/dist/chunks/fx-DhXerGyt.js +248 -0
  29. package/dist/chunks/fx-DhXerGyt.js.map +1 -0
  30. package/dist/chunks/{games-BvYTWgaR.js → games-D_9SiSBT.js} +1261 -677
  31. package/dist/chunks/games-D_9SiSBT.js.map +1 -0
  32. package/dist/chunks/tank-DIn6Jyt8.js +605 -0
  33. package/dist/chunks/tank-DIn6Jyt8.js.map +1 -0
  34. package/dist/chunks/{tanks-live-BcKmrhnV.js → tanks-live-BRhYW4MG.js} +36 -36
  35. package/dist/chunks/{tanks-live-BcKmrhnV.js.map → tanks-live-BRhYW4MG.js.map} +1 -1
  36. package/dist/chunks/{tanksSounds-DwjfSp3W.js → tanksSounds-tYPzje61.js} +5 -5
  37. package/dist/chunks/{tanksSounds-DwjfSp3W.js.map → tanksSounds-tYPzje61.js.map} +1 -1
  38. package/dist/chunks/track-C1TWWT19.js +557 -0
  39. package/dist/chunks/track-C1TWWT19.js.map +1 -0
  40. package/dist/index.js +4244 -4028
  41. package/dist/index.js.map +1 -1
  42. package/dist/styles.css +1 -1
  43. package/index.d.ts +2 -0
  44. package/package.json +1 -1
  45. package/dist/chunks/RacingLiveGameCard-c1G7uoDI.js +0 -1654
  46. package/dist/chunks/RacingLiveGameCard-c1G7uoDI.js.map +0 -1
  47. package/dist/chunks/TanksLiveGameCard-COr9Oay5.js +0 -1011
  48. package/dist/chunks/TanksLiveGameCard-COr9Oay5.js.map +0 -1
  49. package/dist/chunks/createLiveScene-CWBunxPi.js +0 -367
  50. package/dist/chunks/createLiveScene-CWBunxPi.js.map +0 -1
  51. package/dist/chunks/createRaceScene-C7Rw89kj.js +0 -305
  52. package/dist/chunks/createRaceScene-C7Rw89kj.js.map +0 -1
  53. package/dist/chunks/decor-CyyCnmct.js +0 -418
  54. package/dist/chunks/decor-CyyCnmct.js.map +0 -1
  55. package/dist/chunks/fx-1ZwzmPWj.js +0 -458
  56. package/dist/chunks/fx-1ZwzmPWj.js.map +0 -1
  57. package/dist/chunks/fx-UZwhOmdl.js +0 -584
  58. package/dist/chunks/fx-UZwhOmdl.js.map +0 -1
  59. package/dist/chunks/games-BvYTWgaR.js.map +0 -1
  60. package/dist/chunks/rolldown-runtime-CGnMp29N.js +0 -10
  61. package/dist/chunks/tank-DjEQ4FXF.js +0 -553
  62. package/dist/chunks/tank-DjEQ4FXF.js.map +0 -1
  63. package/dist/chunks/track-BcnnpnAm.js +0 -259
  64. package/dist/chunks/track-BcnnpnAm.js.map +0 -1
  65. package/dist/chunks/util-DwI9bfS7.js +0 -57
  66. package/dist/chunks/util-DwI9bfS7.js.map +0 -1
@@ -0,0 +1,340 @@
1
+ import { Box3 as e, BoxGeometry as t, CircleGeometry as n, Color as r, CylinderGeometry as i, ExtrudeGeometry as a, Float32BufferAttribute as o, MeshBasicMaterial as s, MeshLambertMaterial as c, MeshStandardMaterial as l, Shape as u, SphereGeometry as d, Vector3 as f } from "three";
2
+ import { mergeBufferGeometries as p } from "three/examples/jsm/utils/BufferGeometryUtils.js";
3
+ // src/effects/avalanche/models/cars.js
4
+ var m = [
5
+ "#ff2b3d",
6
+ "#ffd23f",
7
+ "#2f6bff",
8
+ "#7be22c",
9
+ "#ff7a1a",
10
+ "#ff4fb0",
11
+ "#f5f6f8"
12
+ ], h = {
13
+ tire: "#1b1c21",
14
+ rim: "#cfd5de",
15
+ hub: "#3a3d45",
16
+ chrome: "#dfe4ec",
17
+ dark: "#2a2c33",
18
+ black: "#111216",
19
+ white: "#f4f4f6",
20
+ head: "#fff2bf",
21
+ tail: "#ff1f3a"
22
+ }, g = [
23
+ "body",
24
+ "glass",
25
+ "wheels",
26
+ "trim",
27
+ "lights"
28
+ ];
29
+ function _(e) {
30
+ let t = e.index ? e.toNonIndexed() : e;
31
+ return t.deleteAttribute("uv"), t;
32
+ }
33
+ function v(e, t) {
34
+ let n = _(e), i = new r(t), a = new Float32Array(n.attributes.position.count * 3);
35
+ for (let e = 0; e < a.length; e += 3) i.toArray(a, e);
36
+ return n.setAttribute("color", new o(a, 3)), n;
37
+ }
38
+ function y(e, t, n, r, i = r.bevel) {
39
+ let o = Math.max(.01, t - n * 2);
40
+ return new a(e, {
41
+ depth: o,
42
+ bevelEnabled: !0,
43
+ bevelThickness: n,
44
+ bevelSize: n,
45
+ bevelSegments: i,
46
+ curveSegments: r.curve
47
+ }).translate(0, 0, -o / 2);
48
+ }
49
+ function b(e, n, r, i, a, o, s, c = .025) {
50
+ if (e.low || !c || c * 2.5 > Math.min(n, r, i)) return new t(n, r, i).translate(a, o, s);
51
+ let l = n / 2 - c, d = r / 2 - c, f = new u();
52
+ return f.moveTo(-l, -d), f.lineTo(l, -d), f.lineTo(l, d), f.lineTo(-l, d), y(f, i, c, e, 1).translate(a, o, s);
53
+ }
54
+ function x(e, t, n, r, a, o, s) {
55
+ let c = new i(e, e, t, n);
56
+ return r === "x" ? c.rotateZ(Math.PI / 2) : r === "z" && c.rotateX(Math.PI / 2), c.translate(a, o, s);
57
+ }
58
+ function S(e) {
59
+ let t = new u();
60
+ return e.forEach((e, n) => {
61
+ if (e.arch) {
62
+ let [n, r, i, a] = e.arch, o = Math.asin((r - a) / i);
63
+ t.absarc(n, r, i, Math.PI + o, -o, !0);
64
+ } else e.q ? t.quadraticCurveTo(...e.q) : n === 0 ? t.moveTo(...e) : t.lineTo(...e);
65
+ }), t;
66
+ }
67
+ function C(e, t, r) {
68
+ r.forEach(({ x: r, r: i, w: a, z: o }) => {
69
+ [-1, 1].forEach((s) => {
70
+ let c = s * o;
71
+ t.wheels.push(v(x(i, a, e.radial, "z", r, i, c), h.tire));
72
+ let l = [[
73
+ i * .6,
74
+ e.radial,
75
+ h.rim,
76
+ .004
77
+ ]];
78
+ e.low || l.push([
79
+ i * .2,
80
+ 6,
81
+ h.hub,
82
+ .008
83
+ ]), l.forEach(([e, o, l, u]) => {
84
+ let d = new n(e, o);
85
+ s < 0 && d.rotateY(Math.PI), t.wheels.push(v(d.translate(r, i, c + s * (a / 2 + u)), l));
86
+ });
87
+ });
88
+ });
89
+ }
90
+ function w(e) {
91
+ let t = {
92
+ body: [],
93
+ glass: [],
94
+ wheels: [],
95
+ trim: [],
96
+ lights: []
97
+ };
98
+ return t.body.push(y(S([
99
+ [-1.06, .16],
100
+ { arch: [
101
+ -.72,
102
+ .22,
103
+ .31,
104
+ .16
105
+ ] },
106
+ { arch: [
107
+ .76,
108
+ .22,
109
+ .31,
110
+ .16
111
+ ] },
112
+ [1.1, .16],
113
+ { q: [
114
+ 1.15,
115
+ .17,
116
+ 1.15,
117
+ .26
118
+ ] },
119
+ { q: [
120
+ 1.14,
121
+ .38,
122
+ 1,
123
+ .4
124
+ ] },
125
+ [.42, .46],
126
+ [-.7, .48],
127
+ { q: [
128
+ -1.06,
129
+ .49,
130
+ -1.1,
131
+ .38
132
+ ] },
133
+ [-1.11, .22],
134
+ { q: [
135
+ -1.11,
136
+ .16,
137
+ -1.06,
138
+ .16
139
+ ] }
140
+ ]), .9, .05, e)), t.glass.push(y(S([
141
+ [.4, .46],
142
+ [.02, .74],
143
+ [-.42, .74],
144
+ [-.86, .47]
145
+ ]), .76, .03, e)), t.body.push(b(e, .5, .06, .8, -.2, .79, 0, .02)), t.body.push(b(e, .16, .05, .86, -1.05, .6, 0, .02)), e.low || [-1, 1].forEach((n) => t.body.push(b(e, .08, .05, .07, .32, .54, n * .475, 0))), C(e, t, [{
146
+ x: .76,
147
+ r: .22,
148
+ w: .2,
149
+ z: .4
150
+ }, {
151
+ x: -.72,
152
+ r: .22,
153
+ w: .2,
154
+ z: .4
155
+ }]), t.trim.push(v(b(e, .1, .1, .92, 1.17, .17, 0, .03), h.dark), v(b(e, .1, .1, .92, -1.15, .18, 0, .03), h.dark), v(b(e, .04, .07, .44, 1.2, .27, 0, 0), h.black)), [-1, 1].forEach((n) => {
156
+ t.trim.push(v(b(e, .05, .12, .05, -1.03, .53, n * .3, 0), h.dark)), e.low || t.trim.push(v(x(.035, .1, 8, "x", -1.17, .1, n * .22), h.chrome)), t.lights.push(v(b(e, .06, .06, .2, 1.18, .35, n * .29, 0), h.head), v(b(e, .04, .06, .24, -1.17, .36, n * .27, 0), h.tail));
157
+ }), t;
158
+ }
159
+ function T(e) {
160
+ let t = {
161
+ body: [],
162
+ glass: [],
163
+ wheels: [],
164
+ trim: [],
165
+ lights: []
166
+ };
167
+ return t.body.push(y(S([
168
+ [-1.12, .17],
169
+ { arch: [
170
+ -.74,
171
+ .25,
172
+ .34,
173
+ .17
174
+ ] },
175
+ { arch: [
176
+ .8,
177
+ .22,
178
+ .31,
179
+ .17
180
+ ] },
181
+ [1.15, .17],
182
+ [1.18, .21],
183
+ [1.18, .44],
184
+ [1.13, .5],
185
+ [-1.1, .5],
186
+ [-1.15, .46],
187
+ [-1.15, .22]
188
+ ]), .96, .05, e)), t.glass.push(y(S([
189
+ [.28, .47],
190
+ [-.02, .76],
191
+ [-.58, .76],
192
+ [-.8, .47]
193
+ ]), .8, .03, e)), t.body.push(b(e, .62, .06, .86, -.3, .8, 0, .02), b(e, .34, .08, .3, .62, .58, 0, .025), b(e, .2, .05, 1, -1.04, .75, 0, .02)), C(e, t, [{
194
+ x: .8,
195
+ r: .22,
196
+ w: .2,
197
+ z: .4
198
+ }, {
199
+ x: -.74,
200
+ r: .25,
201
+ w: .26,
202
+ z: .39
203
+ }]), t.trim.push(v(b(e, .06, .08, 1, 1.23, .2, 0, .02), h.chrome), v(b(e, .06, .08, 1, -1.23, .2, 0, .02), h.chrome), v(b(e, .03, .16, .62, 1.235, .33, 0, 0), h.black), v(b(e, .03, .05, .22, .79, .59, 0, 0), h.black)), [-1, 1].forEach((n) => {
204
+ t.trim.push(v(b(e, .82, .012, .08, .71, .553, n * .08, 0), h.black), v(b(e, .6, .012, .08, -.3, .833, n * .08, 0), h.black), v(b(e, .3, .012, .08, -.98, .553, n * .08, 0), h.black), v(b(e, .06, .18, .06, -1.04, .64, n * .34, 0), h.dark)), e.low || t.trim.push(v(x(.04, .12, 8, "x", -1.2, .12, n * .3), h.chrome)), t.lights.push(v(x(.065, .03, e.low ? 6 : 10, "x", 1.245, .34, n * .37), h.head), v(b(e, .03, .07, .3, -1.215, .4, n * .26, 0), h.tail));
205
+ }), t;
206
+ }
207
+ function E(e) {
208
+ let t = {
209
+ body: [],
210
+ glass: [],
211
+ wheels: [],
212
+ trim: [],
213
+ lights: []
214
+ }, n = y(S([
215
+ [-.96, .12],
216
+ [1.14, .12],
217
+ [1.14, .17],
218
+ [.55, .3],
219
+ [.2, .36],
220
+ [.14, .4],
221
+ [-.16, .4],
222
+ [-.24, .54],
223
+ [-.4, .56],
224
+ [-.96, .3]
225
+ ]), .44, .04, e), r = n.attributes.position;
226
+ for (let e = 0; e < r.count; e += 1) {
227
+ let t = r.getX(e), n = t > .35 ? 1 - .55 * Math.min(1, (t - .35) / .85) : t < -.5 ? 1 - .3 * Math.min(1, (-.5 - t) / .5) : 1;
228
+ r.setZ(e, r.getZ(e) * n);
229
+ }
230
+ return n.computeVertexNormals(), t.body.push(n, b(e, .6, .2, .76, -.12, .24, 0, .05), b(e, .22, .04, 1, 1.12, .07, 0, .015), b(e, .24, .05, .84, -.98, .71, 0, .02)), t.glass.push(b(e, .04, .045, .15, .08, .49, 0, 0)), C(e, t, [{
231
+ x: .82,
232
+ r: .2,
233
+ w: .17,
234
+ z: .415
235
+ }, {
236
+ x: -.74,
237
+ r: .24,
238
+ w: .24,
239
+ z: .38
240
+ }]), t.trim.push(v(new d(.12, e.low ? 8 : 10, e.low ? 6 : 8).translate(-.03, .47, 0), h.white), v(b(e, .06, .36, .05, -.94, .52, 0, 0), h.dark), v(b(e, .03, .06, .08, -.25, .56, 0, 0), h.black)), [-1, 1].forEach((n) => {
241
+ t.trim.push(v(b(e, .24, .12, .025, 1.14, .1, n * .5, 0), h.dark), v(b(e, .3, .24, .025, -.98, .64, n * .43, 0), h.dark), v(b(e, .04, .03, .2, .82, .2, n * .24, 0), h.dark), v(b(e, .04, .03, .1, -.74, .24, n * .23, 0), h.dark));
242
+ }), e.low || t.trim.push(v(x(.03, .08, 8, "x", -1, .28, 0), h.chrome)), t.lights.push(v(b(e, .03, .06, .1, -1.015, .2, 0, 0), h.tail)), t;
243
+ }
244
+ function D(e) {
245
+ let t = (t, n) => e ? new c(t) : new l({
246
+ ...t,
247
+ ...n
248
+ });
249
+ return {
250
+ body: t({ color: 16777215 }, {
251
+ roughness: .28,
252
+ metalness: .3
253
+ }),
254
+ glass: t({ color: 1844790 }, {
255
+ roughness: .12,
256
+ metalness: .6
257
+ }),
258
+ wheels: t({
259
+ color: 16777215,
260
+ vertexColors: !0
261
+ }, {
262
+ roughness: .75,
263
+ metalness: .15
264
+ }),
265
+ trim: t({
266
+ color: 16777215,
267
+ vertexColors: !0
268
+ }, {
269
+ roughness: .35,
270
+ metalness: .55
271
+ }),
272
+ lights: new s({
273
+ color: 16777215,
274
+ vertexColors: !0,
275
+ toneMapped: !1
276
+ })
277
+ };
278
+ }
279
+ function O(t, n, r, i, a) {
280
+ let o = g.filter((e) => n[e].length).map((e) => {
281
+ let r = p(n[e].map(_));
282
+ if (!r) throw Error(`avalanche cars: cannot merge ${t}.${e}`);
283
+ return r.computeBoundingBox(), {
284
+ name: e,
285
+ geometry: r
286
+ };
287
+ }), s = new e();
288
+ o.forEach(({ geometry: e }) => s.union(e.boundingBox));
289
+ let c = s.getCenter(new f()), l = s.getSize(new f());
290
+ return o.forEach(({ geometry: e }) => {
291
+ e.translate(-c.x, -c.y, -c.z), e.computeBoundingSphere();
292
+ }), {
293
+ kind: t,
294
+ parts: o.map(({ name: e, geometry: t }) => ({
295
+ geometry: t,
296
+ material: r[e],
297
+ tinted: e === "body"
298
+ })),
299
+ palette: i,
300
+ radius: Math.cbrt(l.x * l.y * l.z * 3 / (4 * Math.PI)),
301
+ ...a
302
+ };
303
+ }
304
+ function k({ lowPower: e = !1 } = {}) {
305
+ let t = {
306
+ low: e,
307
+ curve: e ? 3 : 5,
308
+ bevel: e ? 1 : 2,
309
+ radial: e ? 8 : 14
310
+ }, n = D(e), i = m.map((e) => new r(e)), a = {
311
+ count: 10,
312
+ countLow: 6
313
+ };
314
+ return [
315
+ O("car", w(t), n, i, {
316
+ mass: 3,
317
+ bounce: .35,
318
+ friction: .6,
319
+ drag: .04,
320
+ ...a
321
+ }),
322
+ O("car-muscle", T(t), n, i, {
323
+ mass: 3.4,
324
+ bounce: .3,
325
+ friction: .62,
326
+ drag: .04,
327
+ ...a
328
+ }),
329
+ O("car-racer", E(t), n, i, {
330
+ mass: 2.6,
331
+ bounce: .4,
332
+ friction: .55,
333
+ drag: .05,
334
+ ...a
335
+ })
336
+ ];
337
+ }
338
+ export { k as t };
339
+
340
+ //# sourceMappingURL=cars-D1PM7mVD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cars-D1PM7mVD.js","names":["PALETTE","C","PART_ORDER","prep","flat","geometry","paint","color","Color","hex","array","i","Float32BufferAttribute","extrude","q","depth","width","bevel","ExtrudeGeometry","shape","segments","box","w","h","d","BoxGeometry","x","y","z","hw","hh","Shape","cylinder","CylinderGeometry","r","length","axis","profile","points","point","cx","cy","bottom","tilt","addWheels","zc","side","discs","radius","offset","disc","CircleGeometry","parts","coupe","s","muscle","racer","tub","position","taper","SphereGeometry","createMaterials","make","lowPower","MeshLambertMaterial","options","MeshStandardMaterial","standard","MeshBasicMaterial","assemble","merged","name","mergeBufferGeometries","kind","bounds","Box3","center","Vector3","size","materials","physics","createCarModels","palette","pools"],"sources":["../../src/effects/avalanche/models/cars.js"],"sourcesContent":["// Low-poly toy sports cars for the Avalancha effect: 'car' (coupe), 'car-muscle', 'car-racer'.\n// Each kind is a few merged parts (one InstancedMesh each); only the body takes the palette tint.\n// Local frame: origin at the bbox center, +X front, +Y up, +Z right. No DOM at module scope.\nimport {\n Box3, BoxGeometry, CircleGeometry, Color, CylinderGeometry, ExtrudeGeometry, Float32BufferAttribute,\n MeshBasicMaterial, MeshLambertMaterial, MeshStandardMaterial, Shape, SphereGeometry, Vector3,\n} from 'three';\nimport { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';\n\nconst PALETTE = ['#ff2b3d', '#ffd23f', '#2f6bff', '#7be22c', '#ff7a1a', '#ff4fb0', '#f5f6f8'];\nconst C = {\n tire: '#1b1c21', rim: '#cfd5de', hub: '#3a3d45', chrome: '#dfe4ec', dark: '#2a2c33', black: '#111216',\n white: '#f4f4f6', head: '#fff2bf', tail: '#ff1f3a',\n};\nconst PART_ORDER = ['body', 'glass', 'wheels', 'trim', 'lights'];\n\n// Non-indexed, uv-less: the common layout mergeBufferGeometries needs.\nfunction prep(geometry) {\n const flat = geometry.index ? geometry.toNonIndexed() : geometry;\n flat.deleteAttribute('uv');\n return flat;\n}\n\nfunction paint(geometry, hex) {\n const flat = prep(geometry);\n const color = new Color(hex);\n const array = new Float32Array(flat.attributes.position.count * 3);\n for (let i = 0; i < array.length; i += 3) color.toArray(array, i);\n flat.setAttribute('color', new Float32BufferAttribute(array, 3));\n return flat;\n}\n\n// Side profile (XY) extruded across Z, centered on z = 0. The bevel grows the outline by `bevel`.\nfunction extrude(shape, width, bevel, q, segments = q.bevel) {\n const depth = Math.max(0.01, width - bevel * 2);\n const geometry = new ExtrudeGeometry(shape, {\n depth, bevelEnabled: true, bevelThickness: bevel, bevelSize: bevel, bevelSegments: segments, curveSegments: q.curve,\n });\n return geometry.translate(0, 0, -depth / 2);\n}\n\n// Chamfered box (plain box on lowPower or when too thin).\nfunction box(q, w, h, d, x, y, z, bevel = 0.025) {\n if (q.low || !bevel || bevel * 2.5 > Math.min(w, h, d)) return new BoxGeometry(w, h, d).translate(x, y, z);\n const hw = w / 2 - bevel;\n const hh = h / 2 - bevel;\n const shape = new Shape();\n shape.moveTo(-hw, -hh);\n shape.lineTo(hw, -hh);\n shape.lineTo(hw, hh);\n shape.lineTo(-hw, hh);\n return extrude(shape, d, bevel, q, 1).translate(x, y, z);\n}\n\nfunction cylinder(r, length, segments, axis, x, y, z) {\n const geometry = new CylinderGeometry(r, r, length, segments);\n if (axis === 'x') geometry.rotateZ(Math.PI / 2);\n else if (axis === 'z') geometry.rotateX(Math.PI / 2);\n return geometry.translate(x, y, z);\n}\n\n// Polygon outline; `arch` entries cut a wheel arch into the bottom edge at y = bottom.\nfunction profile(points) {\n const shape = new Shape();\n points.forEach((point, i) => {\n if (point.arch) {\n const [cx, cy, r, bottom] = point.arch;\n const tilt = Math.asin((cy - bottom) / r);\n // Clockwise over the top of the wheel; absarc adds the joining segment.\n shape.absarc(cx, cy, r, Math.PI + tilt, -tilt, true);\n } else if (point.q) {\n shape.quadraticCurveTo(...point.q);\n } else if (i === 0) shape.moveTo(...point);\n else shape.lineTo(...point);\n });\n return shape;\n}\n\n// Wheels resting on y = 0: rubber tire plus rim (and hub) discs on the outer face.\nfunction addWheels(q, parts, axles) {\n axles.forEach(({ x, r, w, z }) => {\n [-1, 1].forEach((side) => {\n const zc = side * z;\n parts.wheels.push(paint(cylinder(r, w, q.radial, 'z', x, r, zc), C.tire));\n const discs = [[r * 0.6, q.radial, C.rim, 0.004]];\n if (!q.low) discs.push([r * 0.2, 6, C.hub, 0.008]);\n discs.forEach(([radius, segments, color, offset]) => {\n const disc = new CircleGeometry(radius, segments);\n if (side < 0) disc.rotateY(Math.PI);\n parts.wheels.push(paint(disc.translate(x, r, zc + side * (w / 2 + offset)), color));\n });\n });\n });\n}\n\nfunction coupe(q) {\n const parts = { body: [], glass: [], wheels: [], trim: [], lights: [] };\n // Outline before the 0.05 bevel: arches 0.31 → 0.26 visible over 0.22 tires.\n parts.body.push(extrude(profile([\n [-1.06, 0.16], { arch: [-0.72, 0.22, 0.31, 0.16] }, { arch: [0.76, 0.22, 0.31, 0.16] }, [1.1, 0.16],\n { q: [1.15, 0.17, 1.15, 0.26] }, { q: [1.14, 0.38, 1.0, 0.4] }, [0.42, 0.46], [-0.7, 0.48],\n { q: [-1.06, 0.49, -1.1, 0.38] }, [-1.11, 0.22], { q: [-1.11, 0.16, -1.06, 0.16] },\n ]), 0.9, 0.05, q));\n // Fastback greenhouse with a body-colored roof slab.\n parts.glass.push(extrude(profile([[0.4, 0.46], [0.02, 0.74], [-0.42, 0.74], [-0.86, 0.47]]), 0.76, 0.03, q));\n parts.body.push(box(q, 0.5, 0.06, 0.8, -0.2, 0.79, 0, 0.02));\n parts.body.push(box(q, 0.16, 0.05, 0.86, -1.05, 0.6, 0, 0.02));\n if (!q.low) [-1, 1].forEach((s) => parts.body.push(box(q, 0.08, 0.05, 0.07, 0.32, 0.54, s * 0.475, 0)));\n addWheels(q, parts, [{ x: 0.76, r: 0.22, w: 0.2, z: 0.4 }, { x: -0.72, r: 0.22, w: 0.2, z: 0.4 }]);\n parts.trim.push(\n paint(box(q, 0.1, 0.1, 0.92, 1.17, 0.17, 0, 0.03), C.dark),\n paint(box(q, 0.1, 0.1, 0.92, -1.15, 0.18, 0, 0.03), C.dark),\n paint(box(q, 0.04, 0.07, 0.44, 1.2, 0.27, 0, 0), C.black),\n );\n [-1, 1].forEach((s) => {\n parts.trim.push(paint(box(q, 0.05, 0.12, 0.05, -1.03, 0.53, s * 0.3, 0), C.dark));\n if (!q.low) parts.trim.push(paint(cylinder(0.035, 0.1, 8, 'x', -1.17, 0.1, s * 0.22), C.chrome));\n parts.lights.push(\n paint(box(q, 0.06, 0.06, 0.2, 1.18, 0.35, s * 0.29, 0), C.head),\n paint(box(q, 0.04, 0.06, 0.24, -1.17, 0.36, s * 0.27, 0), C.tail),\n );\n });\n return parts;\n}\n\nfunction muscle(q) {\n const parts = { body: [], glass: [], wheels: [], trim: [], lights: [] };\n // Boxy body, long flat hood, short deck; fat rear tires.\n parts.body.push(extrude(profile([\n [-1.12, 0.17], { arch: [-0.74, 0.25, 0.34, 0.17] }, { arch: [0.8, 0.22, 0.31, 0.17] }, [1.15, 0.17],\n [1.18, 0.21], [1.18, 0.44], [1.13, 0.5], [-1.1, 0.5], [-1.15, 0.46], [-1.15, 0.22],\n ]), 0.96, 0.05, q));\n parts.glass.push(extrude(profile([[0.28, 0.47], [-0.02, 0.76], [-0.58, 0.76], [-0.8, 0.47]]), 0.8, 0.03, q));\n parts.body.push(\n box(q, 0.62, 0.06, 0.86, -0.3, 0.8, 0, 0.02),\n box(q, 0.34, 0.08, 0.3, 0.62, 0.58, 0, 0.025), // hood scoop\n box(q, 0.2, 0.05, 1.0, -1.04, 0.75, 0, 0.02), // rear wing\n );\n addWheels(q, parts, [{ x: 0.8, r: 0.22, w: 0.2, z: 0.4 }, { x: -0.74, r: 0.25, w: 0.26, z: 0.39 }]);\n parts.trim.push(\n paint(box(q, 0.06, 0.08, 1.0, 1.23, 0.2, 0, 0.02), C.chrome),\n paint(box(q, 0.06, 0.08, 1.0, -1.23, 0.2, 0, 0.02), C.chrome),\n paint(box(q, 0.03, 0.16, 0.62, 1.235, 0.33, 0, 0), C.black),\n paint(box(q, 0.03, 0.05, 0.22, 0.79, 0.59, 0, 0), C.black),\n );\n [-1, 1].forEach((s) => {\n // Twin racing stripes over hood, roof and deck.\n parts.trim.push(\n paint(box(q, 0.82, 0.012, 0.08, 0.71, 0.553, s * 0.08, 0), C.black),\n paint(box(q, 0.6, 0.012, 0.08, -0.3, 0.833, s * 0.08, 0), C.black),\n paint(box(q, 0.3, 0.012, 0.08, -0.98, 0.553, s * 0.08, 0), C.black),\n paint(box(q, 0.06, 0.18, 0.06, -1.04, 0.64, s * 0.34, 0), C.dark),\n );\n if (!q.low) parts.trim.push(paint(cylinder(0.04, 0.12, 8, 'x', -1.2, 0.12, s * 0.3), C.chrome));\n parts.lights.push(\n paint(cylinder(0.065, 0.03, q.low ? 6 : 10, 'x', 1.245, 0.34, s * 0.37), C.head),\n paint(box(q, 0.03, 0.07, 0.3, -1.215, 0.4, s * 0.26, 0), C.tail),\n );\n });\n return parts;\n}\n\nfunction racer(q) {\n const parts = { body: [], glass: [], wheels: [], trim: [], lights: [] };\n // Open-wheel tub: tapered nose, cockpit, airbox and sloped engine cover.\n const tub = extrude(profile([\n [-0.96, 0.12], [1.14, 0.12], [1.14, 0.17], [0.55, 0.3], [0.2, 0.36], [0.14, 0.4], [-0.16, 0.4],\n [-0.24, 0.54], [-0.4, 0.56], [-0.96, 0.3],\n ]), 0.44, 0.04, q);\n const position = tub.attributes.position;\n for (let i = 0; i < position.count; i += 1) {\n const x = position.getX(i);\n const taper = x > 0.35 ? 1 - 0.55 * Math.min(1, (x - 0.35) / 0.85) : x < -0.5 ? 1 - 0.3 * Math.min(1, (-0.5 - x) / 0.5) : 1;\n position.setZ(i, position.getZ(i) * taper);\n }\n tub.computeVertexNormals();\n parts.body.push(\n tub,\n box(q, 0.6, 0.2, 0.76, -0.12, 0.24, 0, 0.05), // sidepods\n box(q, 0.22, 0.04, 1.0, 1.12, 0.07, 0, 0.015), // front wing\n box(q, 0.24, 0.05, 0.84, -0.98, 0.71, 0, 0.02), // rear wing\n );\n parts.glass.push(box(q, 0.04, 0.045, 0.15, 0.08, 0.49, 0, 0));\n addWheels(q, parts, [{ x: 0.82, r: 0.2, w: 0.17, z: 0.415 }, { x: -0.74, r: 0.24, w: 0.24, z: 0.38 }]);\n parts.trim.push(\n paint(new SphereGeometry(0.12, q.low ? 8 : 10, q.low ? 6 : 8).translate(-0.03, 0.47, 0), C.white),\n paint(box(q, 0.06, 0.36, 0.05, -0.94, 0.52, 0, 0), C.dark),\n paint(box(q, 0.03, 0.06, 0.08, -0.25, 0.56, 0, 0), C.black),\n );\n [-1, 1].forEach((s) => {\n parts.trim.push(\n paint(box(q, 0.24, 0.12, 0.025, 1.14, 0.1, s * 0.5, 0), C.dark),\n paint(box(q, 0.3, 0.24, 0.025, -0.98, 0.64, s * 0.43, 0), C.dark),\n paint(box(q, 0.04, 0.03, 0.2, 0.82, 0.2, s * 0.24, 0), C.dark),\n paint(box(q, 0.04, 0.03, 0.1, -0.74, 0.24, s * 0.23, 0), C.dark),\n );\n });\n if (!q.low) parts.trim.push(paint(cylinder(0.03, 0.08, 8, 'x', -1.0, 0.28, 0), C.chrome));\n parts.lights.push(paint(box(q, 0.03, 0.06, 0.1, -1.015, 0.2, 0, 0), C.tail));\n return parts;\n}\n\nfunction createMaterials(lowPower) {\n const make = (options, standard) => (lowPower ? new MeshLambertMaterial(options) : new MeshStandardMaterial({ ...options, ...standard }));\n return {\n body: make({ color: 0xffffff }, { roughness: 0.28, metalness: 0.3 }),\n glass: make({ color: 0x1c2636 }, { roughness: 0.12, metalness: 0.6 }),\n wheels: make({ color: 0xffffff, vertexColors: true }, { roughness: 0.75, metalness: 0.15 }),\n trim: make({ color: 0xffffff, vertexColors: true }, { roughness: 0.35, metalness: 0.55 }),\n // Unlit so head/tail lights read as glowing.\n lights: new MeshBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }),\n };\n}\n\n// Merges each part, recenters every part on the car's bbox and sizes the collision sphere.\nfunction assemble(kind, parts, materials, palette, physics) {\n const merged = PART_ORDER.filter((name) => parts[name].length).map((name) => {\n const geometry = mergeBufferGeometries(parts[name].map(prep));\n if (!geometry) throw new Error(`avalanche cars: cannot merge ${kind}.${name}`);\n geometry.computeBoundingBox();\n return { name, geometry };\n });\n const bounds = new Box3();\n merged.forEach(({ geometry }) => bounds.union(geometry.boundingBox));\n const center = bounds.getCenter(new Vector3());\n const size = bounds.getSize(new Vector3());\n merged.forEach(({ geometry }) => {\n geometry.translate(-center.x, -center.y, -center.z);\n geometry.computeBoundingSphere();\n });\n return {\n kind,\n parts: merged.map(({ name, geometry }) => ({ geometry, material: materials[name], tinted: name === 'body' })),\n palette,\n // Equal-volume sphere of the bbox (≈0.78): snugger piles than the full bounding sphere.\n radius: Math.cbrt((size.x * size.y * size.z * 3) / (4 * Math.PI)),\n ...physics,\n };\n}\n\n/** Coupe, muscle car and open-wheel racer. Materials and palette are shared across kinds. */\nexport function createCarModels({ lowPower = false } = {}) {\n const q = { low: lowPower, curve: lowPower ? 3 : 5, bevel: lowPower ? 1 : 2, radial: lowPower ? 8 : 14 };\n const materials = createMaterials(lowPower);\n const palette = PALETTE.map((hex) => new Color(hex));\n const pools = { count: 10, countLow: 6 };\n return [\n assemble('car', coupe(q), materials, palette, { mass: 3, bounce: 0.35, friction: 0.6, drag: 0.04, ...pools }),\n assemble('car-muscle', muscle(q), materials, palette, { mass: 3.4, bounce: 0.3, friction: 0.62, drag: 0.04, ...pools }),\n assemble('car-racer', racer(q), materials, palette, { mass: 2.6, bounce: 0.4, friction: 0.55, drag: 0.05, ...pools }),\n ];\n}\n"],"mappings":";;;AASA,IAAMA,IAAU;CAAC;CAAW;CAAW;CAAW;CAAW;CAAW;CAAW;AAAS,GACtFC,IAAI;CACR,MAAM;CAAW,KAAK;CAAW,KAAK;CAAW,QAAQ;CAAW,MAAM;CAAW,OAAO;CAC5F,OAAO;CAAW,MAAM;CAAW,MAAM;AAC3C,GACMC,IAAa;CAAC;CAAQ;CAAS;CAAU;CAAQ;AAAQ;AAG/D,SAASC,EAAK,GAAU;CACtB,IAAMC,IAAOC,EAAS,QAAQA,EAAS,aAAa,IAAIA;CAExD,OADAD,EAAK,gBAAgB,IAAI,GAClBA;AACT;AAEA,SAASE,EAAM,GAAU,GAAK;CAC5B,IAAMF,IAAOD,EAAKE,CAAQ,GACpBE,IAAQ,IAAIC,EAAMC,CAAG,GACrBC,IAAQ,IAAI,aAAaN,EAAK,WAAW,SAAS,QAAQ,CAAC;CACjE,KAAK,IAAIO,IAAI,GAAGA,IAAID,EAAM,QAAQ,KAAK,GAAG,EAAM,QAAQA,GAAOC,CAAC;CAEhE,OADAP,EAAK,aAAa,SAAS,IAAIQ,EAAuBF,GAAO,CAAC,CAAC,GACxDN;AACT;AAGA,SAASS,EAAQ,GAAO,GAAO,GAAO,GAAG,IAAWC,EAAE,OAAO;CAC3D,IAAMC,IAAQ,KAAK,IAAI,KAAMC,IAAQC,IAAQ,CAAC;CAI9C,OAAO,IAHcC,EAAgBC,GAAO;EAC1C;EAAO,cAAc;EAAM,gBAAgBF;EAAO,WAAWA;EAAO,eAAeG;EAAU,eAAeN,EAAE;CAChH,CACc,CAAC,CAAC,UAAU,GAAG,GAAG,CAACC,IAAQ,CAAC;AAC5C;AAGA,SAASM,EAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAQ,MAAO;CAC/C,IAAIP,EAAE,OAAO,CAACG,KAASA,IAAQ,MAAM,KAAK,IAAIK,GAAGC,GAAGC,CAAC,GAAG,OAAO,IAAIC,EAAYH,GAAGC,GAAGC,CAAC,CAAC,CAAC,UAAUE,GAAGC,GAAGC,CAAC;CACzG,IAAMC,IAAKP,IAAI,IAAIL,GACba,IAAKP,IAAI,IAAIN,GACbE,IAAQ,IAAIY,EAAM;CAKxB,OAJAZ,EAAM,OAAO,CAACU,GAAI,CAACC,CAAE,GACrBX,EAAM,OAAOU,GAAI,CAACC,CAAE,GACpBX,EAAM,OAAOU,GAAIC,CAAE,GACnBX,EAAM,OAAO,CAACU,GAAIC,CAAE,GACbjB,EAAQM,GAAOK,GAAGP,GAAOH,GAAG,CAAC,CAAC,CAAC,UAAUY,GAAGC,GAAGC,CAAC;AACzD;AAEA,SAASI,EAAS,GAAG,GAAQ,GAAU,GAAM,GAAG,GAAG,GAAG;CACpD,IAAM3B,IAAW,IAAI4B,EAAiBC,GAAGA,GAAGC,GAAQf,CAAQ;CAG5D,OAFIgB,MAAS,MAAK/B,EAAS,QAAQ,KAAK,KAAK,CAAC,IACrC+B,MAAS,OAAK/B,EAAS,QAAQ,KAAK,KAAK,CAAC,GAC5CA,EAAS,UAAUqB,GAAGC,GAAGC,CAAC;AACnC;AAGA,SAASS,EAAQ,GAAQ;CACvB,IAAMlB,IAAQ,IAAIY,EAAM;CAYxB,OAXAO,EAAO,SAAS,GAAO,MAAM;EAC3B,IAAIC,EAAM,MAAM;GACd,IAAM,CAACC,GAAIC,GAAIP,GAAGQ,KAAUH,EAAM,MAC5BI,IAAO,KAAK,MAAMF,IAAKC,KAAUR,CAAC;GAExC,EAAM,OAAOM,GAAIC,GAAIP,GAAG,KAAK,KAAKS,GAAM,CAACA,GAAM,EAAI;EACrD,OAAO,AAAIJ,EAAM,IACfpB,EAAM,iBAAiB,GAAGoB,EAAM,CAAC,IACxB5B,MAAM,IAAGQ,EAAM,OAAO,GAAGoB,CAAK,IACpCpB,EAAM,OAAO,GAAGoB,CAAK;CAC5B,CAAC,GACMpB;AACT;AAGA,SAASyB,EAAU,GAAG,GAAO,GAAO;CAClC,EAAM,SAAS,EAAE,MAAG,MAAG,MAAG,WAAQ;EAChC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,MAAS;GACxB,IAAMC,IAAKC,IAAOlB;GAClB,EAAM,OAAO,KAAKtB,EAAM0B,EAASE,GAAGZ,GAAGR,EAAE,QAAQ,KAAKY,GAAGQ,GAAGW,CAAE,GAAG5C,EAAE,IAAI,CAAC;GACxE,IAAM8C,IAAQ,CAAC;IAACb,IAAI;IAAKpB,EAAE;IAAQb,EAAE;IAAK;GAAK,CAAC;GAEhD,AADKa,EAAE,OAAKiC,EAAM,KAAK;IAACb,IAAI;IAAK;IAAGjC,EAAE;IAAK;GAAK,CAAC,GACjD8C,EAAM,SAAS,CAACC,GAAQ5B,GAAUb,GAAO0C,OAAY;IACnD,IAAMC,IAAO,IAAIC,EAAeH,GAAQ5B,CAAQ;IAEhD,AADI0B,IAAO,KAAGI,EAAK,QAAQ,KAAK,EAAE,GAClCE,EAAM,OAAO,KAAK9C,EAAM4C,EAAK,UAAUxB,GAAGQ,GAAGW,IAAKC,KAAQxB,IAAI,IAAI2B,EAAO,GAAG1C,CAAK,CAAC;GACpF,CAAC;EACH,CAAC;CACH,CAAC;AACH;AAEA,SAAS8C,EAAM,GAAG;CAChB,IAAMD,IAAQ;EAAE,MAAM,CAAC;EAAG,OAAO,CAAC;EAAG,QAAQ,CAAC;EAAG,MAAM,CAAC;EAAG,QAAQ,CAAC;CAAE;CA0BtE,OAxBAA,EAAM,KAAK,KAAKvC,EAAQwB,EAAQ;EAC9B,CAAC,OAAO,GAAI;EAAG,EAAE,MAAM;GAAC;GAAO;GAAM;GAAM;EAAI,EAAE;EAAG,EAAE,MAAM;GAAC;GAAM;GAAM;GAAM;EAAI,EAAE;EAAG,CAAC,KAAK,GAAI;EAClG,EAAE,GAAG;GAAC;GAAM;GAAM;GAAM;EAAI,EAAE;EAAG,EAAE,GAAG;GAAC;GAAM;GAAM;GAAK;EAAG,EAAE;EAAG,CAAC,KAAM,GAAI;EAAG,CAAC,KAAM,GAAI;EACzF,EAAE,GAAG;GAAC;GAAO;GAAM;GAAM;EAAI,EAAE;EAAG,CAAC,OAAO,GAAI;EAAG,EAAE,GAAG;GAAC;GAAO;GAAM;GAAO;EAAI,EAAE;CACnF,CAAC,GAAG,IAAK,KAAMvB,CAAC,CAAC,GAEjBsC,EAAM,MAAM,KAAKvC,EAAQwB,EAAQ;EAAC,CAAC,IAAK,GAAI;EAAG,CAAC,KAAM,GAAI;EAAG,CAAC,MAAO,GAAI;EAAG,CAAC,MAAO,GAAI;CAAC,CAAC,GAAG,KAAM,KAAMvB,CAAC,CAAC,GAC3GsC,EAAM,KAAK,KAAK/B,EAAIP,GAAG,IAAK,KAAM,IAAK,KAAM,KAAM,GAAG,GAAI,CAAC,GAC3DsC,EAAM,KAAK,KAAK/B,EAAIP,GAAG,KAAM,KAAM,KAAM,OAAO,IAAK,GAAG,GAAI,CAAC,GACxDA,EAAE,OAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,MAAMsC,EAAM,KAAK,KAAK/B,EAAIP,GAAG,KAAM,KAAM,KAAM,KAAM,KAAMwC,IAAI,MAAO,CAAC,CAAC,CAAC,GACtGV,EAAU9B,GAAGsC,GAAO,CAAC;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAK,GAAG;CAAI,GAAG;EAAE,GAAG;EAAO,GAAG;EAAM,GAAG;EAAK,GAAG;CAAI,CAAC,CAAC,GACjGA,EAAM,KAAK,KACT9C,EAAMe,EAAIP,GAAG,IAAK,IAAK,KAAM,MAAM,KAAM,GAAG,GAAI,GAAGb,EAAE,IAAI,GACzDK,EAAMe,EAAIP,GAAG,IAAK,IAAK,KAAM,OAAO,KAAM,GAAG,GAAI,GAAGb,EAAE,IAAI,GAC1DK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,KAAK,KAAM,GAAG,CAAC,GAAGb,EAAE,KAAK,CAC1D,GACA,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,MAAM;EAGrB,AAFAmD,EAAM,KAAK,KAAK9C,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,OAAO,KAAMwC,IAAI,IAAK,CAAC,GAAGrD,EAAE,IAAI,CAAC,GAC3Ea,EAAE,OAAKsC,EAAM,KAAK,KAAK9C,EAAM0B,EAAS,MAAO,IAAK,GAAG,KAAK,OAAO,IAAKsB,IAAI,GAAI,GAAGrD,EAAE,MAAM,CAAC,GAC/FmD,EAAM,OAAO,KACX9C,EAAMe,EAAIP,GAAG,KAAM,KAAM,IAAK,MAAM,KAAMwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,GAC9DK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,OAAO,KAAMwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,CAClE;CACF,CAAC,GACMmD;AACT;AAEA,SAASG,EAAO,GAAG;CACjB,IAAMH,IAAQ;EAAE,MAAM,CAAC;EAAG,OAAO,CAAC;EAAG,QAAQ,CAAC;EAAG,MAAM,CAAC;EAAG,QAAQ,CAAC;CAAE;CAiCtE,OA/BAA,EAAM,KAAK,KAAKvC,EAAQwB,EAAQ;EAC9B,CAAC,OAAO,GAAI;EAAG,EAAE,MAAM;GAAC;GAAO;GAAM;GAAM;EAAI,EAAE;EAAG,EAAE,MAAM;GAAC;GAAK;GAAM;GAAM;EAAI,EAAE;EAAG,CAAC,MAAM,GAAI;EAClG,CAAC,MAAM,GAAI;EAAG,CAAC,MAAM,GAAI;EAAG,CAAC,MAAM,EAAG;EAAG,CAAC,MAAM,EAAG;EAAG,CAAC,OAAO,GAAI;EAAG,CAAC,OAAO,GAAI;CACnF,CAAC,GAAG,KAAM,KAAMvB,CAAC,CAAC,GAClBsC,EAAM,MAAM,KAAKvC,EAAQwB,EAAQ;EAAC,CAAC,KAAM,GAAI;EAAG,CAAC,MAAO,GAAI;EAAG,CAAC,MAAO,GAAI;EAAG,CAAC,KAAM,GAAI;CAAC,CAAC,GAAG,IAAK,KAAMvB,CAAC,CAAC,GAC3GsC,EAAM,KAAK,KACT/B,EAAIP,GAAG,KAAM,KAAM,KAAM,KAAM,IAAK,GAAG,GAAI,GAC3CO,EAAIP,GAAG,KAAM,KAAM,IAAK,KAAM,KAAM,GAAG,IAAK,GAC5CO,EAAIP,GAAG,IAAK,KAAM,GAAK,OAAO,KAAM,GAAG,GAAI,CAC7C,GACA8B,EAAU9B,GAAGsC,GAAO,CAAC;EAAE,GAAG;EAAK,GAAG;EAAM,GAAG;EAAK,GAAG;CAAI,GAAG;EAAE,GAAG;EAAO,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,CAAC,CAAC,GAClGA,EAAM,KAAK,KACT9C,EAAMe,EAAIP,GAAG,KAAM,KAAM,GAAK,MAAM,IAAK,GAAG,GAAI,GAAGb,EAAE,MAAM,GAC3DK,EAAMe,EAAIP,GAAG,KAAM,KAAM,GAAK,OAAO,IAAK,GAAG,GAAI,GAAGb,EAAE,MAAM,GAC5DK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,OAAO,KAAM,GAAG,CAAC,GAAGb,EAAE,KAAK,GAC1DK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,KAAM,KAAM,GAAG,CAAC,GAAGb,EAAE,KAAK,CAC3D,GACA,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,MAAM;EASrB,AAPAmD,EAAM,KAAK,KACT9C,EAAMe,EAAIP,GAAG,KAAM,MAAO,KAAM,KAAM,MAAOwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,KAAK,GAClEK,EAAMe,EAAIP,GAAG,IAAK,MAAO,KAAM,KAAM,MAAOwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,KAAK,GACjEK,EAAMe,EAAIP,GAAG,IAAK,MAAO,KAAM,MAAO,MAAOwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,KAAK,GAClEK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,OAAO,KAAMwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,CAClE,GACKa,EAAE,OAAKsC,EAAM,KAAK,KAAK9C,EAAM0B,EAAS,KAAM,KAAM,GAAG,KAAK,MAAM,KAAMsB,IAAI,EAAG,GAAGrD,EAAE,MAAM,CAAC,GAC9FmD,EAAM,OAAO,KACX9C,EAAM0B,EAAS,MAAO,KAAMlB,EAAE,MAAM,IAAI,IAAI,KAAK,OAAO,KAAMwC,IAAI,GAAI,GAAGrD,EAAE,IAAI,GAC/EK,EAAMe,EAAIP,GAAG,KAAM,KAAM,IAAK,QAAQ,IAAKwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,CACjE;CACF,CAAC,GACMmD;AACT;AAEA,SAASI,EAAM,GAAG;CAChB,IAAMJ,IAAQ;EAAE,MAAM,CAAC;EAAG,OAAO,CAAC;EAAG,QAAQ,CAAC;EAAG,MAAM,CAAC;EAAG,QAAQ,CAAC;CAAE,GAEhEK,IAAM5C,EAAQwB,EAAQ;EAC1B,CAAC,MAAO,GAAI;EAAG,CAAC,MAAM,GAAI;EAAG,CAAC,MAAM,GAAI;EAAG,CAAC,KAAM,EAAG;EAAG,CAAC,IAAK,GAAI;EAAG,CAAC,KAAM,EAAG;EAAG,CAAC,MAAO,EAAG;EAC7F,CAAC,MAAO,GAAI;EAAG,CAAC,KAAM,GAAI;EAAG,CAAC,MAAO,EAAG;CAC1C,CAAC,GAAG,KAAM,KAAMvB,CAAC,GACX4C,IAAWD,EAAI,WAAW;CAChC,KAAK,IAAI9C,IAAI,GAAGA,IAAI+C,EAAS,OAAO,KAAK,GAAG;EAC1C,IAAMhC,IAAIgC,EAAS,KAAK/C,CAAC,GACnBgD,IAAQjC,IAAI,MAAO,IAAI,MAAO,KAAK,IAAI,IAAIA,IAAI,OAAQ,GAAI,IAAIA,IAAI,MAAO,IAAI,KAAM,KAAK,IAAI,IAAI,MAAOA,KAAK,EAAG,IAAI;EAC1H,EAAS,KAAKf,GAAG+C,EAAS,KAAK/C,CAAC,IAAIgD,CAAK;CAC3C;CAyBA,OAxBAF,EAAI,qBAAqB,GACzBL,EAAM,KAAK,KACTK,GACApC,EAAIP,GAAG,IAAK,IAAK,KAAM,MAAO,KAAM,GAAG,GAAI,GAC3CO,EAAIP,GAAG,KAAM,KAAM,GAAK,MAAM,KAAM,GAAG,IAAK,GAC5CO,EAAIP,GAAG,KAAM,KAAM,KAAM,MAAO,KAAM,GAAG,GAAI,CAC/C,GACAsC,EAAM,MAAM,KAAK/B,EAAIP,GAAG,KAAM,MAAO,KAAM,KAAM,KAAM,GAAG,CAAC,CAAC,GAC5D8B,EAAU9B,GAAGsC,GAAO,CAAC;EAAE,GAAG;EAAM,GAAG;EAAK,GAAG;EAAM,GAAG;CAAM,GAAG;EAAE,GAAG;EAAO,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,CAAC,CAAC,GACrGA,EAAM,KAAK,KACT9C,EAAM,IAAIsD,EAAe,KAAM9C,EAAE,MAAM,IAAI,IAAIA,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,UAAU,MAAO,KAAM,CAAC,GAAGb,EAAE,KAAK,GAChGK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,MAAO,KAAM,GAAG,CAAC,GAAGb,EAAE,IAAI,GACzDK,EAAMe,EAAIP,GAAG,KAAM,KAAM,KAAM,MAAO,KAAM,GAAG,CAAC,GAAGb,EAAE,KAAK,CAC5D,GACA,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,MAAM;EACrB,EAAM,KAAK,KACTK,EAAMe,EAAIP,GAAG,KAAM,KAAM,MAAO,MAAM,IAAKwC,IAAI,IAAK,CAAC,GAAGrD,EAAE,IAAI,GAC9DK,EAAMe,EAAIP,GAAG,IAAK,KAAM,MAAO,MAAO,KAAMwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,GAChEK,EAAMe,EAAIP,GAAG,KAAM,KAAM,IAAK,KAAM,IAAKwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,GAC7DK,EAAMe,EAAIP,GAAG,KAAM,KAAM,IAAK,MAAO,KAAMwC,IAAI,KAAM,CAAC,GAAGrD,EAAE,IAAI,CACjE;CACF,CAAC,GACIa,EAAE,OAAKsC,EAAM,KAAK,KAAK9C,EAAM0B,EAAS,KAAM,KAAM,GAAG,KAAK,IAAM,KAAM,CAAC,GAAG/B,EAAE,MAAM,CAAC,GACxFmD,EAAM,OAAO,KAAK9C,EAAMe,EAAIP,GAAG,KAAM,KAAM,IAAK,QAAQ,IAAK,GAAG,CAAC,GAAGb,EAAE,IAAI,CAAC,GACpEmD;AACT;AAEA,SAASS,EAAgB,GAAU;CACjC,IAAMC,KAAQ,GAAS,MAAcC,IAAW,IAAIC,EAAoBC,CAAO,IAAI,IAAIC,EAAqB;EAAE,GAAGD;EAAS,GAAGE;CAAS,CAAC;CACvI,OAAO;EACL,MAAML,EAAK,EAAE,OAAO,SAAS,GAAG;GAAE,WAAW;GAAM,WAAW;EAAI,CAAC;EACnE,OAAOA,EAAK,EAAE,OAAO,QAAS,GAAG;GAAE,WAAW;GAAM,WAAW;EAAI,CAAC;EACpE,QAAQA,EAAK;GAAE,OAAO;GAAU,cAAc;EAAK,GAAG;GAAE,WAAW;GAAM,WAAW;EAAK,CAAC;EAC1F,MAAMA,EAAK;GAAE,OAAO;GAAU,cAAc;EAAK,GAAG;GAAE,WAAW;GAAM,WAAW;EAAK,CAAC;EAExF,QAAQ,IAAIM,EAAkB;GAAE,OAAO;GAAU,cAAc;GAAM,YAAY;EAAM,CAAC;CAC1F;AACF;AAGA,SAASC,EAAS,GAAM,GAAO,GAAW,GAAS,GAAS;CAC1D,IAAMC,IAASpE,EAAW,QAAQ,MAASkD,EAAMmB,EAAK,CAAC,MAAM,CAAC,CAAC,KAAK,MAAS;EAC3E,IAAMlE,IAAWmE,EAAsBpB,EAAMmB,EAAK,CAAC,IAAIpE,CAAI,CAAC;EAC5D,IAAI,CAACE,GAAU,MAAU,MAAM,gCAAgCoE,EAAK,GAAGF,GAAM;EAE7E,OADAlE,EAAS,mBAAmB,GACrB;GAAE;GAAM;EAAS;CAC1B,CAAC,GACKqE,IAAS,IAAIC,EAAK;CACxB,EAAO,SAAS,EAAE,kBAAeD,EAAO,MAAMrE,EAAS,WAAW,CAAC;CACnE,IAAMuE,IAASF,EAAO,UAAU,IAAIG,EAAQ,CAAC,GACvCC,IAAOJ,EAAO,QAAQ,IAAIG,EAAQ,CAAC;CAKzC,OAJAP,EAAO,SAAS,EAAE,kBAAe;EAE/B,AADAjE,EAAS,UAAU,CAACuE,EAAO,GAAG,CAACA,EAAO,GAAG,CAACA,EAAO,CAAC,GAClDvE,EAAS,sBAAsB;CACjC,CAAC,GACM;EACL;EACA,OAAOiE,EAAO,KAAK,EAAE,SAAM,mBAAgB;GAAE;GAAU,UAAUS,EAAUR;GAAO,QAAQA,MAAS;EAAO,EAAE;EAC5G;EAEA,QAAQ,KAAK,KAAMO,EAAK,IAAIA,EAAK,IAAIA,EAAK,IAAI,KAAM,IAAI,KAAK,GAAG;EAChE,GAAGE;CACL;AACF;AAGA,SAAgBC,EAAgB,EAAE,cAAW,OAAU,CAAC,GAAG;CACzD,IAAMnE,IAAI;EAAE,KAAKiD;EAAU,OAAOA,IAAW,IAAI;EAAG,OAAOA,IAAW,IAAI;EAAG,QAAQA,IAAW,IAAI;CAAG,GACjGgB,IAAYlB,EAAgBE,CAAQ,GACpCmB,IAAUlF,EAAQ,KAAK,MAAQ,IAAIQ,EAAMC,CAAG,CAAC,GAC7C0E,IAAQ;EAAE,OAAO;EAAI,UAAU;CAAE;CACvC,OAAO;EACLd,EAAS,OAAOhB,EAAMvC,CAAC,GAAGiE,GAAWG,GAAS;GAAE,MAAM;GAAG,QAAQ;GAAM,UAAU;GAAK,MAAM;GAAM,GAAGC;EAAM,CAAC;EAC5Gd,EAAS,cAAcd,EAAOzC,CAAC,GAAGiE,GAAWG,GAAS;GAAE,MAAM;GAAK,QAAQ;GAAK,UAAU;GAAM,MAAM;GAAM,GAAGC;EAAM,CAAC;EACtHd,EAAS,aAAab,EAAM1C,CAAC,GAAGiE,GAAWG,GAAS;GAAE,MAAM;GAAK,QAAQ;GAAK,UAAU;GAAM,MAAM;GAAM,GAAGC;EAAM,CAAC;CACtH;AACF"}
@@ -1,7 +1,7 @@
1
- import { a as e, t } from "./games-BvYTWgaR.js";
1
+ import { a as e, t } from "./games-D_9SiSBT.js";
2
2
  import { useSyncExternalStore as n } from "react";
3
- import { Volume2 as r, VolumeX as i } from "lucide-react";
4
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
3
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
+ import { Volume2 as a, VolumeX as o } from "lucide-react";
5
5
  // src/audio/useSoundSettings.js
6
6
  function s(e) {
7
7
  return n(e.subscribe, e.getSettings, e.getSettings);
@@ -9,17 +9,17 @@ function s(e) {
9
9
  // src/games/shared/GameSoundControls.jsx
10
10
  function c({ manager: e }) {
11
11
  let { volume: t, muted: n } = s(e), c = n || t === 0;
12
- return /* @__PURE__ */ o("div", {
12
+ return /* @__PURE__ */ i("div", {
13
13
  className: "chat-game-sound",
14
- children: [/* @__PURE__ */ a("button", {
14
+ children: [/* @__PURE__ */ r("button", {
15
15
  type: "button",
16
16
  className: "chat-game-sound-toggle",
17
17
  onClick: e.toggleMuted,
18
18
  "aria-pressed": c,
19
19
  "aria-label": c ? "Activar sonido del juego" : "Silenciar sonido del juego",
20
20
  title: c ? "Activar sonido" : "Silenciar",
21
- children: a(c ? i : r, { size: 15 })
22
- }), /* @__PURE__ */ a("input", {
21
+ children: r(c ? o : a, { size: 15 })
22
+ }), /* @__PURE__ */ r("input", {
23
23
  type: "range",
24
24
  min: "0",
25
25
  max: "100",
@@ -232,4 +232,4 @@ function k() {
232
232
  }
233
233
  export { g as a, k as i, O as n, c as o, T as r, _ as t };
234
234
 
235
- //# sourceMappingURL=connect4Sounds-DmD-E9kX.js.map
235
+ //# sourceMappingURL=connect4Sounds-3a6h7gUC.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connect4Sounds-DmD-E9kX.js","names":["useChatGames","useSoundSettings","useSyncExternalStore","manager","GameSoundControls","silent","muted","volume","jsx","VolumeX","Volume2","event","GESTURES","MAX_KEYS","MAX_LATENCY_MS","clamp","value","readSettings","stored","storageKey","defaults","LOOP_FADE_IN_S","LOOP_FADE_OUT_S","createSoundManager","id","AudioContextClass","settings","settingsFrom","defaultVolume","context","gain","unlocked","raw","buffers","missing","playedKeys","listeners","wantedLoops","loops","loopOptions","started","listening","unsubscribeSettings","urlFor","baseUrl","sounds","name","fetchRaw","response","ensureContext","loadBuffer","ctx","data","resolve","buffer","levelFor","levels","beginLoop","token","level","fadeIn","source","trim","endLoop","loop","fadeOut","now","unlock","removeGestureListeners","type","remember","key","emit","listener","applySettings","next","setSettings","patch","start","names","requestedAt","node","options","connect4Sounds","chatSoundUrl","FOLLOW_UP_MS","pendingDrops","followUps","stopSounds","same","left","right","reducedMotion","playMoveOutcome","game","eventId","settleConnect4Drop","entry","timer","scheduleDrop","delay","onGameChange","before","isConnect4SoundsRunning","startConnect4Sounds","unsubscribe","state","previous","stop"],"sources":["../../src/audio/useSoundSettings.js","../../src/games/shared/GameSoundControls.jsx","../../src/audio/createSoundManager.js","../../src/games/connect4/connect4Sounds.js"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\n// React binding for a sound manager's volume/mute settings.\nexport default function useSoundSettings(manager) {\n return useSyncExternalStore(manager.subscribe, manager.getSettings, manager.getSettings);\n}\n","/* eslint-disable react/prop-types */\nimport { Volume2, VolumeX } from 'lucide-react';\nimport useSoundSettings from '../../audio/useSoundSettings';\n\nexport default function GameSoundControls({ manager }) {\n const { volume, muted } = useSoundSettings(manager);\n const silent = muted || volume === 0;\n return (\n <div className=\"chat-game-sound\">\n <button type=\"button\" className=\"chat-game-sound-toggle\" onClick={manager.toggleMuted}\n aria-pressed={silent} aria-label={silent ? 'Activar sonido del juego' : 'Silenciar sonido del juego'} title={silent ? 'Activar sonido' : 'Silenciar'}>\n {silent ? <VolumeX size={15} /> : <Volume2 size={15} />}\n </button>\n <input type=\"range\" min=\"0\" max=\"100\" step=\"5\" value={Math.round((muted ? 0 : volume) * 100)}\n onChange={(event) => manager.setVolume(Number(event.target.value) / 100)} aria-label=\"Volumen del juego\" />\n </div>\n );\n}\n","// Reusable game sound manager built on Web Audio (no dependencies).\n// - Lazy: files are fetched on preload()/first play and decoded once.\n// - Autoplay-safe: the AudioContext is created/resumed only after a user\n// gesture; sounds requested before that are dropped, never queued.\n// - Fault-tolerant: a missing or undecodable file is marked unavailable and\n// every later play() of it is a silent no-op.\n// - Deduplicated: play(name, { key }) ignores keys already played recently.\n// - Optional per-sound levels, looped beds (startLoop/stopLoop) and settings\n// shared with another manager (settingsFrom) so games keep one preference.\n// - Side-effect free at creation (SSR-safe): gesture listeners and the\n// settingsFrom subscription are registered on start() or the first\n// preload()/play()/startLoop()/subscribe(); dispose() tears everything down.\n\nconst GESTURES = ['pointerdown', 'keydown', 'touchend'];\nconst MAX_KEYS = 300;\n// Sounds still loading when requested are dropped if they would play too late.\nconst MAX_LATENCY_MS = 800;\n\nconst clamp = (value) => Math.min(1, Math.max(0, Number(value) || 0));\n\nfunction readSettings(storageKey, defaults) {\n try {\n const stored = JSON.parse(localStorage.getItem(storageKey) ?? 'null');\n if (stored && typeof stored === 'object') {\n return { volume: clamp(stored.volume ?? defaults.volume), muted: Boolean(stored.muted) };\n }\n } catch { /* storage unavailable */ }\n return { volume: clamp(defaults.volume), muted: false };\n}\n\nconst LOOP_FADE_IN_S = 1.5;\nconst LOOP_FADE_OUT_S = 0.6;\n\nexport function createSoundManager({ id, baseUrl, sounds, levels = {}, defaultVolume = 0.6, settingsFrom = null }) {\n const storageKey = `lira.sound.${id}`;\n const AudioContextClass = typeof window !== 'undefined' ? (window.AudioContext || window.webkitAudioContext) : null;\n let settings = settingsFrom ? settingsFrom.getSettings() : readSettings(storageKey, { volume: defaultVolume });\n let context = null;\n let gain = null;\n let unlocked = false;\n const raw = new Map(); // name -> Promise<ArrayBuffer | null>\n const buffers = new Map(); // name -> Promise<AudioBuffer | null>\n const missing = new Set();\n const playedKeys = new Set();\n const listeners = new Set();\n const wantedLoops = new Set();\n const loops = new Map(); // name -> { source, level } once playing\n const loopOptions = new Map(); // name -> { fadeIn, fadeOut, trim } from startLoop\n let started = false;\n let listening = false;\n let unsubscribeSettings = null;\n\n // baseUrl may be a function so hosts can configure asset locations after import.\n const urlFor = (name) => `${String(typeof baseUrl === 'function' ? baseUrl() : baseUrl).replace(/\\/$/, '')}/${sounds[name]}`;\n\n const fetchRaw = (name) => {\n if (!raw.has(name)) {\n raw.set(name, fetch(urlFor(name))\n .then((response) => (response.ok ? response.arrayBuffer() : null))\n .catch(() => null));\n }\n return raw.get(name);\n };\n\n const ensureContext = () => {\n if (!AudioContextClass) return null;\n if (!context) {\n try {\n context = new AudioContextClass();\n gain = context.createGain();\n gain.gain.value = settings.muted ? 0 : settings.volume;\n gain.connect(context.destination);\n } catch {\n context = null;\n return null;\n }\n }\n if (context.state === 'suspended') void context.resume().catch(() => {});\n return context;\n };\n\n const loadBuffer = (name) => {\n if (missing.has(name) || !sounds[name]) return Promise.resolve(null);\n if (!buffers.has(name)) {\n const ctx = ensureContext();\n if (!ctx) return Promise.resolve(null);\n buffers.set(name, fetchRaw(name).then((data) => {\n // decodeAudioData detaches the buffer, so decode a copy.\n if (!data) return null;\n return new Promise((resolve) => {\n // Modern browsers also return a promise that rejects alongside the error\n // callback; without this catch a missing/HTML file is an unhandled rejection.\n ctx.decodeAudioData(data.slice(0), resolve, () => resolve(null))?.catch?.(() => resolve(null));\n });\n }).then((buffer) => {\n if (!buffer) missing.add(name);\n return buffer;\n }).catch(() => {\n missing.add(name);\n return null;\n }));\n }\n return buffers.get(name);\n };\n\n const levelFor = (name) => clamp(levels[name] ?? 1);\n\n const beginLoop = (name) => {\n if (!wantedLoops.has(name) || loops.has(name) || !unlocked) return;\n const ctx = ensureContext();\n if (!ctx) return;\n const token = {}; // a stop/start while loading must not start a stale copy\n loops.set(name, token);\n void loadBuffer(name).then((buffer) => {\n if (loops.get(name) !== token) return;\n if (!buffer || ctx.state === 'closed') { loops.delete(name); return; }\n try {\n const { fadeIn = LOOP_FADE_IN_S, trim = 0 } = loopOptions.get(name) ?? {};\n const level = ctx.createGain();\n level.gain.setValueAtTime(0, ctx.currentTime);\n level.gain.linearRampToValueAtTime(levelFor(name), ctx.currentTime + fadeIn);\n level.connect(gain);\n const source = ctx.createBufferSource();\n source.buffer = buffer;\n source.loop = true;\n // Skips encoder padding at both ends (MP3) so the loop has no audible gap.\n if (trim > 0 && buffer.duration > trim * 4) {\n source.loopStart = trim;\n source.loopEnd = buffer.duration - trim;\n }\n source.connect(level);\n source.start(0, trim > 0 && buffer.duration > trim * 4 ? trim : 0);\n loops.set(name, { source, level });\n } catch {\n loops.delete(name);\n }\n });\n };\n\n const endLoop = (name) => {\n const loop = loops.get(name);\n loops.delete(name);\n if (!loop?.source || !context) return;\n try {\n const fadeOut = loopOptions.get(name)?.fadeOut ?? LOOP_FADE_OUT_S;\n const now = context.currentTime;\n loop.level.gain.cancelScheduledValues(now);\n loop.level.gain.setValueAtTime(loop.level.gain.value, now);\n loop.level.gain.linearRampToValueAtTime(0, now + fadeOut);\n loop.source.stop(now + fadeOut + 0.05);\n } catch { /* already stopped */ }\n };\n\n const unlock = () => {\n if (unlocked) return;\n unlocked = true;\n removeGestureListeners();\n if (ensureContext()) Object.keys(sounds).forEach((name) => { if (raw.has(name)) void loadBuffer(name); });\n wantedLoops.forEach(beginLoop);\n };\n\n function removeGestureListeners() {\n if (!listening) return;\n listening = false;\n GESTURES.forEach((type) => window.removeEventListener(type, unlock, true));\n }\n\n const remember = (key) => {\n if (playedKeys.has(key)) return false;\n playedKeys.add(key);\n if (playedKeys.size > MAX_KEYS) playedKeys.delete(playedKeys.values().next().value);\n return true;\n };\n\n const emit = () => listeners.forEach((listener) => listener());\n\n const applySettings = (next) => {\n settings = next;\n if (gain) gain.gain.value = settings.muted ? 0 : settings.volume;\n emit();\n };\n\n const setSettings = (patch) => {\n const next = { volume: clamp(patch.volume ?? settings.volume), muted: patch.muted ?? settings.muted };\n try { localStorage.setItem(storageKey, JSON.stringify(next)); } catch { /* storage unavailable */ }\n applySettings(next);\n };\n\n // Idempotent; registers the unlock listeners and the shared-settings link.\n const start = () => {\n if (started) return;\n started = true;\n if (settingsFrom) {\n settings = settingsFrom.getSettings();\n unsubscribeSettings = settingsFrom.subscribe(() => applySettings(settingsFrom.getSettings()));\n }\n if (typeof window === 'undefined' || unlocked) return;\n // Sticky activation: a gesture already happened before the manager started.\n if (typeof navigator !== 'undefined' && navigator.userActivation?.hasBeenActive) unlocked = true;\n else {\n listening = true;\n GESTURES.forEach((type) => window.addEventListener(type, unlock, { capture: true, passive: true }));\n }\n };\n\n // Removes listeners, unlinks settings, stops loops and closes the context.\n // A later start()/play() starts the manager again.\n const dispose = () => {\n removeGestureListeners();\n if (unsubscribeSettings) { unsubscribeSettings(); unsubscribeSettings = null; }\n started = false;\n wantedLoops.clear();\n loops.forEach((loop) => { try { loop?.source?.stop(); } catch { /* already stopped */ } });\n loops.clear();\n buffers.clear();\n const ctx = context;\n context = null;\n gain = null;\n if (ctx && ctx.state !== 'closed') void ctx.close().catch(() => {});\n };\n\n return {\n start,\n dispose,\n // Fetches files ahead of time without touching the AudioContext.\n preload(names = Object.keys(sounds)) {\n start();\n names.forEach((name) => {\n if (!sounds[name] || missing.has(name)) return;\n void fetchRaw(name);\n if (context) void loadBuffer(name);\n });\n },\n // Returns true when the sound was scheduled (not muted, unlocked, not a duplicate).\n play(name, { key } = {}) {\n start();\n if (key != null && !remember(`${name}:${key}`)) return false;\n if (!sounds[name] || missing.has(name) || !unlocked || settings.muted || settings.volume === 0) return false;\n const ctx = ensureContext();\n if (!ctx) return false;\n const requestedAt = Date.now();\n void loadBuffer(name).then((buffer) => {\n if (!buffer || ctx.state === 'closed' || Date.now() - requestedAt > MAX_LATENCY_MS) return;\n try {\n const source = ctx.createBufferSource();\n source.buffer = buffer;\n const level = levelFor(name);\n if (level === 1) source.connect(gain);\n else {\n const node = ctx.createGain();\n node.gain.value = level;\n source.connect(node);\n node.connect(gain);\n }\n source.start();\n } catch { /* the game must keep working without audio */ }\n });\n return true;\n },\n // Marks a key as handled without playing (e.g. replays that must stay silent).\n markPlayed(name, key) { remember(`${name}:${key}`); },\n // Looped bed through the master gain, so mute/volume apply live. Requested\n // before the first gesture it starts on unlock; stopLoop fades it out.\n // Optional { fadeIn, fadeOut, trim } seconds; defaults keep the slow ambient fades.\n startLoop(name, options) {\n start();\n if (!sounds[name] || missing.has(name)) return;\n if (options) loopOptions.set(name, options);\n wantedLoops.add(name);\n beginLoop(name);\n },\n stopLoop(name) {\n wantedLoops.delete(name);\n endLoop(name);\n },\n // Before start() a follower reads the shared settings directly.\n getSettings: () => (settingsFrom && !started ? settingsFrom.getSettings() : settings),\n setVolume: settingsFrom ? settingsFrom.setVolume : (volume) => setSettings({ volume, muted: clamp(volume) === 0 ? settings.muted : false }),\n setMuted: settingsFrom ? settingsFrom.setMuted : (muted) => setSettings({ muted: Boolean(muted) }),\n toggleMuted: settingsFrom ? settingsFrom.toggleMuted : () => setSettings({ muted: !settings.muted }),\n subscribe(listener) {\n start();\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n\nexport default createSoundManager;\n","import { createSoundManager } from '../../audio/createSoundManager';\nimport { chatSoundUrl } from '../../assets/chatAssets';\nimport useChatGames from '../shared/useChatGames';\n\nexport const connect4Sounds = createSoundManager({\n id: 'connect4',\n baseUrl: () => chatSoundUrl('connect4'), // resolved lazily at fetch time\n sounds: {\n invite: 'invite.ogg', start: 'start.ogg', drop: 'drop.ogg', turn: 'turn.ogg',\n win: 'win.ogg', lose: 'lose.ogg', draw: 'draw.ogg', close: 'close.ogg',\n },\n});\n\n// Keep in sync with .chat-game-piece--new in games.css.\nconst DROP_ANIMATION_MS = 360;\n// Gap between the piece landing and the follow-up cue (turn/win/lose/draw).\nconst FOLLOW_UP_MS = 140;\nconst pendingDrops = new Map(); // eventId -> { game, timer }\nconst followUps = new Set(); // outcome timers scheduled after a drop\nlet stopSounds = null;\n\nconst same = (left, right) => left != null && right != null && String(left) === String(right);\nconst reducedMotion = () => typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n\nfunction playMoveOutcome(game, eventId) {\n if (game.status === 'won') connect4Sounds.play(same(game.winnerId, game.ownId) ? 'win' : 'lose', { key: eventId });\n else if (game.status === 'draw') connect4Sounds.play('draw', { key: eventId });\n // turnId is the next player, so this only fires after the rival's move.\n else if (game.status === 'active' && same(game.turnId, game.ownId)) connect4Sounds.play('turn', { key: eventId });\n}\n\n// Called when the piece animation ends (or by the fallback timer when the card\n// is not on screen). Plays the impact, then the outcome of that move.\nexport function settleConnect4Drop(eventId) {\n const entry = pendingDrops.get(eventId);\n if (!entry) return;\n clearTimeout(entry.timer);\n pendingDrops.delete(eventId);\n connect4Sounds.play('drop', { key: eventId });\n const timer = setTimeout(() => {\n followUps.delete(timer);\n playMoveOutcome(entry.game, eventId);\n }, FOLLOW_UP_MS);\n followUps.add(timer);\n}\n\nfunction scheduleDrop(game) {\n const eventId = game.lastMove?.eventId;\n if (!eventId || pendingDrops.has(eventId)) return;\n const delay = reducedMotion() ? 0 : DROP_ANIMATION_MS + 80;\n pendingDrops.set(eventId, { game, timer: setTimeout(() => settleConnect4Drop(eventId), delay) });\n}\n\nfunction onGameChange(game, before) {\n if (!before) {\n if (game.status === 'invited' && same(game.guestId, game.ownId)) connect4Sounds.play('invite', { key: game.inviteEventId });\n return;\n }\n if (before.status === 'invited' && game.status === 'active') {\n connect4Sounds.play('start', { key: game.lastEventId });\n return;\n }\n if (game.ply > before.ply) {\n scheduleDrop(game);\n return;\n }\n // Competing invitations are closed locally without closedBy: stay silent.\n const ended = (game.status === 'closed' && game.closedBy != null) || game.status === 'declined';\n if (ended && before.status !== game.status) connect4Sounds.play('close', { key: game.gameId });\n}\n\n// Sounds derive from store transitions, so WebSocket replays already rejected\n// by the store (seen event_id) never reach here; keys guard the rest.\n// Idempotent: repeated calls return the same stop(), which unsubscribes and\n// clears pending timers.\n/** True when startConnect4Sounds() is active (someone else owns its stop()). */\nexport const isConnect4SoundsRunning = () => stopSounds != null;\n\nexport function startConnect4Sounds() {\n if (stopSounds) return stopSounds;\n connect4Sounds.start();\n const unsubscribe = useChatGames.subscribe((state, previous) => {\n if (state.games === previous.games) return;\n Object.entries(state.games).forEach(([key, game]) => {\n const before = previous.games[key];\n // Other games (e.g. Battleship) have their own cues.\n if (game.game && game.game !== 'connect4') return;\n if (game !== before && game.ownId != null) onGameChange(game, before);\n });\n });\n const stop = () => {\n if (stopSounds !== stop) return;\n stopSounds = null;\n unsubscribe();\n pendingDrops.forEach(({ timer }) => clearTimeout(timer));\n pendingDrops.clear();\n followUps.forEach(clearTimeout);\n followUps.clear();\n };\n stopSounds = stop;\n return stop;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"connect4Sounds-3a6h7gUC.js","names":["useChatGames","useSoundSettings","useSyncExternalStore","manager","GameSoundControls","silent","muted","volume","jsx","VolumeX","Volume2","event","GESTURES","MAX_KEYS","MAX_LATENCY_MS","clamp","value","readSettings","stored","storageKey","defaults","LOOP_FADE_IN_S","LOOP_FADE_OUT_S","createSoundManager","id","AudioContextClass","settings","settingsFrom","defaultVolume","context","gain","unlocked","raw","buffers","missing","playedKeys","listeners","wantedLoops","loops","loopOptions","started","listening","unsubscribeSettings","urlFor","baseUrl","sounds","name","fetchRaw","response","ensureContext","loadBuffer","ctx","data","resolve","buffer","levelFor","levels","beginLoop","token","level","fadeIn","source","trim","endLoop","loop","fadeOut","now","unlock","removeGestureListeners","type","remember","key","emit","listener","applySettings","next","setSettings","patch","start","names","requestedAt","node","options","connect4Sounds","chatSoundUrl","FOLLOW_UP_MS","pendingDrops","followUps","stopSounds","same","left","right","reducedMotion","playMoveOutcome","game","eventId","settleConnect4Drop","entry","timer","scheduleDrop","delay","onGameChange","before","isConnect4SoundsRunning","startConnect4Sounds","unsubscribe","state","previous","stop"],"sources":["../../src/audio/useSoundSettings.js","../../src/games/shared/GameSoundControls.jsx","../../src/audio/createSoundManager.js","../../src/games/connect4/connect4Sounds.js"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\n// React binding for a sound manager's volume/mute settings.\nexport default function useSoundSettings(manager) {\n return useSyncExternalStore(manager.subscribe, manager.getSettings, manager.getSettings);\n}\n","/* eslint-disable react/prop-types */\nimport { Volume2, VolumeX } from 'lucide-react';\nimport useSoundSettings from '../../audio/useSoundSettings';\n\nexport default function GameSoundControls({ manager }) {\n const { volume, muted } = useSoundSettings(manager);\n const silent = muted || volume === 0;\n return (\n <div className=\"chat-game-sound\">\n <button type=\"button\" className=\"chat-game-sound-toggle\" onClick={manager.toggleMuted}\n aria-pressed={silent} aria-label={silent ? 'Activar sonido del juego' : 'Silenciar sonido del juego'} title={silent ? 'Activar sonido' : 'Silenciar'}>\n {silent ? <VolumeX size={15} /> : <Volume2 size={15} />}\n </button>\n <input type=\"range\" min=\"0\" max=\"100\" step=\"5\" value={Math.round((muted ? 0 : volume) * 100)}\n onChange={(event) => manager.setVolume(Number(event.target.value) / 100)} aria-label=\"Volumen del juego\" />\n </div>\n );\n}\n","// Reusable game sound manager built on Web Audio (no dependencies).\n// - Lazy: files are fetched on preload()/first play and decoded once.\n// - Autoplay-safe: the AudioContext is created/resumed only after a user\n// gesture; sounds requested before that are dropped, never queued.\n// - Fault-tolerant: a missing or undecodable file is marked unavailable and\n// every later play() of it is a silent no-op.\n// - Deduplicated: play(name, { key }) ignores keys already played recently.\n// - Optional per-sound levels, looped beds (startLoop/stopLoop) and settings\n// shared with another manager (settingsFrom) so games keep one preference.\n// - Side-effect free at creation (SSR-safe): gesture listeners and the\n// settingsFrom subscription are registered on start() or the first\n// preload()/play()/startLoop()/subscribe(); dispose() tears everything down.\n\nconst GESTURES = ['pointerdown', 'keydown', 'touchend'];\nconst MAX_KEYS = 300;\n// Sounds still loading when requested are dropped if they would play too late.\nconst MAX_LATENCY_MS = 800;\n\nconst clamp = (value) => Math.min(1, Math.max(0, Number(value) || 0));\n\nfunction readSettings(storageKey, defaults) {\n try {\n const stored = JSON.parse(localStorage.getItem(storageKey) ?? 'null');\n if (stored && typeof stored === 'object') {\n return { volume: clamp(stored.volume ?? defaults.volume), muted: Boolean(stored.muted) };\n }\n } catch { /* storage unavailable */ }\n return { volume: clamp(defaults.volume), muted: false };\n}\n\nconst LOOP_FADE_IN_S = 1.5;\nconst LOOP_FADE_OUT_S = 0.6;\n\nexport function createSoundManager({ id, baseUrl, sounds, levels = {}, defaultVolume = 0.6, settingsFrom = null }) {\n const storageKey = `lira.sound.${id}`;\n const AudioContextClass = typeof window !== 'undefined' ? (window.AudioContext || window.webkitAudioContext) : null;\n let settings = settingsFrom ? settingsFrom.getSettings() : readSettings(storageKey, { volume: defaultVolume });\n let context = null;\n let gain = null;\n let unlocked = false;\n const raw = new Map(); // name -> Promise<ArrayBuffer | null>\n const buffers = new Map(); // name -> Promise<AudioBuffer | null>\n const missing = new Set();\n const playedKeys = new Set();\n const listeners = new Set();\n const wantedLoops = new Set();\n const loops = new Map(); // name -> { source, level } once playing\n const loopOptions = new Map(); // name -> { fadeIn, fadeOut, trim } from startLoop\n let started = false;\n let listening = false;\n let unsubscribeSettings = null;\n\n // baseUrl may be a function so hosts can configure asset locations after import.\n const urlFor = (name) => `${String(typeof baseUrl === 'function' ? baseUrl() : baseUrl).replace(/\\/$/, '')}/${sounds[name]}`;\n\n const fetchRaw = (name) => {\n if (!raw.has(name)) {\n raw.set(name, fetch(urlFor(name))\n .then((response) => (response.ok ? response.arrayBuffer() : null))\n .catch(() => null));\n }\n return raw.get(name);\n };\n\n const ensureContext = () => {\n if (!AudioContextClass) return null;\n if (!context) {\n try {\n context = new AudioContextClass();\n gain = context.createGain();\n gain.gain.value = settings.muted ? 0 : settings.volume;\n gain.connect(context.destination);\n } catch {\n context = null;\n return null;\n }\n }\n if (context.state === 'suspended') void context.resume().catch(() => {});\n return context;\n };\n\n const loadBuffer = (name) => {\n if (missing.has(name) || !sounds[name]) return Promise.resolve(null);\n if (!buffers.has(name)) {\n const ctx = ensureContext();\n if (!ctx) return Promise.resolve(null);\n buffers.set(name, fetchRaw(name).then((data) => {\n // decodeAudioData detaches the buffer, so decode a copy.\n if (!data) return null;\n return new Promise((resolve) => {\n // Modern browsers also return a promise that rejects alongside the error\n // callback; without this catch a missing/HTML file is an unhandled rejection.\n ctx.decodeAudioData(data.slice(0), resolve, () => resolve(null))?.catch?.(() => resolve(null));\n });\n }).then((buffer) => {\n if (!buffer) missing.add(name);\n return buffer;\n }).catch(() => {\n missing.add(name);\n return null;\n }));\n }\n return buffers.get(name);\n };\n\n const levelFor = (name) => clamp(levels[name] ?? 1);\n\n const beginLoop = (name) => {\n if (!wantedLoops.has(name) || loops.has(name) || !unlocked) return;\n const ctx = ensureContext();\n if (!ctx) return;\n const token = {}; // a stop/start while loading must not start a stale copy\n loops.set(name, token);\n void loadBuffer(name).then((buffer) => {\n if (loops.get(name) !== token) return;\n if (!buffer || ctx.state === 'closed') { loops.delete(name); return; }\n try {\n const { fadeIn = LOOP_FADE_IN_S, trim = 0 } = loopOptions.get(name) ?? {};\n const level = ctx.createGain();\n level.gain.setValueAtTime(0, ctx.currentTime);\n level.gain.linearRampToValueAtTime(levelFor(name), ctx.currentTime + fadeIn);\n level.connect(gain);\n const source = ctx.createBufferSource();\n source.buffer = buffer;\n source.loop = true;\n // Skips encoder padding at both ends (MP3) so the loop has no audible gap.\n if (trim > 0 && buffer.duration > trim * 4) {\n source.loopStart = trim;\n source.loopEnd = buffer.duration - trim;\n }\n source.connect(level);\n source.start(0, trim > 0 && buffer.duration > trim * 4 ? trim : 0);\n loops.set(name, { source, level });\n } catch {\n loops.delete(name);\n }\n });\n };\n\n const endLoop = (name) => {\n const loop = loops.get(name);\n loops.delete(name);\n if (!loop?.source || !context) return;\n try {\n const fadeOut = loopOptions.get(name)?.fadeOut ?? LOOP_FADE_OUT_S;\n const now = context.currentTime;\n loop.level.gain.cancelScheduledValues(now);\n loop.level.gain.setValueAtTime(loop.level.gain.value, now);\n loop.level.gain.linearRampToValueAtTime(0, now + fadeOut);\n loop.source.stop(now + fadeOut + 0.05);\n } catch { /* already stopped */ }\n };\n\n const unlock = () => {\n if (unlocked) return;\n unlocked = true;\n removeGestureListeners();\n if (ensureContext()) Object.keys(sounds).forEach((name) => { if (raw.has(name)) void loadBuffer(name); });\n wantedLoops.forEach(beginLoop);\n };\n\n function removeGestureListeners() {\n if (!listening) return;\n listening = false;\n GESTURES.forEach((type) => window.removeEventListener(type, unlock, true));\n }\n\n const remember = (key) => {\n if (playedKeys.has(key)) return false;\n playedKeys.add(key);\n if (playedKeys.size > MAX_KEYS) playedKeys.delete(playedKeys.values().next().value);\n return true;\n };\n\n const emit = () => listeners.forEach((listener) => listener());\n\n const applySettings = (next) => {\n settings = next;\n if (gain) gain.gain.value = settings.muted ? 0 : settings.volume;\n emit();\n };\n\n const setSettings = (patch) => {\n const next = { volume: clamp(patch.volume ?? settings.volume), muted: patch.muted ?? settings.muted };\n try { localStorage.setItem(storageKey, JSON.stringify(next)); } catch { /* storage unavailable */ }\n applySettings(next);\n };\n\n // Idempotent; registers the unlock listeners and the shared-settings link.\n const start = () => {\n if (started) return;\n started = true;\n if (settingsFrom) {\n settings = settingsFrom.getSettings();\n unsubscribeSettings = settingsFrom.subscribe(() => applySettings(settingsFrom.getSettings()));\n }\n if (typeof window === 'undefined' || unlocked) return;\n // Sticky activation: a gesture already happened before the manager started.\n if (typeof navigator !== 'undefined' && navigator.userActivation?.hasBeenActive) unlocked = true;\n else {\n listening = true;\n GESTURES.forEach((type) => window.addEventListener(type, unlock, { capture: true, passive: true }));\n }\n };\n\n // Removes listeners, unlinks settings, stops loops and closes the context.\n // A later start()/play() starts the manager again.\n const dispose = () => {\n removeGestureListeners();\n if (unsubscribeSettings) { unsubscribeSettings(); unsubscribeSettings = null; }\n started = false;\n wantedLoops.clear();\n loops.forEach((loop) => { try { loop?.source?.stop(); } catch { /* already stopped */ } });\n loops.clear();\n buffers.clear();\n const ctx = context;\n context = null;\n gain = null;\n if (ctx && ctx.state !== 'closed') void ctx.close().catch(() => {});\n };\n\n return {\n start,\n dispose,\n // Fetches files ahead of time without touching the AudioContext.\n preload(names = Object.keys(sounds)) {\n start();\n names.forEach((name) => {\n if (!sounds[name] || missing.has(name)) return;\n void fetchRaw(name);\n if (context) void loadBuffer(name);\n });\n },\n // Returns true when the sound was scheduled (not muted, unlocked, not a duplicate).\n play(name, { key } = {}) {\n start();\n if (key != null && !remember(`${name}:${key}`)) return false;\n if (!sounds[name] || missing.has(name) || !unlocked || settings.muted || settings.volume === 0) return false;\n const ctx = ensureContext();\n if (!ctx) return false;\n const requestedAt = Date.now();\n void loadBuffer(name).then((buffer) => {\n if (!buffer || ctx.state === 'closed' || Date.now() - requestedAt > MAX_LATENCY_MS) return;\n try {\n const source = ctx.createBufferSource();\n source.buffer = buffer;\n const level = levelFor(name);\n if (level === 1) source.connect(gain);\n else {\n const node = ctx.createGain();\n node.gain.value = level;\n source.connect(node);\n node.connect(gain);\n }\n source.start();\n } catch { /* the game must keep working without audio */ }\n });\n return true;\n },\n // Marks a key as handled without playing (e.g. replays that must stay silent).\n markPlayed(name, key) { remember(`${name}:${key}`); },\n // Looped bed through the master gain, so mute/volume apply live. Requested\n // before the first gesture it starts on unlock; stopLoop fades it out.\n // Optional { fadeIn, fadeOut, trim } seconds; defaults keep the slow ambient fades.\n startLoop(name, options) {\n start();\n if (!sounds[name] || missing.has(name)) return;\n if (options) loopOptions.set(name, options);\n wantedLoops.add(name);\n beginLoop(name);\n },\n stopLoop(name) {\n wantedLoops.delete(name);\n endLoop(name);\n },\n // Before start() a follower reads the shared settings directly.\n getSettings: () => (settingsFrom && !started ? settingsFrom.getSettings() : settings),\n setVolume: settingsFrom ? settingsFrom.setVolume : (volume) => setSettings({ volume, muted: clamp(volume) === 0 ? settings.muted : false }),\n setMuted: settingsFrom ? settingsFrom.setMuted : (muted) => setSettings({ muted: Boolean(muted) }),\n toggleMuted: settingsFrom ? settingsFrom.toggleMuted : () => setSettings({ muted: !settings.muted }),\n subscribe(listener) {\n start();\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n\nexport default createSoundManager;\n","import { createSoundManager } from '../../audio/createSoundManager';\nimport { chatSoundUrl } from '../../assets/chatAssets';\nimport useChatGames from '../shared/useChatGames';\n\nexport const connect4Sounds = createSoundManager({\n id: 'connect4',\n baseUrl: () => chatSoundUrl('connect4'), // resolved lazily at fetch time\n sounds: {\n invite: 'invite.ogg', start: 'start.ogg', drop: 'drop.ogg', turn: 'turn.ogg',\n win: 'win.ogg', lose: 'lose.ogg', draw: 'draw.ogg', close: 'close.ogg',\n },\n});\n\n// Keep in sync with .chat-game-piece--new in games.css.\nconst DROP_ANIMATION_MS = 360;\n// Gap between the piece landing and the follow-up cue (turn/win/lose/draw).\nconst FOLLOW_UP_MS = 140;\nconst pendingDrops = new Map(); // eventId -> { game, timer }\nconst followUps = new Set(); // outcome timers scheduled after a drop\nlet stopSounds = null;\n\nconst same = (left, right) => left != null && right != null && String(left) === String(right);\nconst reducedMotion = () => typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n\nfunction playMoveOutcome(game, eventId) {\n if (game.status === 'won') connect4Sounds.play(same(game.winnerId, game.ownId) ? 'win' : 'lose', { key: eventId });\n else if (game.status === 'draw') connect4Sounds.play('draw', { key: eventId });\n // turnId is the next player, so this only fires after the rival's move.\n else if (game.status === 'active' && same(game.turnId, game.ownId)) connect4Sounds.play('turn', { key: eventId });\n}\n\n// Called when the piece animation ends (or by the fallback timer when the card\n// is not on screen). Plays the impact, then the outcome of that move.\nexport function settleConnect4Drop(eventId) {\n const entry = pendingDrops.get(eventId);\n if (!entry) return;\n clearTimeout(entry.timer);\n pendingDrops.delete(eventId);\n connect4Sounds.play('drop', { key: eventId });\n const timer = setTimeout(() => {\n followUps.delete(timer);\n playMoveOutcome(entry.game, eventId);\n }, FOLLOW_UP_MS);\n followUps.add(timer);\n}\n\nfunction scheduleDrop(game) {\n const eventId = game.lastMove?.eventId;\n if (!eventId || pendingDrops.has(eventId)) return;\n const delay = reducedMotion() ? 0 : DROP_ANIMATION_MS + 80;\n pendingDrops.set(eventId, { game, timer: setTimeout(() => settleConnect4Drop(eventId), delay) });\n}\n\nfunction onGameChange(game, before) {\n if (!before) {\n if (game.status === 'invited' && same(game.guestId, game.ownId)) connect4Sounds.play('invite', { key: game.inviteEventId });\n return;\n }\n if (before.status === 'invited' && game.status === 'active') {\n connect4Sounds.play('start', { key: game.lastEventId });\n return;\n }\n if (game.ply > before.ply) {\n scheduleDrop(game);\n return;\n }\n // Competing invitations are closed locally without closedBy: stay silent.\n const ended = (game.status === 'closed' && game.closedBy != null) || game.status === 'declined';\n if (ended && before.status !== game.status) connect4Sounds.play('close', { key: game.gameId });\n}\n\n// Sounds derive from store transitions, so WebSocket replays already rejected\n// by the store (seen event_id) never reach here; keys guard the rest.\n// Idempotent: repeated calls return the same stop(), which unsubscribes and\n// clears pending timers.\n/** True when startConnect4Sounds() is active (someone else owns its stop()). */\nexport const isConnect4SoundsRunning = () => stopSounds != null;\n\nexport function startConnect4Sounds() {\n if (stopSounds) return stopSounds;\n connect4Sounds.start();\n const unsubscribe = useChatGames.subscribe((state, previous) => {\n if (state.games === previous.games) return;\n Object.entries(state.games).forEach(([key, game]) => {\n const before = previous.games[key];\n // Other games (e.g. Battleship) have their own cues.\n if (game.game && game.game !== 'connect4') return;\n if (game !== before && game.ownId != null) onGameChange(game, before);\n });\n });\n const stop = () => {\n if (stopSounds !== stop) return;\n stopSounds = null;\n unsubscribe();\n pendingDrops.forEach(({ timer }) => clearTimeout(timer));\n pendingDrops.clear();\n followUps.forEach(clearTimeout);\n followUps.clear();\n };\n stopSounds = stop;\n return stop;\n}\n"],"mappings":";;;;;AAGA,SAAwBC,EAAiB,GAAS;CAChD,OAAOC,EAAqBC,EAAQ,WAAWA,EAAQ,aAAaA,EAAQ,WAAW;AACzF;;ACDA,SAAwBC,EAAkB,EAAE,cAAW;CACrD,IAAM,EAAE,WAAQ,aAAUH,EAAiBE,CAAO,GAC5CE,IAASC,KAASC,MAAW;CACnC,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAU;GAAyB,SAASJ,EAAQ;GACxE,gBAAcE;GAAQ,cAAYA,IAAS,6BAA6B;GAA8B,OAAOA,IAAS,mBAAmB;GACxI,UAASG,EAATH,IAAUI,IAAwBC,GAAzB,EAAS,MAAM,GAAK,CAAwB;EAChD,CAAA,GACR,kBAAC,SAAD;GAAO,MAAK;GAAQ,KAAI;GAAI,KAAI;GAAM,MAAK;GAAI,OAAO,KAAK,OAAOJ,IAAQ,IAAIC,KAAU,GAAG;GACzF,WAAW,MAAUJ,EAAQ,UAAU,OAAOQ,EAAM,OAAO,KAAK,IAAI,GAAG;GAAG,cAAW;EAAqB,CAAA,CACzG;;AAET;;ACJA,IAAMC,IAAW;CAAC;CAAe;CAAW;AAAU,GAChDC,IAAW,KAEXC,IAAiB,KAEjBC,KAAS,MAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAOC,CAAK,KAAK,CAAC,CAAC;AAEpE,SAASC,EAAa,GAAY,GAAU;CAC1C,IAAI;EACF,IAAMC,IAAS,KAAK,MAAM,aAAa,QAAQC,CAAU,KAAK,MAAM;EACpE,IAAID,KAAU,OAAOA,KAAW,UAC9B,OAAO;GAAE,QAAQH,EAAMG,EAAO,UAAUE,EAAS,MAAM;GAAG,OAAO,EAAQF,EAAO;EAAO;CAE3F,QAAQ,CAA4B;CACpC,OAAO;EAAE,QAAQH,EAAMK,EAAS,MAAM;EAAG,OAAO;CAAM;AACxD;AAEA,IAAMC,IAAiB,KACjBC,IAAkB;AAExB,SAAgBC,EAAmB,EAAE,OAAI,YAAS,WAAQ,YAAS,CAAC,GAAG,mBAAgB,IAAK,kBAAe,QAAQ;CACjH,IAAMJ,IAAa,cAAcK,KAC3BC,IAAoB,OAAO,SAAW,MAAe,OAAO,gBAAgB,OAAO,qBAAsB,MAC3GC,IAAWC,IAAeA,EAAa,YAAY,IAAIV,EAAaE,GAAY,EAAE,QAAQS,EAAc,CAAC,GACzGC,IAAU,MACVC,IAAO,MACPC,IAAW,IACTC,oBAAM,IAAI,IAAI,GACdC,oBAAU,IAAI,IAAI,GAClBC,oBAAU,IAAI,IAAI,GAClBC,oBAAa,IAAI,IAAI,GACrBC,oBAAY,IAAI,IAAI,GACpBC,oBAAc,IAAI,IAAI,GACtBC,oBAAQ,IAAI,IAAI,GAChBC,oBAAc,IAAI,IAAI,GACxBC,IAAU,IACVC,IAAY,IACZC,IAAsB,MAGpBC,KAAU,MAAS,GAAG,OAAO,OAAOC,KAAY,aAAaA,EAAQ,IAAIA,CAAO,CAAC,CAAC,QAAQ,OAAO,EAAE,EAAE,GAAGC,EAAOC,MAE/GC,KAAY,OACXf,EAAI,IAAIc,CAAI,KACfd,EAAI,IAAIc,GAAM,MAAMH,EAAOG,CAAI,CAAC,CAAC,CAC9B,MAAM,MAAcE,EAAS,KAAKA,EAAS,YAAY,IAAI,IAAK,CAAC,CACjE,YAAY,IAAI,CAAC,GAEfhB,EAAI,IAAIc,CAAI,IAGfG,UAAsB;EAC1B,IAAI,CAACxB,GAAmB,OAAO;EAC/B,IAAI,CAACI,GACH,IAAI;GAIF,AAHA,IAAU,IAAIJ,EAAkB,GAChC,IAAOI,EAAQ,WAAW,GAC1B,EAAK,KAAK,QAAQH,EAAS,QAAQ,IAAIA,EAAS,QAChDI,EAAK,QAAQD,EAAQ,WAAW;EAClC,QAAQ;GAEN,OADA,IAAU,MACH;EACT;EAGF,OADIA,EAAQ,UAAU,eAAaA,EAAa,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,GAChEA;CACT,GAEMqB,KAAc,MAAS;EAC3B,IAAIhB,EAAQ,IAAIY,CAAI,KAAK,CAACD,EAAOC,IAAO,OAAO,QAAQ,QAAQ,IAAI;EACnE,IAAI,CAACb,EAAQ,IAAIa,CAAI,GAAG;GACtB,IAAMK,IAAMF,EAAc;GAC1B,IAAI,CAACE,GAAK,OAAO,QAAQ,QAAQ,IAAI;GACrC,EAAQ,IAAIL,GAAMC,EAASD,CAAI,CAAC,CAAC,MAAM,MAEhCM,IACE,IAAI,SAAS,MAAY;IAG9B,EAAI,gBAAgBA,EAAK,MAAM,CAAC,GAAGC,SAAeA,EAAQ,IAAI,CAAC,CAAC,EAAE,cAAcA,EAAQ,IAAI,CAAC;GAC/F,CAAC,IALiB,IAMnB,CAAC,CAAC,MAAM,OACFC,KAAQpB,EAAQ,IAAIY,CAAI,GACtBQ,EACR,CAAC,CAAC,aACDpB,EAAQ,IAAIY,CAAI,GACT,KACR,CAAC;EACJ;EACA,OAAOb,EAAQ,IAAIa,CAAI;CACzB,GAEMS,KAAY,MAASxC,EAAMyC,EAAOV,MAAS,CAAC,GAE5CW,KAAa,MAAS;EAC1B,IAAI,CAACpB,EAAY,IAAIS,CAAI,KAAKR,EAAM,IAAIQ,CAAI,KAAK,CAACf,GAAU;EAC5D,IAAMoB,IAAMF,EAAc;EAC1B,IAAI,CAACE,GAAK;EACV,IAAMO,IAAQ,CAAC;EAEf,AADApB,EAAM,IAAIQ,GAAMY,CAAK,GACrBR,EAAgBJ,CAAI,CAAC,CAAC,MAAM,MAAW;GACrC,IAAIR,EAAM,IAAIQ,CAAI,MAAMY,GACxB;QAAI,CAACJ,KAAUH,EAAI,UAAU,UAAY,EAAM,OAAOL,CAAI;SAC1D,IAAI;KACF,IAAM,EAAE,YAASzB,GAAgB,UAAO,MAAMkB,EAAY,IAAIO,CAAI,KAAK,CAAC,GAClEa,IAAQR,EAAI,WAAW;KAG7B,AAFAQ,EAAM,KAAK,eAAe,GAAGR,EAAI,WAAW,GAC5CQ,EAAM,KAAK,wBAAwBJ,EAAST,CAAI,GAAGK,EAAI,cAAcS,CAAM,GAC3ED,EAAM,QAAQ7B,CAAI;KAClB,IAAM+B,IAASV,EAAI,mBAAmB;KAUtC,AATA,EAAO,SAASG,GAChB,EAAO,OAAO,IAEVQ,IAAO,KAAKR,EAAO,WAAWQ,IAAO,MACvC,EAAO,YAAYA,GACnB,EAAO,UAAUR,EAAO,WAAWQ,IAErCD,EAAO,QAAQF,CAAK,GACpBE,EAAO,MAAM,GAAGC,IAAO,KAAKR,EAAO,WAAWQ,IAAO,IAAIA,IAAO,CAAC,GACjExB,EAAM,IAAIQ,GAAM;MAAE;MAAQ;KAAM,CAAC;IACnC,QAAQ;KACN,EAAM,OAAOA,CAAI;IACnB;GApBqE;EAqBvE,CAAC;CACH,GAEMiB,KAAW,MAAS;EACxB,IAAMC,IAAO1B,EAAM,IAAIQ,CAAI;EAE3B,IADAR,EAAM,OAAOQ,CAAI,GACZkB,GAAM,UAAWnC,GACtB,IAAI;GACF,IAAMoC,IAAU1B,EAAY,IAAIO,CAAI,CAAC,EAAE,WAAWxB,GAC5C4C,IAAMrC,EAAQ;GAIpB,AAHAmC,EAAK,MAAM,KAAK,sBAAsBE,CAAG,GACzCF,EAAK,MAAM,KAAK,eAAeA,EAAK,MAAM,KAAK,OAAOE,CAAG,GACzDF,EAAK,MAAM,KAAK,wBAAwB,GAAGE,IAAMD,CAAO,GACxDD,EAAK,OAAO,KAAKE,IAAMD,IAAU,GAAI;EACvC,QAAQ,CAAwB;CAClC,GAEME,UAAe;EACnB,AAAIpC,MACJ,IAAW,IACXqC,EAAuB,GACnBnB,EAAc,KAAG,OAAO,KAAKJ,CAAM,CAAC,CAAC,SAAS,MAAS;GAAE,AAAIb,EAAI,IAAIc,CAAI,KAAGI,EAAgBJ,CAAI;EAAG,CAAC,GACxGT,EAAY,QAAQoB,CAAS;CAC/B;CAEA,SAASW,IAAyB;EAChC,AAAK3B,MACL,IAAY,IACZ7B,EAAS,SAAS,MAAS,OAAO,oBAAoByD,GAAMF,GAAQ,EAAI,CAAC;CAC3E;CAEA,IAAMG,KAAY,MAChB,CAAInC,EAAW,IAAIoC,CAAG,MACtBpC,EAAW,IAAIoC,CAAG,GACdpC,EAAW,OAAOtB,KAAUsB,EAAW,OAAOA,EAAW,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAC3E,KAGHqC,UAAapC,EAAU,SAAS,MAAaqC,EAAS,CAAC,GAEvDC,KAAiB,MAAS;EAG9B,AAFA,IAAWC,GACP7C,MAAM,EAAK,KAAK,QAAQJ,EAAS,QAAQ,IAAIA,EAAS,SAC1D8C,EAAK;CACP,GAEMI,KAAe,MAAU;EAC7B,IAAMD,IAAO;GAAE,QAAQ5D,EAAM8D,EAAM,UAAUnD,EAAS,MAAM;GAAG,OAAOmD,EAAM,SAASnD,EAAS;EAAM;EACpG,IAAI;GAAE,aAAa,QAAQP,GAAY,KAAK,UAAUwD,CAAI,CAAC;EAAG,QAAQ,CAA4B;EAClG,EAAcA,CAAI;CACpB,GAGMG,UAAc;EAClB,AAAItC,MACJ,IAAU,IACNb,MACF,IAAWA,EAAa,YAAY,GACpC,IAAsBA,EAAa,gBAAgB+C,EAAc/C,EAAa,YAAY,CAAC,CAAC,IAE1F,SAAO,SAAW,OAAeI,OAEjC,OAAO,YAAc,OAAe,UAAU,gBAAgB,gBAAe,IAAW,MAE1F,IAAY,IACZnB,EAAS,SAAS,MAAS,OAAO,iBAAiByD,GAAMF,GAAQ;GAAE,SAAS;GAAM,SAAS;EAAK,CAAC,CAAC;CAEtG;CAkBA,OAAO;EACL;EACA,eAhBoB;GAOpB,AANAC,EAAuB,GACnB1B,MAAuBA,EAAoB,GAAG,IAAsB,OACxE,IAAU,IACVL,EAAY,MAAM,GAClBC,EAAM,SAAS,MAAS;IAAE,IAAI;KAAE,GAAM,QAAQ,KAAK;IAAG,QAAQ,CAAwB;GAAE,CAAC,GACzFA,EAAM,MAAM,GACZL,EAAQ,MAAM;GACd,IAAMkB,IAAMtB;GAGZ,AAFA,IAAU,MACV,IAAO,MACHsB,KAAOA,EAAI,UAAU,YAAUA,EAAS,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EACpE;EAME,QAAQ,IAAQ,OAAO,KAAKN,CAAM,GAAG;GAEnC,AADAiC,EAAM,GACNC,EAAM,SAAS,MAAS;IACtB,AAAKlC,EAAOC,MAAS,GAAQ,IAAIA,CAAI,MACrCC,EAAcD,CAAI,GACdjB,KAASqB,EAAgBJ,CAAI;GACnC,CAAC;EACH;EAEA,KAAK,GAAM,EAAE,WAAQ,CAAC,GAAG;GAGvB,IAFAgC,EAAM,GACFP,KAAO,QAAQ,CAACD,EAAS,GAAGxB,EAAK,GAAGyB,GAAK,KACzC,CAAC1B,EAAOC,MAASZ,EAAQ,IAAIY,CAAI,KAAK,CAACf,KAAYL,EAAS,SAASA,EAAS,WAAW,GAAG,OAAO;GACvG,IAAMyB,IAAMF,EAAc;GAC1B,IAAI,CAACE,GAAK,OAAO;GACjB,IAAM6B,IAAc,KAAK,IAAI;GAiB7B,OAhBA9B,EAAgBJ,CAAI,CAAC,CAAC,MAAM,MAAW;IACrC,IAAI,GAACQ,KAAUH,EAAI,UAAU,YAAY,KAAK,IAAI,IAAI6B,IAAclE,IACpE,IAAI;KACF,IAAM+C,IAASV,EAAI,mBAAmB;KACtC,EAAO,SAASG;KAChB,IAAMK,IAAQJ,EAAST,CAAI;KAC3B,IAAIa,MAAU,GAAG,EAAO,QAAQ7B,CAAI;UAC/B;MACH,IAAMmD,IAAO9B,EAAI,WAAW;MAG5B,AAFA,EAAK,KAAK,QAAQQ,GAClBE,EAAO,QAAQoB,CAAI,GACnBA,EAAK,QAAQnD,CAAI;KACnB;KACA,EAAO,MAAM;IACf,QAAQ,CAAiD;GAC3D,CAAC,GACM;EACT;EAEA,WAAW,GAAM,GAAK;GAAE,EAAS,GAAGgB,EAAK,GAAGyB,GAAK;EAAG;EAIpD,UAAU,GAAM,GAAS;GAEvB,AADAO,EAAM,GACDjC,EAAOC,MAAS,GAAQ,IAAIA,CAAI,MACjCoC,KAAS3C,EAAY,IAAIO,GAAMoC,CAAO,GAC1C7C,EAAY,IAAIS,CAAI,GACpBW,EAAUX,CAAI;EAChB;EACA,SAAS,GAAM;GAEb,AADAT,EAAY,OAAOS,CAAI,GACvBiB,EAAQjB,CAAI;EACd;EAEA,mBAAoBnB,KAAgB,CAACa,IAAUb,EAAa,YAAY,IAAID;EAC5E,WAAWC,IAAeA,EAAa,aAAa,MAAWiD,EAAY;GAAE;GAAQ,OAAO7D,EAAMR,CAAM,MAAM,KAAImB,EAAS;EAAc,CAAC;EAC1I,UAAUC,IAAeA,EAAa,YAAY,MAAUiD,EAAY,EAAE,OAAO,EAAQtE,EAAO,CAAC;EACjG,aAAaqB,IAAeA,EAAa,oBAAoBiD,EAAY,EAAE,OAAO,CAAClD,EAAS,MAAM,CAAC;EACnG,UAAU,GAAU;GAGlB,OAFAoD,EAAM,GACN1C,EAAU,IAAIqC,CAAQ,SACTrC,EAAU,OAAOqC,CAAQ;EACxC;CACF;AACF;;AC1RA,IAAaU,IAAiB5D,EAAmB;CAC/C,IAAI;CACJ,eAAe6D,EAAa,UAAU;CACtC,QAAQ;EACN,QAAQ;EAAc,OAAO;EAAa,MAAM;EAAY,MAAM;EAClE,KAAK;EAAW,MAAM;EAAY,MAAM;EAAY,OAAO;CAC7D;AACF,CAAC,GAKKC,IAAe,KACfC,oBAAe,IAAI,IAAI,GACvBC,oBAAY,IAAI,IAAI,GACtBC,IAAa,MAEXC,KAAQ,GAAM,MAAUC,KAAQ,QAAQC,KAAS,QAAQ,OAAOD,CAAI,MAAM,OAAOC,CAAK,GACtFC,UAAsB,OAAO,SAAW,OAAe,OAAO,aAAa,kCAAkC,CAAC,CAAC;AAErH,SAASC,EAAgB,GAAM,GAAS;CACtC,AAAIC,EAAK,WAAW,QAAOX,EAAe,KAAKM,EAAKK,EAAK,UAAUA,EAAK,KAAK,IAAI,QAAQ,QAAQ,EAAE,KAAKC,EAAQ,CAAC,IACxGD,EAAK,WAAW,SAAQX,EAAe,KAAK,QAAQ,EAAE,KAAKY,EAAQ,CAAC,IAEpED,EAAK,WAAW,YAAYL,EAAKK,EAAK,QAAQA,EAAK,KAAK,KAAGX,EAAe,KAAK,QAAQ,EAAE,KAAKY,EAAQ,CAAC;AAClH;AAIA,SAAgBC,EAAmB,GAAS;CAC1C,IAAMC,IAAQX,EAAa,IAAIS,CAAO;CACtC,IAAI,CAACE,GAAO;CAGZ,AAFA,aAAaA,EAAM,KAAK,GACxBX,EAAa,OAAOS,CAAO,GAC3BZ,EAAe,KAAK,QAAQ,EAAE,KAAKY,EAAQ,CAAC;CAC5C,IAAMG,IAAQ,iBAAiB;EAE7B,AADAX,EAAU,OAAOW,CAAK,GACtBL,EAAgBI,EAAM,MAAMF,CAAO;CACrC,GAAGV,CAAY;CACf,EAAU,IAAIa,CAAK;AACrB;AAEA,SAASC,EAAa,GAAM;CAC1B,IAAMJ,IAAUD,EAAK,UAAU;CAC/B,IAAI,CAACC,KAAWT,EAAa,IAAIS,CAAO,GAAG;CAC3C,IAAMK,IAAQR,EAAc,IAAI,IAAI;CACpC,EAAa,IAAIG,GAAS;EAAE;EAAM,OAAO,iBAAiBC,EAAmBD,CAAO,GAAGK,CAAK;CAAE,CAAC;AACjG;AAEA,SAASC,EAAa,GAAM,GAAQ;CAClC,AAAKC,IAIDA,EAAO,WAAW,aAAaR,EAAK,WAAW,WACjDX,EAAe,KAAK,SAAS,EAAE,KAAKW,EAAK,YAAY,CAAC,IAGpDA,EAAK,MAAMQ,EAAO,MACpBH,EAAaL,CAAI,KAIJA,EAAK,WAAW,YAAYA,EAAK,YAAY,QAASA,EAAK,WAAW,eACxEQ,EAAO,WAAWR,EAAK,UAAQX,EAAe,KAAK,SAAS,EAAE,KAAKW,EAAK,OAAO,CAAC,IAbvFA,EAAK,WAAW,aAAaL,EAAKK,EAAK,SAASA,EAAK,KAAK,KAAGX,EAAe,KAAK,UAAU,EAAE,KAAKW,EAAK,cAAc,CAAC;AAc9H;AAOA,IAAaS,UAAgCf,KAAc;AAE3D,SAAgBgB,IAAsB;CACpC,IAAIhB,GAAY,OAAOA;CACvB,EAAe,MAAM;CACrB,IAAMiB,IAAczG,EAAa,WAAW,GAAO,MAAa;EAC9D,AAAI0G,EAAM,UAAUC,EAAS,SAC7B,OAAO,QAAQD,EAAM,KAAK,CAAC,CAAC,SAAS,CAACnC,GAAKuB,OAAU;GACnD,IAAMQ,IAASK,EAAS,MAAMpC;GAE9B,AAAIuB,EAAK,QAAQA,EAAK,SAAS,cAC3BA,MAASQ,KAAUR,EAAK,SAAS,QAAMO,EAAaP,GAAMQ,CAAM;EACtE,CAAC;CACH,CAAC,GACKM,UAAa;EACjB,AAAIpB,MAAeoB,MACnB,IAAa,MACbH,EAAY,GACZnB,EAAa,SAAS,EAAE,eAAY,aAAaY,CAAK,CAAC,GACvDZ,EAAa,MAAM,GACnBC,EAAU,QAAQ,YAAY,GAC9BA,EAAU,MAAM;CAClB;CAEA,OADA,IAAaqB,GACNA;AACT"}