@minicode-dev/chat 0.1.12 → 0.1.14

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 (107) hide show
  1. package/CHANGELOG.md +112 -5
  2. package/README.md +401 -309
  3. package/assets/sounds/effects/avalanche.mp3 +0 -0
  4. package/assets/sounds/racing/car_boost.mp3 +0 -0
  5. package/assets/sounds/racing/car_boost.ogg +0 -0
  6. package/assets/sounds/racing/car_bump.mp3 +0 -0
  7. package/assets/sounds/racing/car_bump.ogg +0 -0
  8. package/assets/sounds/racing/car_drift_loop.mp3 +0 -0
  9. package/assets/sounds/racing/car_drift_loop.ogg +0 -0
  10. package/assets/sounds/racing/car_engine_loop.mp3 +0 -0
  11. package/assets/sounds/racing/car_engine_loop.ogg +0 -0
  12. package/assets/sounds/racing/car_engine_start.mp3 +0 -0
  13. package/assets/sounds/racing/car_engine_start.ogg +0 -0
  14. package/assets/sounds/racing/countdown_beep.mp3 +0 -0
  15. package/assets/sounds/racing/countdown_beep.ogg +0 -0
  16. package/assets/sounds/racing/countdown_go.mp3 +0 -0
  17. package/assets/sounds/racing/countdown_go.ogg +0 -0
  18. package/assets/sounds/racing/final_lap.mp3 +0 -0
  19. package/assets/sounds/racing/final_lap.ogg +0 -0
  20. package/assets/sounds/racing/finish_line.mp3 +0 -0
  21. package/assets/sounds/racing/finish_line.ogg +0 -0
  22. package/assets/sounds/racing/lap_complete.mp3 +0 -0
  23. package/assets/sounds/racing/lap_complete.ogg +0 -0
  24. package/assets/sounds/racing/music_circuit.mp3 +0 -0
  25. package/assets/sounds/racing/music_circuit.ogg +0 -0
  26. package/assets/sounds/racing/music_final_lap.mp3 +0 -0
  27. package/assets/sounds/racing/music_final_lap.ogg +0 -0
  28. package/assets/sounds/racing/race_defeat.mp3 +0 -0
  29. package/assets/sounds/racing/race_defeat.ogg +0 -0
  30. package/assets/sounds/racing/race_victory.mp3 +0 -0
  31. package/assets/sounds/racing/race_victory.ogg +0 -0
  32. package/assets/sounds/tanks/barrel_explode.mp3 +0 -0
  33. package/assets/sounds/tanks/barrel_explode.ogg +0 -0
  34. package/assets/sounds/tanks/crate_break.mp3 +0 -0
  35. package/assets/sounds/tanks/crate_break.ogg +0 -0
  36. package/assets/sounds/tanks/health_pickup.mp3 +0 -0
  37. package/assets/sounds/tanks/health_pickup.ogg +0 -0
  38. package/assets/sounds/tanks/last_life.mp3 +0 -0
  39. package/assets/sounds/tanks/last_life.ogg +0 -0
  40. package/assets/sounds/tanks/life_lost.mp3 +0 -0
  41. package/assets/sounds/tanks/life_lost.ogg +0 -0
  42. package/assets/sounds/tanks/respawn.mp3 +0 -0
  43. package/assets/sounds/tanks/respawn.ogg +0 -0
  44. package/assets/sounds/tanks/shield_end.mp3 +0 -0
  45. package/assets/sounds/tanks/shield_end.ogg +0 -0
  46. package/assets/sounds/tanks/shield_loop.mp3 +0 -0
  47. package/assets/sounds/tanks/shield_loop.ogg +0 -0
  48. package/assets/sounds/tanks/wall_hit.mp3 +0 -0
  49. package/assets/sounds/tanks/wall_hit.ogg +0 -0
  50. package/dist/chunks/{BattleshipGameCard-iRk8d4vR.js → BattleshipGameCard-B-B4r7lK.js} +146 -144
  51. package/dist/chunks/{BattleshipGameCard-iRk8d4vR.js.map → BattleshipGameCard-B-B4r7lK.js.map} +1 -1
  52. package/dist/chunks/{ChatContext-DjIEifGY.js → ChatContext-DQtKsR6Z.js} +2 -2
  53. package/dist/chunks/{ChatContext-DjIEifGY.js.map → ChatContext-DQtKsR6Z.js.map} +1 -1
  54. package/dist/chunks/RacingLiveGameCard-c1G7uoDI.js +1654 -0
  55. package/dist/chunks/RacingLiveGameCard-c1G7uoDI.js.map +1 -0
  56. package/dist/chunks/{TanksGameCard-B47ZQcSr.js → TanksGameCard-CvTaF7tu.js} +164 -162
  57. package/dist/chunks/{TanksGameCard-B47ZQcSr.js.map → TanksGameCard-CvTaF7tu.js.map} +1 -1
  58. package/dist/chunks/TanksLiveGameCard-COr9Oay5.js +1011 -0
  59. package/dist/chunks/TanksLiveGameCard-COr9Oay5.js.map +1 -0
  60. package/dist/chunks/avalancheScene3d-Z5QyZIid.js +1299 -0
  61. package/dist/chunks/avalancheScene3d-Z5QyZIid.js.map +1 -0
  62. package/dist/chunks/connect4Sounds-DmD-E9kX.js +235 -0
  63. package/dist/chunks/connect4Sounds-DmD-E9kX.js.map +1 -0
  64. package/dist/chunks/connectionStore-DwgIUl30.js +13 -0
  65. package/dist/chunks/connectionStore-DwgIUl30.js.map +1 -0
  66. package/dist/chunks/createLiveScene-CWBunxPi.js +367 -0
  67. package/dist/chunks/createLiveScene-CWBunxPi.js.map +1 -0
  68. package/dist/chunks/createRaceScene-C7Rw89kj.js +305 -0
  69. package/dist/chunks/createRaceScene-C7Rw89kj.js.map +1 -0
  70. package/dist/chunks/{createTankScene-CV7Brxmt.js → createTankScene-yhX6la27.js} +150 -147
  71. package/dist/chunks/{createTankScene-CV7Brxmt.js.map → createTankScene-yhX6la27.js.map} +1 -1
  72. package/dist/chunks/decor-CyyCnmct.js +418 -0
  73. package/dist/chunks/decor-CyyCnmct.js.map +1 -0
  74. package/dist/chunks/fx-1ZwzmPWj.js +458 -0
  75. package/dist/chunks/fx-1ZwzmPWj.js.map +1 -0
  76. package/dist/chunks/fx-UZwhOmdl.js +584 -0
  77. package/dist/chunks/fx-UZwhOmdl.js.map +1 -0
  78. package/dist/chunks/gameStatus-BChd29yI.js +62 -0
  79. package/dist/chunks/gameStatus-BChd29yI.js.map +1 -0
  80. package/dist/chunks/{connect4Sounds-Cv0q31VW.js → games-BvYTWgaR.js} +814 -951
  81. package/dist/chunks/games-BvYTWgaR.js.map +1 -0
  82. package/dist/chunks/rolldown-runtime-CGnMp29N.js +10 -0
  83. package/dist/chunks/tank-DjEQ4FXF.js +553 -0
  84. package/dist/chunks/tank-DjEQ4FXF.js.map +1 -0
  85. package/dist/chunks/tanks-live-BcKmrhnV.js +261 -0
  86. package/dist/chunks/tanks-live-BcKmrhnV.js.map +1 -0
  87. package/dist/chunks/{tanksSounds-CUNDIOal.js → tanksSounds-DwjfSp3W.js} +43 -11
  88. package/dist/chunks/tanksSounds-DwjfSp3W.js.map +1 -0
  89. package/dist/chunks/track-BcnnpnAm.js +259 -0
  90. package/dist/chunks/track-BcnnpnAm.js.map +1 -0
  91. package/dist/chunks/util-DwI9bfS7.js +57 -0
  92. package/dist/chunks/util-DwI9bfS7.js.map +1 -0
  93. package/dist/index.js +2176 -1592
  94. package/dist/index.js.map +1 -1
  95. package/dist/styles.css +1 -1
  96. package/index.d.ts +4 -2
  97. package/package.json +1 -1
  98. package/dist/chunks/TanksLiveGameCard-DGsnMD3j.js +0 -1225
  99. package/dist/chunks/TanksLiveGameCard-DGsnMD3j.js.map +0 -1
  100. package/dist/chunks/connect4Sounds-Cv0q31VW.js.map +0 -1
  101. package/dist/chunks/createLiveScene-BqI7qV4n.js +0 -364
  102. package/dist/chunks/createLiveScene-BqI7qV4n.js.map +0 -1
  103. package/dist/chunks/fx-BHsl4S_J.js +0 -1470
  104. package/dist/chunks/fx-BHsl4S_J.js.map +0 -1
  105. package/dist/chunks/gameStatus-Cu1Z8un9.js +0 -69
  106. package/dist/chunks/gameStatus-Cu1Z8un9.js.map +0 -1
  107. package/dist/chunks/tanksSounds-CUNDIOal.js.map +0 -1
@@ -1,1225 +0,0 @@
1
- import { d as e, o as t } from "./connect4Sounds-Cv0q31VW.js";
2
- import { n, o as r, r as i, s as a } from "./createPeerLink-DBVs_yvQ.js";
3
- import { t as o } from "./ChatContext-DjIEifGY.js";
4
- import { a as s, i as c, n as l, r as u, t as d } from "./tanksSounds-CUNDIOal.js";
5
- import { memo as f, useContext as p, useEffect as m, useId as h, useRef as g, useState as _ } from "react";
6
- import { AlertTriangle as v, CheckCircle2 as y, Link2 as b, Loader2 as x, Settings2 as S, ShieldCheck as C, X as w } from "lucide-react";
7
- import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
8
- // \0rolldown/runtime.js
9
- var O = Object.defineProperty, k = (e, t) => {
10
- let n = {};
11
- for (var r in e) O(n, r, {
12
- get: e[r],
13
- enumerable: !0
14
- });
15
- return t || O(n, Symbol.toStringTag, { value: "Module" }), n;
16
- }, A = "{\n \"iceServers\": [{\n \"urls\": [\"turn:turn.cloudflare.com:3478?transport=udp\"],\n \"username\": \"usuario-temporal\",\n \"credential\": \"credencial-temporal\"\n }]\n}";
17
- function j({ open: e, onClose: t, onApply: n, manualTurn: r }) {
18
- let a = g(null), o = g(0), s = h(), c = h(), l = h(), [u, d] = _(""), [f, p] = _(""), [v, S] = _(!1), [T, O] = _(""), [k, j] = _(!1), [M, N] = _(!1);
19
- m(() => {
20
- let t = a.current;
21
- if (!e) {
22
- o.current += 1, d(""), p(""), S(!1), O(""), j(!1), N(!1), t?.open && t.close();
23
- return;
24
- }
25
- let n = document.activeElement;
26
- return t.showModal(), () => {
27
- o.current += 1, t.open && t.close(), n instanceof HTMLElement && n.isConnected && n.focus({ preventScroll: !0 });
28
- };
29
- }, [e]);
30
- let P = async (e) => {
31
- if (e.preventDefault(), k || !u.trim()) return;
32
- O("");
33
- let t = {
34
- iceTransportPolicy: v ? "relay" : "all",
35
- ...f.trim() ? { ttlSeconds: Number(f) } : {}
36
- };
37
- try {
38
- i(u, t);
39
- } catch (e) {
40
- O(e.message);
41
- return;
42
- }
43
- let r = ++o.current;
44
- j(!0), N(!1);
45
- try {
46
- if (await n(u, t), r !== o.current) return;
47
- d(""), p(""), N(!0);
48
- } catch (e) {
49
- r === o.current && O(e.message || "No se pudo compartir la configuración. Inténtalo de nuevo.");
50
- } finally {
51
- r === o.current && j(!1);
52
- }
53
- }, F = r?.status === "applying" ? "Aplicando configuración en ambos jugadores…" : r?.status === "waiting_peer" ? "Esperando al otro jugador para compartir la configuración…" : "Sincronizando con el otro jugador…";
54
- return /* @__PURE__ */ D("dialog", {
55
- ref: a,
56
- className: "tl-turn-dialog",
57
- "aria-labelledby": s,
58
- "aria-describedby": c,
59
- onCancel: (e) => {
60
- e.preventDefault(), t();
61
- },
62
- onClick: (e) => {
63
- if (e.target === e.currentTarget) {
64
- let n = e.currentTarget.getBoundingClientRect();
65
- (e.clientX < n.left || e.clientX > n.right || e.clientY < n.top || e.clientY > n.bottom) && t();
66
- }
67
- },
68
- children: [/* @__PURE__ */ D("header", {
69
- className: "tl-turn-dialog__header",
70
- children: [
71
- /* @__PURE__ */ E("span", {
72
- className: "tl-turn-dialog__icon",
73
- "aria-hidden": "true",
74
- children: /* @__PURE__ */ E(b, { size: 22 })
75
- }),
76
- /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("h2", {
77
- id: s,
78
- children: "Configurar conexión alternativa"
79
- }), /* @__PURE__ */ E("p", { children: "Se comparte con el otro jugador." })] }),
80
- /* @__PURE__ */ E("button", {
81
- type: "button",
82
- className: "tl-turn-dialog__close",
83
- "aria-label": "Cerrar configuración de conexión",
84
- onClick: t,
85
- children: /* @__PURE__ */ E(w, { size: 18 })
86
- })
87
- ]
88
- }), /* @__PURE__ */ D("form", {
89
- onSubmit: P,
90
- children: [/* @__PURE__ */ D("div", {
91
- className: "tl-turn-dialog__body",
92
- children: [
93
- /* @__PURE__ */ E("p", {
94
- id: c,
95
- children: "Pega el JSON de credenciales temporales de Cloudflare TURN. Se compartirá con el otro jugador para buscar una conexión alternativa."
96
- }),
97
- /* @__PURE__ */ D("label", {
98
- className: "tl-turn-dialog__field",
99
- children: ["Configuración JSON", /* @__PURE__ */ E("textarea", {
100
- autoFocus: !0,
101
- value: u,
102
- onChange: (e) => {
103
- d(e.target.value), O(""), N(!1);
104
- },
105
- placeholder: A,
106
- maxLength: 6e3,
107
- rows: 7,
108
- required: !M,
109
- readOnly: k,
110
- autoComplete: "off",
111
- autoCorrect: "off",
112
- autoCapitalize: "off",
113
- spellCheck: !1,
114
- "aria-invalid": !!T,
115
- "aria-describedby": T ? l : void 0
116
- })]
117
- }),
118
- /* @__PURE__ */ D("div", {
119
- className: "tl-turn-dialog__privacy",
120
- children: [/* @__PURE__ */ E(C, {
121
- size: 16,
122
- "aria-hidden": "true"
123
- }), /* @__PURE__ */ E("span", { children: "Solo credenciales temporales. No pegues tu API Token ni secretos permanentes." })]
124
- }),
125
- /* @__PURE__ */ D("label", {
126
- className: "tl-turn-dialog__field",
127
- children: [
128
- "Validez restante en segundos ",
129
- /* @__PURE__ */ E("span", {
130
- className: "tl-turn-dialog__optional",
131
- children: "Opcional"
132
- }),
133
- /* @__PURE__ */ E("input", {
134
- type: "number",
135
- min: "1",
136
- max: "172800",
137
- step: "1",
138
- inputMode: "numeric",
139
- value: f,
140
- onChange: (e) => {
141
- p(e.target.value), O("");
142
- },
143
- disabled: k,
144
- placeholder: "Ej. 3600"
145
- }),
146
- /* @__PURE__ */ E("small", { children: "Déjalo vacío si no conoces la caducidad. Si dejan de funcionar, pega credenciales nuevas." })
147
- ]
148
- }),
149
- /* @__PURE__ */ D("label", {
150
- className: "tl-turn-dialog__debug",
151
- children: [/* @__PURE__ */ E("input", {
152
- type: "checkbox",
153
- checked: v,
154
- onChange: (e) => S(e.target.checked),
155
- disabled: k
156
- }), /* @__PURE__ */ D("span", { children: [/* @__PURE__ */ E("strong", { children: "Forzar relay para diagnóstico" }), /* @__PURE__ */ E("small", { children: "Normalmente se permite P2P. Actívalo solo para comprobar TURN." })] })]
157
- }),
158
- k && /* @__PURE__ */ D("p", {
159
- className: "tl-turn-dialog__feedback",
160
- role: "status",
161
- children: [/* @__PURE__ */ E(x, {
162
- size: 16,
163
- className: "tl-connection-spinner",
164
- "aria-hidden": "true"
165
- }), F]
166
- }),
167
- M && /* @__PURE__ */ D("p", {
168
- className: "tl-turn-dialog__feedback tl-turn-dialog__feedback--success",
169
- role: "status",
170
- children: [/* @__PURE__ */ E(y, {
171
- size: 17,
172
- "aria-hidden": "true"
173
- }), "Configuración compartida y aplicada en ambos jugadores. La ruta utilizada se mostrará al conectar."]
174
- }),
175
- T && /* @__PURE__ */ E("p", {
176
- id: l,
177
- className: "tl-turn-dialog__error",
178
- role: "alert",
179
- children: T
180
- })
181
- ]
182
- }), /* @__PURE__ */ D("footer", {
183
- className: "tl-turn-dialog__footer",
184
- children: [/* @__PURE__ */ E("button", {
185
- type: "button",
186
- onClick: t,
187
- children: M ? "Listo" : "Cancelar"
188
- }), /* @__PURE__ */ D("button", {
189
- type: "submit",
190
- className: "tl-turn-dialog__apply",
191
- disabled: k || !u.trim(),
192
- children: [k ? /* @__PURE__ */ E(x, {
193
- size: 16,
194
- className: "tl-connection-spinner",
195
- "aria-hidden": "true"
196
- }) : /* @__PURE__ */ E(b, {
197
- size: 16,
198
- "aria-hidden": "true"
199
- }), "Aplicar y reconectar"]
200
- })]
201
- })]
202
- })]
203
- });
204
- }
205
- // src/games/tanks-live/net/connectionDiagnostics.js
206
- var M = (e) => typeof e == "string" && /^[a-z_]+$/.test(e) ? e : e ? "connection_error" : null;
207
- function N(e = {}) {
208
- let t = e.route, n = e.candidates ?? {}, r = e.manualTurn ?? {};
209
- return {
210
- phase: e.phase ?? "idle",
211
- ice: e.ice ?? "new",
212
- connection: e.connection ?? "new",
213
- signalingState: e.signalingState ?? "stable",
214
- gathering: e.gathering ?? "new",
215
- signaling: {
216
- sent: e.signals?.sent ?? 0,
217
- received: e.signals?.received ?? 0,
218
- sendFailures: e.signals?.sendFailures ?? 0,
219
- ignored: e.signals?.ignored ?? 0,
220
- invalid: e.signals?.invalid ?? 0,
221
- retransmits: e.signals?.retransmits ?? 0,
222
- duplicates: e.signals?.duplicates ?? 0,
223
- tooLarge: e.signals?.tooLarge ?? 0
224
- },
225
- turnConfigured: !!e.turnConfigured,
226
- configuration: {
227
- status: r.status ?? "idle",
228
- shared: !!r.shared,
229
- received: !!r.received,
230
- expiresAt: r.expiresAt ?? null,
231
- expired: r.expiresAt != null && r.expiresAt <= Date.now()
232
- },
233
- route: t?.label ?? null,
234
- selectedPair: t ? {
235
- relay: !!t.relay,
236
- localType: t.local ?? null,
237
- remoteType: t.remote ?? null,
238
- protocol: t.protocol ?? null,
239
- relayProtocol: t.relayProtocol ?? null
240
- } : null,
241
- candidates: {
242
- local: n.local ?? 0,
243
- remote: n.remote ?? 0,
244
- accepted: n.accepted ?? 0,
245
- rejected: n.rejected ?? 0,
246
- localRelay: n.localRelay ?? 0,
247
- remoteRelay: n.remoteRelay ?? 0
248
- },
249
- iceErrors: (e.iceErrors ?? []).slice(-10).map((e) => ({
250
- code: Number(e.code) || 0,
251
- stage: e.stage === "gathering" ? "gathering" : "remote_candidate"
252
- })),
253
- reconnects: e.reconnects ?? 0,
254
- restarts: e.restarts ?? 0,
255
- rttMs: e.rtt ?? e.statsRtt ?? null,
256
- connectionTimeMs: e.elapsedMs ?? 0,
257
- channels: {
258
- move: e.channels?.move ?? "closed",
259
- events: e.channels?.events ?? "closed"
260
- },
261
- movesSent: e.move?.sent ?? 0,
262
- movesReceived: e.move?.received ?? 0,
263
- error: M(e.error)
264
- };
265
- }
266
- function P(e = {}) {
267
- return e.phase === "connected" ? "" : e.manualTurn?.status === "applying" ? "Aplicando la configuración de conexión alternativa." : e.manualTurn?.status === "sharing" ? "Sincronizando la configuración con el otro jugador." : e.manualTurn?.status === "waiting_peer" ? "Esperando al otro jugador para compartir la configuración." : e.error === "turn_credentials_expired" || e.manualTurn?.expiresAt != null && e.manualTurn.expiresAt <= Date.now() ? "La validez indicada de TURN terminó. Pega credenciales temporales nuevas para reconectar." : e.signals?.sendFailures ? "No se pudieron enviar señales por el chat. Revisa la conexión WebSocket y reintenta." : e.ice === "checking" || e.ice === "failed" ? e.turnConfigured ? "WebRTC aún no encuentra una ruta entre los jugadores. Puedes revisar o renovar la configuración TURN." : "WebRTC aún no encuentra una ruta entre los jugadores. Algunas redes necesitan un servidor TURN; puedes configurar una conexión alternativa." : e.phase === "listening" ? "Esperando la conexión del rival. Ambos deben mantener abierta la partida y usar la misma versión del chat." : e.phase === "failed" ? "No se pudo conectar. Revisa el diagnóstico de ambos jugadores y reintenta." : "Intercambiando señales de conexión por el chat. El combate empieza cuando conecta WebRTC.";
268
- }
269
- // src/games/tanks-live/logic/simulation.js
270
- var F = Object.freeze({
271
- speed: 16,
272
- reverse: 9,
273
- turn: 1.8,
274
- acceleration: 28,
275
- radius: 2,
276
- reloadMs: 1500,
277
- shellSpeed: 60,
278
- damage: 25
279
- }), I = (e, t, n) => Math.max(t, Math.min(n, e)), L = (e, t) => Math.atan2(Math.sin(t - e), Math.cos(t - e));
280
- function ee() {
281
- return {
282
- size: 120,
283
- bounds: {
284
- minX: 0,
285
- maxX: 120,
286
- minZ: 0,
287
- maxZ: 120
288
- },
289
- obstacles: [
290
- [
291
- 42,
292
- 35,
293
- 4
294
- ],
295
- [
296
- 78,
297
- 85,
298
- 4
299
- ],
300
- [
301
- 60,
302
- 30,
303
- 3
304
- ],
305
- [
306
- 60,
307
- 90,
308
- 3
309
- ],
310
- [
311
- 35,
312
- 65,
313
- 3
314
- ],
315
- [
316
- 85,
317
- 55,
318
- 3
319
- ],
320
- [
321
- 60,
322
- 60,
323
- 5
324
- ]
325
- ].map(([e, t, n], r) => ({
326
- x: e,
327
- z: t,
328
- r: n,
329
- kind: r % 2 ? "tree" : "rock"
330
- }))
331
- };
332
- }
333
- function te(e, t) {
334
- return {
335
- id: String(e),
336
- x: t ? 102 : 18,
337
- z: 60,
338
- yaw: t ? Math.PI : 0,
339
- turretYaw: t ? Math.PI : 0,
340
- speed: 0,
341
- hp: 100,
342
- alive: !0,
343
- color: t ? "#d7463f" : "#54a34a"
344
- };
345
- }
346
- function R(e, t, n, r = F.radius) {
347
- let i = n.bounds;
348
- return e >= i.minX + r && e <= i.maxX - r && t >= i.minZ + r && t <= i.maxZ - r && !n.obstacles.some((n) => Math.hypot(e - n.x, t - n.z) < n.r + r);
349
- }
350
- function ne(e, t, n, r) {
351
- if (!e.alive) return;
352
- let i = I(Number(t.throttle) || 0, -1, 1), a = I(Number(t.steer) || 0, -1, 1), o = i * (i < 0 ? F.reverse : F.speed);
353
- e.speed += I(o - e.speed, -F.acceleration * n, F.acceleration * n), e.yaw += a * F.turn * n, Number.isFinite(t.turretYaw) && (e.turretYaw = t.turretYaw);
354
- let s = e.x + Math.cos(e.yaw) * e.speed * n, c = e.z + Math.sin(e.yaw) * e.speed * n;
355
- R(s, e.z, r) ? e.x = s : e.speed = 0, R(e.x, c, r) ? e.z = c : e.speed = 0;
356
- }
357
- function z(e, t, n, r, i, a, o) {
358
- let s = n - e, c = r - t, l = s * s + c * c, u = l ? I(((i - e) * s + (a - t) * c) / l, 0, 1) : 0;
359
- return Math.hypot(e + s * u - i, t + c * u - a) <= o ? u : null;
360
- }
361
- function re(e, t, n, r) {
362
- let i = e.x + Math.cos(e.yaw) * F.shellSpeed * t, a = e.z + Math.sin(e.yaw) * F.shellSpeed * t, o = null;
363
- for (let t of n.obstacles) {
364
- let n = z(e.x, e.z, i, a, t.x, t.z, t.r);
365
- n !== null && (!o || n < o.t) && (o = {
366
- t: n,
367
- kind: "obstacle"
368
- });
369
- }
370
- for (let t of r) {
371
- if (!t.alive || t.id === e.owner) continue;
372
- let n = z(e.x, e.z, i, a, t.x, t.z, F.radius);
373
- n !== null && (!o || n < o.t) && (o = {
374
- t: n,
375
- kind: "tank",
376
- target: t.id
377
- });
378
- }
379
- return o ? (e.x += (i - e.x) * o.t, e.z += (a - e.z) * o.t, o) : (e.x = i, e.z = a, e.age += t, !R(i, a, {
380
- ...n,
381
- obstacles: []
382
- }, 0) || e.age > 3 ? { kind: "terrain" } : null);
383
- }
384
- function ie(e, t, n) {
385
- let r = I(n, 0, 1);
386
- return {
387
- ...t,
388
- x: e.x + (t.x - e.x) * r,
389
- z: e.z + (t.z - e.z) * r,
390
- yaw: e.yaw + L(e.yaw, t.yaw) * r,
391
- turretYaw: e.turretYaw + L(e.turretYaw, t.turretYaw) * r
392
- };
393
- }
394
- // src/games/tanks-live/net/createLiveMatch.js
395
- var B = (e) => typeof e == "number" && Number.isFinite(e), ae = (e) => typeof e == "string" && e.length > 0 && e.length < 160;
396
- function oe({ game: e, selfId: t, isHost: i = String(t) === String(e.hostId), signaling: o, onChange: s = () => {}, peerFactory: c = n, iceServers: l, iceTransportPolicy: u, RTCPeerConnectionImpl: d } = {}) {
397
- t = String(t);
398
- let f = [String(e.hostId), String(e.guestId)];
399
- if (!f.includes(t) || f[0] === f[1]) throw Error("Invalid live match players");
400
- let p = (l || []).some((e) => [e.urls].flat().some((e) => /^turns?:/i.test(String(e)))), m = f.find((e) => e !== t), h = ee(), g = f.map(te), _ = [], v = [], y = {}, b = 0, x = 0, S = 0, C = 0, w = "connecting", T = null, E = !1, D = {
401
- phase: "idle",
402
- rtt: null,
403
- route: null
404
- }, O = !1, k = !1, A = null, j = [], M = null, N = null, P = /* @__PURE__ */ new Map(), I = /* @__PURE__ */ new Set(), L = /* @__PURE__ */ new Set(), z = () => g.find((e) => e.id === t), oe = () => g.find((e) => e.id === m), V = () => {
405
- E || s({
406
- status: w,
407
- winner: T,
408
- net: D
409
- });
410
- }, H = (e) => {
411
- v.push(e), v.length > 200 && v.shift();
412
- }, U = (t) => X.sendEvent({
413
- ...t,
414
- g: e.gameId,
415
- r: S
416
- }), W = (e) => {
417
- I.add(e), I.size > 2048 && I.delete(I.values().next().value);
418
- }, G = () => ({
419
- k: "snapshot",
420
- hp: g.map((e) => e.hp),
421
- status: w,
422
- winner: T,
423
- reason: A
424
- });
425
- function K() {
426
- g = f.map(te), _ = [], j = [], M = null, N = null, P.clear(), I.clear(), L.clear(), k = !1, A = null, T = null, w = D.phase === "connected" ? "playing" : "connecting", H({ k: "rematch" }), V();
427
- }
428
- function q(e, t = !1) {
429
- (w !== "over" || t && T !== e) && (w = "over", T = e, _ = [], X.clearManualTurn?.(), H({
430
- k: "over",
431
- winner: T
432
- }), V());
433
- }
434
- function J(e, t, n) {
435
- if (w !== "playing" || !ae(t.id) || I.has(t.id)) return !1;
436
- let r = g.find((t) => t.id === e);
437
- if (!r?.alive || b - (P.get(e) ?? -Infinity) < F.reloadMs - 20 || ![
438
- t.x,
439
- t.z,
440
- t.yaw
441
- ].every(B) || Math.hypot(t.x - (e === m && N ? N.x : r.x), t.z - (e === m && N ? N.z : r.z)) > 6) return !1;
442
- W(t.id), P.set(e, b);
443
- let i = {
444
- ...t,
445
- k: "fire",
446
- owner: e,
447
- x: t.x + Math.cos(t.yaw) * 2.8,
448
- z: t.z + Math.sin(t.yaw) * 2.8,
449
- y: 1.4,
450
- age: 0
451
- };
452
- return _.push(i), H(i), n && U({
453
- ...t,
454
- k: "fire"
455
- }), !0;
456
- }
457
- function Y(t) {
458
- if (E || w !== "playing" || t?.g !== e.gameId || t.r !== S || !Array.isArray(t.p) || t.p.length !== 5 || !t.p.every(B)) return;
459
- let [n, r, i, a, o] = t.p;
460
- if (!R(n, r, h) || Math.abs(o) > F.speed + .1 || Math.abs(i) > 1e6 || Math.abs(a) > 1e6) return;
461
- let s = M === null ? .1 : Math.max(0, (b - M) / 1e3), c = N || oe();
462
- if (Math.hypot(n - c.x, r - c.z) > F.speed * s + 3) return;
463
- let l = {
464
- ...oe(),
465
- x: n,
466
- z: r,
467
- yaw: i,
468
- turretYaw: a,
469
- speed: o
470
- };
471
- N = l, M = b, j.push({
472
- time: b,
473
- pose: l
474
- }), j.length > 30 && j.shift();
475
- }
476
- function se(n) {
477
- if (!E && n && n.g === e.gameId && Number.isInteger(n.r)) {
478
- if (n.k === "round" && !i && w === "over" && L.has(t) && n.r === S + 1) S = n.r, K();
479
- else if (n.r === S) {
480
- if (n.k === "sync" && i) U(G());
481
- else if (n.k === "snapshot" && !i && Array.isArray(n.hp) && n.hp.length === 2 && n.hp.every((e) => Number.isInteger(e) && e >= 0 && e <= 100)) {
482
- let e = n.status === "playing" && n.winner === null && n.hp.every((e) => e > 0), t = n.status === "over" && f.includes(n.winner) && (n.hp[1 - f.indexOf(n.winner)] === 0 || n.reason === "resigned");
483
- if (!e && !t) return;
484
- g.forEach((e, t) => {
485
- e.hp = n.hp[t], e.alive = e.hp > 0;
486
- }), t && (k = !1, A = n.reason, q(n.winner, !0));
487
- } else n.k === "resign" ? (A = "resigned", q(t, i), i && U(G())) : n.k === "rematch" && w === "over" ? (L.add(m), ce()) : w === "playing" && (n.k === "fire" && J(m, n, !1), !i && n.k === "hit" && ae(n.id) && !I.has(`hit:${n.id}`) && f.includes(n.target) && Array.isArray(n.hp) && n.hp.length === 2 && n.hp.every((e, t) => Number.isInteger(e) && e >= 0 && e <= g[t].hp) && [n.x, n.z].every(B) && (W(`hit:${n.id}`), g.forEach((e, t) => {
488
- e.hp = n.hp[t], e.alive = e.hp > 0;
489
- }), _ = _.filter((e) => e.id !== n.id), H(n)), !i && n.k === "destroyed" && f.includes(n.target) && !I.has(`destroyed:${n.target}`) && g.find((e) => e.id === n.target).hp === 0 && (W(`destroyed:${n.target}`), H(n)), !i && n.k === "over" && f.includes(n.winner) && g.find((e) => e.id !== n.winner).hp === 0 && q(n.winner));
490
- }
491
- }
492
- }
493
- let X = c({
494
- configurationAuthority: i,
495
- signaling: o && {
496
- send(t) {
497
- let n = r(t);
498
- if (!n) return Promise.reject(/* @__PURE__ */ Error("Invalid live signal"));
499
- let i = a({
500
- ...n,
501
- liveGame: e.gameId
502
- });
503
- return i ? o.send(i) : Promise.reject(/* @__PURE__ */ Error("Live signal too large"));
504
- },
505
- subscribe(t) {
506
- return o.subscribe((n) => {
507
- r(n?.text)?.liveGame === e.gameId && t(n);
508
- });
509
- }
510
- },
511
- selfId: t,
512
- peerId: m,
513
- roomId: e.roomId,
514
- iceServers: l,
515
- iceTransportPolicy: u,
516
- RTCPeerConnectionImpl: d,
517
- onMove: Y,
518
- onEvent: se,
519
- onChange(e) {
520
- let t = D.phase;
521
- if (D = e, e.phase === "connected") {
522
- let e = O;
523
- O = !0, w !== "over" && (w = "playing"), t !== "connected" && queueMicrotask(() => {
524
- E || (k && U({ k: "resign" }), i && e ? U(G()) : i || U({ k: "sync" }));
525
- });
526
- } else w !== "over" && (w = O ? "disconnected" : "connecting");
527
- V();
528
- }
529
- });
530
- function ce() {
531
- i && L.size === 2 && (S++, U({ k: "round" }), K());
532
- }
533
- return i ? X.connect() : X.setListening(!0), {
534
- setInput(e) {
535
- y = { ...e };
536
- },
537
- step(n) {
538
- if (E) return;
539
- let r = Math.max(0, Math.min(Number(n) || 0, 100));
540
- if (b += r, w !== "playing") return;
541
- let a = r / 1e3, o = b - 100;
542
- for (; j.length > 1 && j[1].time <= o;) j.shift();
543
- if (j.length) {
544
- let e = j[0], t = j[1] || e, n = ie(e.pose, t.pose, t.time === e.time ? 1 : (o - e.time) / (t.time - e.time));
545
- for (let e of [
546
- "x",
547
- "z",
548
- "yaw",
549
- "turretYaw",
550
- "speed"
551
- ]) oe()[e] = n[e];
552
- }
553
- for (let e = a; e > 0; e -= .016) ne(z(), y, Math.min(e, .016), h);
554
- y.fire && J(t, {
555
- id: `${t}:${S}:${++C}`,
556
- x: z().x,
557
- z: z().z,
558
- yaw: z().turretYaw,
559
- t: b
560
- }, !0);
561
- let s = D.rtt > 250 || D.move?.lossPct > 15 ? 10 : D.rtt > 100 ? 15 : 20;
562
- if (b >= x) {
563
- x = b + 1e3 / s;
564
- let t = z();
565
- X.sendMove({
566
- g: e.gameId,
567
- r: S,
568
- p: [
569
- t.x,
570
- t.z,
571
- t.yaw,
572
- t.turretYaw,
573
- t.speed
574
- ],
575
- t: b
576
- });
577
- }
578
- _ = _.filter((e) => {
579
- let t = re(e, a, h, g);
580
- if (!t) return !0;
581
- if (i && t.target && w === "playing") {
582
- let n = g.find((e) => e.id === t.target);
583
- n.hp = Math.max(0, n.hp - F.damage), n.alive = n.hp > 0;
584
- let r = {
585
- k: "hit",
586
- id: e.id,
587
- target: n.id,
588
- dmg: F.damage,
589
- hp: g.map((e) => e.hp),
590
- x: e.x,
591
- z: e.z
592
- };
593
- if (U(r), H(r), !n.alive) {
594
- let t = {
595
- k: "destroyed",
596
- target: n.id
597
- };
598
- U(t), H(t), U({
599
- k: "over",
600
- winner: e.owner
601
- }), q(e.owner);
602
- }
603
- } else H({
604
- k: "impact",
605
- kind: t.kind,
606
- x: e.x,
607
- z: e.z
608
- });
609
- return !1;
610
- });
611
- },
612
- getRender() {
613
- let e = {
614
- arena: h,
615
- tanks: g.map((e) => ({
616
- ...e,
617
- self: e.id === t,
618
- reload: Math.min(1, (b - (P.get(e.id) ?? -Infinity)) / F.reloadMs)
619
- })),
620
- shells: _.map((e) => ({ ...e })),
621
- events: v,
622
- status: w,
623
- winner: T,
624
- net: {
625
- ...D,
626
- turnConfigured: D.turnConfigured ?? p
627
- }
628
- };
629
- return v = [], e;
630
- },
631
- configureTurn(e, t) {
632
- return E || w === "over" ? Promise.reject(/* @__PURE__ */ Error("La partida ya terminó.")) : X.configureTurn(e, t);
633
- },
634
- reconnect() {
635
- !E && w !== "over" && (X.requestReconnect ? X.requestReconnect() : X.connect());
636
- },
637
- rematch() {
638
- !E && w === "over" && (L.add(t), U({ k: "rematch" }), ce());
639
- },
640
- resign() {
641
- !E && w !== "over" && (k = !0, A = "resigned", U({ k: "resign" }), q(m));
642
- },
643
- destroy() {
644
- E || (E = !0, X.destroy(), _ = [], j = [], v = []);
645
- }
646
- };
647
- }
648
- var V = 180 / Math.PI, H = (e, t) => Number.isFinite(e) ? e : t;
649
- function U(e) {
650
- let t = e || {}, n = t.bounds || t;
651
- if (Number.isFinite(n.minX) && Number.isFinite(n.maxX) && Number.isFinite(n.minZ) && Number.isFinite(n.maxZ)) return W({
652
- minX: n.minX,
653
- minZ: n.minZ,
654
- maxX: n.maxX,
655
- maxZ: n.maxZ
656
- });
657
- let r = H(t.width, H(t.size, 120)), i = H(t.depth, H(t.height, H(t.size, r)));
658
- return W(t.centered === !0 || t.origin === "center" ? {
659
- minX: -r / 2,
660
- minZ: -i / 2,
661
- maxX: r / 2,
662
- maxZ: i / 2
663
- } : {
664
- minX: 0,
665
- minZ: 0,
666
- maxX: r,
667
- maxZ: i
668
- });
669
- }
670
- function W(e) {
671
- return {
672
- ...e,
673
- width: e.maxX - e.minX,
674
- depth: e.maxZ - e.minZ,
675
- cx: (e.minX + e.maxX) / 2,
676
- cz: (e.minZ + e.maxZ) / 2
677
- };
678
- }
679
- function G(e) {
680
- return (e?.obstacles || []).filter((e) => Number.isFinite(e?.x) && Number.isFinite(e?.z)).map((e) => ({
681
- x: e.x,
682
- z: e.z,
683
- r: H(e.r, H(e.radius, 1.5)),
684
- kind: e.kind === "tree" ? "tree" : "rock"
685
- }));
686
- }
687
- var K = (e) => e ? e.k ?? e.type ?? null : null;
688
- function q(e, t) {
689
- let n = t || [], r = e?.tank ?? e?.by ?? e?.owner ?? e?.from;
690
- if (r != null) {
691
- let e = n.find((e) => String(e.id) === String(r));
692
- if (e) return e;
693
- }
694
- if (!Number.isFinite(e?.x) || !Number.isFinite(e?.z)) return null;
695
- let i = null, a = Infinity;
696
- for (let t of n) {
697
- let n = (t.x - e.x) ** 2 + (t.z - e.z) ** 2;
698
- n < a && (a = n, i = t);
699
- }
700
- return i;
701
- }
702
- var J = (e, t) => t == null ? null : (e || []).find((e) => String(e.id) === String(t)) || null, Y = (e) => (e || []).find((e) => e.self) || null, se = (e, t = !0) => !t || e <= 25 ? 2 : +(e <= 50), X = (e) => {
703
- let t = e % (Math.PI * 2);
704
- return t > Math.PI && (t -= Math.PI * 2), t < -Math.PI && (t += Math.PI * 2), t;
705
- };
706
- function ce({ moveSpeed: e = .6, turnSpeed: t = .35, holdMs: n = 160 } = {}) {
707
- let r = null, i = !1, a = !1, o = 0, s = 0;
708
- return {
709
- update(c, l) {
710
- let u = [];
711
- if (!c || c.alive === !1) return i && u.push({ type: "move_end" }), a && u.push({ type: "turret_end" }), i = a = !1, r = null, u;
712
- let d = Math.max(1, l) / 1e3;
713
- if (r) {
714
- let f = Math.hypot(c.x - r.x, c.z - r.z) / d, p = Math.abs(X((c.yaw ?? 0) - r.yaw)) / d, m = Math.abs(X((c.turretYaw ?? 0) - (c.yaw ?? 0) - r.rel)) / d, h = f > e || p > t;
715
- o = h ? 0 : o + l, h && !i ? (i = !0, u.push({ type: "move_start" })) : !h && i && o >= n && (i = !1, u.push({ type: "move_end" }));
716
- let g = m > t;
717
- s = g ? 0 : s + l, g && !a ? (a = !0, u.push({ type: "turret_start" })) : !g && a && s >= n && (a = !1, u.push({ type: "turret_end" }));
718
- }
719
- return r = {
720
- x: c.x,
721
- z: c.z,
722
- yaw: c.yaw ?? 0,
723
- rel: X((c.turretYaw ?? 0) - (c.yaw ?? 0))
724
- }, u;
725
- },
726
- reset() {
727
- r = null, i = a = !1, o = s = 0;
728
- },
729
- get moving() {
730
- return i;
731
- },
732
- get turning() {
733
- return a;
734
- }
735
- };
736
- }
737
- function le(e) {
738
- switch (K(e)) {
739
- case "fire": return [{ type: "fire" }, { type: "projectile_start" }];
740
- case "impact": return [{
741
- type: "impact",
742
- kind: e.kind === "obstacle" ? "obstacle" : "terrain"
743
- }];
744
- case "hit": return [{ type: "tank_hit" }];
745
- case "destroyed": return [{ type: "destroyed" }];
746
- default: return [];
747
- }
748
- }
749
- function ue(e) {
750
- if (!e || e.status !== "over") return null;
751
- let t = Y(e.tanks);
752
- return !t || e.winner == null ? null : String(e.winner) === String(t.id) ? "victory" : "defeat";
753
- }
754
- // src/games/tanks-live/scene/createLiveScene2d.js
755
- function de(e, { arena: t } = {}) {
756
- let n = document.createElement("canvas");
757
- n.style.cssText = "width:100%;height:100%;display:block", e.appendChild(n);
758
- let r = n.getContext("2d");
759
- if (!r) throw n.remove(), Error("Canvas 2D unavailable");
760
- let i = U(t), a = 1, o = 1, s = 1, c = 0, l = 0;
761
- function u() {
762
- a = Math.max(1, e.clientWidth), o = Math.max(1, e.clientHeight);
763
- let t = Math.min(window.devicePixelRatio || 1, 2);
764
- n.width = a * t, n.height = o * t, r.setTransform(t, 0, 0, t, 0, 0), s = Math.min(a / i.width, o / i.depth), c = (a - i.width * s) / 2, l = (o - i.depth * s) / 2;
765
- }
766
- u();
767
- let d = typeof ResizeObserver == "function" ? new ResizeObserver(u) : null;
768
- return d?.observe(e), {
769
- kind: "2d",
770
- resize: u,
771
- screenToGround(e, t) {
772
- return {
773
- x: ((e + 1) * a / 2 - c) / s + i.minX,
774
- z: ((1 - t) * o / 2 - l) / s + i.minZ
775
- };
776
- },
777
- render(e) {
778
- r.fillStyle = "#152519", r.fillRect(0, 0, a, o), r.save(), r.translate(c, l), r.scale(s, s), r.translate(-i.minX, -i.minZ), r.fillStyle = "#526348", r.fillRect(i.minX, i.minZ, i.width, i.depth);
779
- for (let e of G(t)) r.fillStyle = e.kind === "tree" ? "#24472d" : "#929285", r.beginPath(), r.arc(e.x, e.z, e.r, 0, Math.PI * 2), r.fill();
780
- for (let t of e.tanks || []) r.save(), r.translate(t.x, t.z), r.rotate(t.yaw), r.fillStyle = t.alive === !1 ? "#333" : t.color || "#73ce68", r.fillRect(-3, -1.8, 6, 3.6), r.restore(), r.save(), r.translate(t.x, t.z), r.rotate(t.turretYaw), r.strokeStyle = "#dce8cf", r.lineWidth = .8, r.beginPath(), r.moveTo(0, 0), r.lineTo(5, 0), r.stroke(), r.restore(), t.self && (r.strokeStyle = "#fff", r.lineWidth = .3, r.beginPath(), r.arc(t.x, t.z, 4, 0, Math.PI * 2), r.stroke());
781
- r.fillStyle = "#ffe097";
782
- for (let t of e.shells || []) r.beginPath(), r.arc(t.x, t.z, .7, 0, Math.PI * 2), r.fill();
783
- r.restore();
784
- },
785
- dispose() {
786
- d?.disconnect(), n.remove();
787
- }
788
- };
789
- }
790
- // src/games/tanks-live/TanksLiveGameCard.jsx
791
- var fe = /* @__PURE__ */ k({ default: () => Q }), pe = (t, n) => {
792
- e.getState().act(t, n).catch(() => {});
793
- }, Z = (e) => {
794
- e.throttle = 0, e.steer = 0, e.fire = !1;
795
- };
796
- function me(e, t) {
797
- let n = e?.manualTurn;
798
- return n?.status === "applying" ? "Aplicando configuración…" : n?.status === "sharing" ? "Sincronizando con el otro jugador…" : n?.status === "waiting_peer" ? "Esperando al otro jugador para compartir la configuración…" : e?.phase === "connected" ? "¡Conexión establecida!" : n?.status === "failed" || e?.phase === "failed" || e?.ice === "failed" ? "No pudimos conectar. Puedes reintentar." : t ? "La conexión está tardando más de lo esperado" : e?.phase === "restarting" || e?.phase === "reconnecting" ? "Reconectando…" : e?.phase === "listening" ? "Esperando la conexión del otro jugador…" : e?.phase === "calling" ? "Buscando al otro jugador…" : e?.phase === "negotiating" ? "Buscando una conexión entre los jugadores…" : "Preparando partida…";
799
- }
800
- function he({ gameKey: n, currentUserId: r, onMinimize: i, onDismiss: a, suspended: f = !1 }) {
801
- let h = p(o)?.config?.webrtc, C = e((e) => e.games[n]), w = g(f);
802
- w.current = f;
803
- let O = g(null), k = g(null), A = g(null), M = g({
804
- throttle: 0,
805
- steer: 0,
806
- fire: !1
807
- }), [F, I] = _(null), [L, ee] = _(""), [te, R] = _(!1), [ne, z] = _("loading"), [re, ie] = _(!1), [B, ae] = _(0), V = F?.net?.phase === "connected";
808
- m(() => {
809
- if (!C || C.status !== "active" || V) {
810
- ae(0);
811
- return;
812
- }
813
- let e = Date.now(), t = setInterval(() => ae(Date.now() - e), 1e3);
814
- return () => clearInterval(t);
815
- }, [
816
- n,
817
- C?.status,
818
- V
819
- ]), m(() => {
820
- ie(!1);
821
- }, [n, C?.status]), m(() => {
822
- f ? (Z(M.current), s()) : A.current?.resize();
823
- }, [f]);
824
- let H = ["active", "won"].includes(C?.status), [U, W] = _(null);
825
- if (m(() => {
826
- if (!H || !U || !O.current) return;
827
- let e = !1, t = null, n = {
828
- arena: U,
829
- lowPower: matchMedia("(pointer: coarse)").matches,
830
- reducedMotion: matchMedia("(prefers-reduced-motion: reduce)").matches,
831
- onEvent: (e) => {
832
- e.type === "context_lost" && R(!0);
833
- }
834
- }, r = () => {
835
- e || (t = de(O.current, n), A.current = t, z("2d"));
836
- };
837
- return te ? r() : import("./createLiveScene-BqI7qV4n.js").then(({ createLiveScene: i }) => {
838
- if (!e) try {
839
- t = i(O.current, n), A.current = t, z("3d");
840
- } catch {
841
- r();
842
- }
843
- }).catch(r), () => {
844
- e = !0, t?.dispose(), A.current === t && (A.current = null);
845
- };
846
- }, [
847
- H,
848
- U,
849
- te
850
- ]), m(() => {
851
- if (!H || C.status !== "active" || !O.current) return;
852
- let t = !1, i = 0, a = performance.now(), o = 0, d = null, f = ce(), p = /* @__PURE__ */ new Set();
853
- c();
854
- let m;
855
- try {
856
- let t = e.getState().createLiveSignaling(n);
857
- m = oe({
858
- iceServers: h?.iceServers,
859
- iceTransportPolicy: h?.iceTransportPolicy,
860
- game: C,
861
- selfId: String(r),
862
- isHost: String(C.hostId) === String(r),
863
- signaling: t
864
- }), k.current = m;
865
- } catch (e) {
866
- ee(e.message || "No se pudo iniciar WebRTC");
867
- return;
868
- }
869
- W(m.getRender().arena);
870
- function g() {
871
- p.clear(), Z(M.current), s();
872
- }
873
- function _(e, t) {
874
- /^(INPUT|TEXTAREA|SELECT)$/.test(e.target?.tagName) || [
875
- "KeyW",
876
- "KeyA",
877
- "KeyS",
878
- "KeyD",
879
- "ArrowUp",
880
- "ArrowDown",
881
- "ArrowLeft",
882
- "ArrowRight",
883
- "Space"
884
- ].includes(e.code) && (e.preventDefault(), t ? p.add(e.code) : p.delete(e.code), M.current.throttle = Number(p.has("KeyW") || p.has("ArrowUp")) - Number(p.has("KeyS") || p.has("ArrowDown")), M.current.steer = Number(p.has("KeyD") || p.has("ArrowRight")) - Number(p.has("KeyA") || p.has("ArrowLeft")), M.current.fire = p.has("Space"));
885
- }
886
- let v = (e) => _(e, !0), y = (e) => _(e, !1), b = O.current;
887
- b.addEventListener("keydown", v), b.addEventListener("keyup", y), b.addEventListener("blur", g);
888
- let x = () => {
889
- document.hidden && g();
890
- };
891
- document.addEventListener("visibilitychange", x), l({ type: "engine_start" }, n);
892
- function S(r) {
893
- if (t) return;
894
- let c = Math.min(80, r - a);
895
- a = r;
896
- let h = w.current || document.hidden;
897
- m.setInput(h ? {
898
- throttle: 0,
899
- steer: 0,
900
- fire: !1
901
- } : M.current), m.step(c);
902
- let g = m.getRender();
903
- if (h || A.current?.render(g, c), h && (p.clear(), f.reset(), s()), !h) for (let e of g.events || []) for (let t of le(e)) l(t, e.id);
904
- if (!h) for (let e of f.update(Y(g.tanks), c)) l(e);
905
- let _ = ue(g);
906
- _ && !d && (I(g), d = _, h || u(d, n), e.getState().liveFinish?.(n, g.winner)), r - o > 100 && (o = r, I(g)), i = requestAnimationFrame(S);
907
- }
908
- return i = requestAnimationFrame(S), () => {
909
- t = !0, cancelAnimationFrame(i), g(), m.destroy(), k.current = null, b.removeEventListener("keydown", v), b.removeEventListener("keyup", y), b.removeEventListener("blur", g), document.removeEventListener("visibilitychange", x);
910
- };
911
- }, [
912
- H,
913
- n,
914
- r
915
- ]), !C) return null;
916
- let G = String(r) === String(C.guestId), K = F?.status === "over" || C.status === "won", q = F?.net, J = q?.manualTurn, se = [
917
- "waiting_peer",
918
- "sharing",
919
- "applying"
920
- ].includes(J?.status), X = !V && (B >= 12e3 || q?.phase === "failed" || q?.ice === "failed" || J?.status === "failed" || !!L), fe = V ? q?.route?.relay === !0 ? "TURN Relay" : q?.route?.relay === !1 ? "P2P" : null : null, he = async (e, t) => {
921
- if (!k.current?.configureTurn) throw Error("La partida aún no está preparada. Espera un momento y vuelve a intentarlo.");
922
- await k.current.configureTurn(e, t);
923
- };
924
- function Q(e) {
925
- let t = O.current?.getBoundingClientRect(), n = Y(F?.tanks);
926
- if (!t || !n) return;
927
- let r = A.current?.screenToGround((e.clientX - t.left) / t.width * 2 - 1, 1 - (e.clientY - t.top) / t.height * 2);
928
- r && (M.current.turretYaw = Math.atan2(r.z - n.z, r.x - n.x));
929
- }
930
- let $ = (e, t) => ({
931
- onPointerDown: (n) => {
932
- n.preventDefault(), n.currentTarget.setPointerCapture(n.pointerId), M.current[e] = t;
933
- },
934
- onPointerUp: () => {
935
- M.current[e] = e !== "fire" && 0;
936
- },
937
- onPointerCancel: () => {
938
- M.current[e] = e !== "fire" && 0;
939
- },
940
- onLostPointerCapture: () => {
941
- M.current[e] = e !== "fire" && 0;
942
- }
943
- });
944
- return /* @__PURE__ */ D("section", {
945
- className: `chat-game-card tl-card${H ? " tl-card--active" : ""}`,
946
- "aria-label": "Tanques LIVE 3D",
947
- children: [
948
- /* @__PURE__ */ D("header", {
949
- className: "tl-header",
950
- children: [/* @__PURE__ */ E("strong", { children: "Tanques LIVE 3D" }), /* @__PURE__ */ D("div", {
951
- className: "tl-header-actions",
952
- children: [
953
- /* @__PURE__ */ E(t, { manager: d() }),
954
- i && /* @__PURE__ */ E("button", {
955
- onClick: i,
956
- "aria-label": "Minimizar",
957
- children: "−"
958
- }),
959
- a && /* @__PURE__ */ E("button", {
960
- onClick: a,
961
- "aria-label": "Ocultar",
962
- children: "×"
963
- })
964
- ]
965
- })]
966
- }),
967
- C.status === "invited" && /* @__PURE__ */ D("div", {
968
- className: "tl-invite",
969
- children: [
970
- /* @__PURE__ */ E("span", {
971
- className: "tl-invite-icon",
972
- "aria-hidden": "true",
973
- children: "🪖"
974
- }),
975
- /* @__PURE__ */ E("strong", { children: G ? "¿Listo para combatir?" : "Invitación enviada" }),
976
- /* @__PURE__ */ E("p", { children: G ? "Te invitaron a un combate en tiempo real." : "Esperando que el rival acepte…" }),
977
- G && /* @__PURE__ */ D("div", {
978
- className: "tl-invite-actions",
979
- children: [/* @__PURE__ */ E("button", {
980
- className: "tl-button-primary",
981
- onClick: () => pe(n, "game_accept"),
982
- children: "Aceptar combate"
983
- }), /* @__PURE__ */ E("button", {
984
- onClick: () => pe(n, "game_decline"),
985
- children: "Rechazar"
986
- })]
987
- })
988
- ]
989
- }),
990
- H && !K && /* @__PURE__ */ D("div", {
991
- className: `tl-connection tl-connection--${V ? "connected" : X ? "problem" : "pending"}`,
992
- children: [
993
- /* @__PURE__ */ D("div", {
994
- className: "tl-connection-heading",
995
- children: [
996
- V ? /* @__PURE__ */ E(y, {
997
- size: 18,
998
- "aria-hidden": "true"
999
- }) : X ? /* @__PURE__ */ E(v, {
1000
- size: 18,
1001
- "aria-hidden": "true"
1002
- }) : /* @__PURE__ */ E(x, {
1003
- size: 18,
1004
- className: "tl-connection-spinner",
1005
- "aria-hidden": "true"
1006
- }),
1007
- /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("strong", { children: X ? "Problemas de conexión" : me(q, !1) }), X && /* @__PURE__ */ E("p", { children: "No pudimos establecer una conexión con el otro jugador. Puedes configurar una conexión alternativa para continuar." })] }),
1008
- fe && /* @__PURE__ */ E("span", {
1009
- className: "tl-route-badge",
1010
- children: fe
1011
- })
1012
- ]
1013
- }),
1014
- !V && /* @__PURE__ */ E("p", {
1015
- className: "tl-connection-message",
1016
- role: "status",
1017
- children: X ? me(q, B >= 12e3) : P(q)
1018
- }),
1019
- J?.status === "synchronized" && /* @__PURE__ */ E("p", {
1020
- className: "tl-connection-shared",
1021
- role: "status",
1022
- children: J.received ? "Configuración recibida del otro jugador y aplicada en ambos." : "Configuración compartida y aplicada en ambos jugadores."
1023
- }),
1024
- J?.expiresAt != null && J.expiresAt <= Date.now() && /* @__PURE__ */ E("p", {
1025
- className: "tl-connection-expired",
1026
- role: "status",
1027
- children: "La validez indicada para las credenciales terminó. Introduce una configuración nueva si necesitas reconectar."
1028
- }),
1029
- /* @__PURE__ */ D("div", {
1030
- className: "tl-connection-actions",
1031
- children: [/* @__PURE__ */ D("button", {
1032
- type: "button",
1033
- className: X ? "tl-button-primary" : "",
1034
- onClick: () => ie(!0),
1035
- children: [/* @__PURE__ */ E(S, {
1036
- size: 15,
1037
- "aria-hidden": "true"
1038
- }), "Configurar conexión alternativa"]
1039
- }), !V && /* @__PURE__ */ D("button", {
1040
- type: "button",
1041
- disabled: se,
1042
- onClick: () => k.current?.reconnect(),
1043
- children: [/* @__PURE__ */ E(b, {
1044
- size: 15,
1045
- "aria-hidden": "true"
1046
- }), "Reintentar conexión"]
1047
- })]
1048
- })
1049
- ]
1050
- }),
1051
- /* @__PURE__ */ E(j, {
1052
- open: re && H && !K,
1053
- onClose: () => ie(!1),
1054
- onApply: he,
1055
- manualTurn: J
1056
- }),
1057
- (H || F) && /* @__PURE__ */ D(T, { children: [
1058
- /* @__PURE__ */ D("div", {
1059
- className: "tl-hud",
1060
- children: [(F?.tanks || []).map((e) => /* @__PURE__ */ D("span", { children: [
1061
- e.self ? "Tú" : "Rival",
1062
- " ",
1063
- /* @__PURE__ */ E("meter", {
1064
- min: "0",
1065
- max: "100",
1066
- value: e.hp,
1067
- "aria-label": e.self ? "Tu vida" : "Vida del rival"
1068
- }),
1069
- " ",
1070
- e.hp,
1071
- " HP"
1072
- ] }, e.id)), /* @__PURE__ */ D("span", {
1073
- role: "status",
1074
- children: [
1075
- V ? fe || "Conectado" : me(q, B >= 12e3),
1076
- " ",
1077
- Number.isFinite(q?.rtt) ? `· ${Math.round(q.rtt)} ms` : ""
1078
- ]
1079
- })]
1080
- }),
1081
- /* @__PURE__ */ E("div", {
1082
- className: "tl-stage",
1083
- ref: O,
1084
- tabIndex: 0,
1085
- "aria-label": "Arena. WASD o flechas para conducir, ratón para apuntar, clic o espacio para disparar",
1086
- onPointerMove: Q,
1087
- onPointerDown: (e) => {
1088
- Q(e), e.pointerType === "mouse" && (e.currentTarget.focus(), e.currentTarget.setPointerCapture(e.pointerId), Q(e), M.current.fire = !0);
1089
- },
1090
- onPointerUp: () => {
1091
- M.current.fire = !1;
1092
- },
1093
- onPointerCancel: () => {
1094
- Z(M.current);
1095
- },
1096
- onLostPointerCapture: () => {
1097
- M.current.fire = !1;
1098
- }
1099
- }),
1100
- /* @__PURE__ */ D("div", {
1101
- className: "tl-controls",
1102
- children: [
1103
- /* @__PURE__ */ E("div", {
1104
- className: "tl-joystick",
1105
- role: "group",
1106
- "aria-label": "Joystick de conducción",
1107
- onPointerDown: (e) => {
1108
- e.preventDefault(), e.currentTarget.setPointerCapture(e.pointerId);
1109
- let t = e.currentTarget.getBoundingClientRect();
1110
- M.current.steer = Math.max(-1, Math.min(1, (e.clientX - t.left - t.width / 2) / (t.width / 2))), M.current.throttle = Math.max(-1, Math.min(1, -(e.clientY - t.top - t.height / 2) / (t.height / 2)));
1111
- },
1112
- onPointerMove: (e) => {
1113
- if (!e.currentTarget.hasPointerCapture(e.pointerId)) return;
1114
- let t = e.currentTarget.getBoundingClientRect();
1115
- M.current.steer = Math.max(-1, Math.min(1, (e.clientX - t.left - t.width / 2) / (t.width / 2))), M.current.throttle = Math.max(-1, Math.min(1, -(e.clientY - t.top - t.height / 2) / (t.height / 2)));
1116
- },
1117
- onPointerUp: () => Z(M.current),
1118
- onPointerCancel: () => Z(M.current),
1119
- onLostPointerCapture: () => Z(M.current),
1120
- children: "✥"
1121
- }),
1122
- /* @__PURE__ */ D("div", {
1123
- className: "tl-drive",
1124
- "aria-label": "Controles de conducción",
1125
- children: [
1126
- /* @__PURE__ */ E("button", {
1127
- "aria-label": "Avanzar",
1128
- ...$("throttle", 1),
1129
- children: "▲"
1130
- }),
1131
- /* @__PURE__ */ E("button", {
1132
- "aria-label": "Girar izquierda",
1133
- ...$("steer", -1),
1134
- children: "◀"
1135
- }),
1136
- /* @__PURE__ */ E("button", {
1137
- "aria-label": "Retroceder",
1138
- ...$("throttle", -1),
1139
- children: "▼"
1140
- }),
1141
- /* @__PURE__ */ E("button", {
1142
- "aria-label": "Girar derecha",
1143
- ...$("steer", 1),
1144
- children: "▶"
1145
- })
1146
- ]
1147
- }),
1148
- /* @__PURE__ */ E("button", {
1149
- className: "tl-fire",
1150
- ...$("fire", !0),
1151
- children: "Disparar"
1152
- })
1153
- ]
1154
- }),
1155
- /* @__PURE__ */ E("div", {
1156
- className: "tl-reload",
1157
- children: /* @__PURE__ */ D("label", { children: [
1158
- "Recarga ",
1159
- /* @__PURE__ */ E("progress", {
1160
- max: "1",
1161
- value: Y(F?.tanks)?.reload ?? 1
1162
- }),
1163
- " ",
1164
- (Y(F?.tanks)?.reload ?? 1) >= 1 ? "Listo" : "Recargando…"
1165
- ] })
1166
- }),
1167
- /* @__PURE__ */ E("small", { children: "WASD / flechas · ratón o toca la arena para apuntar · espacio / clic para disparar" }),
1168
- /* @__PURE__ */ E("button", {
1169
- onClick: () => R((e) => !e),
1170
- children: ne === "2d" ? "Probar 3D" : "Vista 2D"
1171
- })
1172
- ] }),
1173
- K && /* @__PURE__ */ D("p", {
1174
- role: "status",
1175
- children: [
1176
- String(F?.winner ?? C.winnerId) === String(r) ? "¡Victoria!" : "Fin del combate",
1177
- " ",
1178
- /* @__PURE__ */ E("button", {
1179
- onClick: () => {
1180
- Promise.resolve(e.getState().liveRematch?.(n)).catch(() => {});
1181
- },
1182
- children: "Revancha"
1183
- })
1184
- ]
1185
- }),
1186
- H && /* @__PURE__ */ D("details", {
1187
- className: "tl-diagnostics",
1188
- children: [/* @__PURE__ */ E("summary", { children: "Diagnóstico de conexión" }), /* @__PURE__ */ E("pre", { children: JSON.stringify(N(F?.net), null, 2) })]
1189
- }),
1190
- H && !K && /* @__PURE__ */ E("button", {
1191
- onClick: () => k.current?.resign(),
1192
- children: "Rendirse"
1193
- }),
1194
- C.pending && /* @__PURE__ */ E("p", {
1195
- role: "status",
1196
- children: "Enviando…"
1197
- }),
1198
- C.pendingEvent && C.error && /* @__PURE__ */ E("button", {
1199
- onClick: () => {
1200
- e.getState().retry(n).catch(() => {});
1201
- },
1202
- children: "Reintentar envío"
1203
- }),
1204
- L && /* @__PURE__ */ E("p", {
1205
- role: "alert",
1206
- children: L
1207
- }),
1208
- C.error && /* @__PURE__ */ E("p", {
1209
- role: "alert",
1210
- children: C.error
1211
- }),
1212
- !["closed", "declined"].includes(C.status) && /* @__PURE__ */ E("footer", {
1213
- className: "tl-footer",
1214
- children: /* @__PURE__ */ E("button", {
1215
- onClick: () => pe(n, "game_end"),
1216
- children: "Cerrar partida"
1217
- })
1218
- })
1219
- ]
1220
- });
1221
- }
1222
- var Q = f(he);
1223
- export { se as a, J as c, Q as default, G as i, X as l, V as n, K as o, U as r, q as s, fe as t };
1224
-
1225
- //# sourceMappingURL=TanksLiveGameCard-DGsnMD3j.js.map