Repository navigation
Expand file tree
/
Copy pathmultiplayer.html
More file actions
318 lines (298 loc) · 17.4 KB
/
Copy pathmultiplayer.html
File metadata and controls
318 lines (298 loc) · 17.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<!-- Opt-in STATICO alla View Transitions API — deve stare qui, non iniettato via JS in ritardo (vedi js/audio/audio-manager.js per la storia completa del bug che questo risolve). -->
<style>@view-transition { navigation: auto; }</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="manifest" href="manifest.json">
<link rel="icon" href="images/icons/icon-192.png">
<link rel="apple-touch-icon" href="images/icons/icon-180.png">
<meta name="theme-color" content="#0b0d14">
<!-- Sfondo scuro applicato SUBITO, prima che gli script auth-gate/supabase
sotto finiscano di scaricare (bloccanti) — evita un flash bianco
prima del nero dell'overlay di auth-gate.js, vedi il commento
completo in cartoteca.html dove è stato diagnosticato. -->
<style>html, body { background: #0b0d14; }</style>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Millennium">
<script src="js/ui/error-recovery.js"></script>
<!-- Accesso obbligatorio (js/cloud/auth-gate.js) — vedi il commento lì:
rimanda a index.html se non c'è una sessione approvata. -->
<script src="js/vendor/supabase.min.js"></script>
<script src="js/cloud/supabase-config.js"></script>
<script src="js/cloud/cloud-sync.js"></script>
<script src="js/cloud/auto-sync.js"></script>
<script src="js/cloud/auth-gate.js"></script>
<script src="js/pwa-register.js"></script>
<link rel="stylesheet" href="js/ui/page-loader.css">
<link rel="preload" href="audio/soundtracks/56. Battle for the Millennium.mp3" as="audio">
<link rel="stylesheet" href="js/ui/topbar.css">
<!-- Ritratto del giocatore in fondo alla topbar: lo monta PageTopbar. -->
<link rel="stylesheet" href="js/ui/deck-switcher.css">
<title>Yu-Gi-Oh! Millennium Duel Arena — Multiplayer</title>
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="js/ui/card.css">
<link rel="stylesheet" href="js/ui/effects.css">
<link rel="stylesheet" href="js/ui/duel-cinematics.css">
<!-- Lobby e Sala d'Attesa: tutto il loro aspetto vive in un file a parte,
altrimenti questa pagina non resterebbe leggibile a mano. -->
<link rel="stylesheet" href="js/ui/mp-lobby.css">
<!-- Le scatole 3D dei mazzi, le stesse di Creazione Deck e del Negozio. -->
<link rel="stylesheet" href="js/ui/deck-box.css">
<!-- Selettore di mazzo/arena/musica, condiviso con il Duello Libero. -->
<link rel="stylesheet" href="js/ui/duel-setup.css">
<!-- Morra cinese: qui si gioca PRIMA che l'arena venga caricata (decide
chi comincia), quindi il suo foglio di stile non può arrivare con
gli altri dell'arena — servirebbe troppo tardi. -->
<link rel="stylesheet" href="js/ui/duel-rps.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
/* Niente selezione di testo/pressione lunga sul contenuto del gioco (nomi
carte, personaggi, pulsanti...): su mobile un tocco prolungato apriva
il menu di sistema "Cerca su Google"/copia invece di far scattare
l'interazione del gioco. input/textarea/select restano selezionabili
normalmente, altrimenti non si potrebbe più scrivere nei campi.
-webkit-tap-highlight-color: via anche il rettangolo azzurrino che
il browser mobile dipinge sopra QUALUNQUE elemento toccato —
segnalato dall'utente tenendo premuto. Non toglie alcun riscontro
al tocco: i pulsanti del gioco hanno il proprio stato ":active",
che quel rettangolo si limitava a coprire con un colore di sistema
estraneo alla tavolozza. Il contorno di messa a fuoco da tastiera
resta intatto: quello NON passa da questa proprietà.
overscroll-behavior: niente rimbalzo elastico quando si trascina
la pagina oltre il bordo, e niente "tira per ricaricare" — su una
pagina di gioco quel gesto e' a un dito di distanza dal ricaricare
tutto a meta' duello. */
html, body { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; overscroll-behavior: none; overscroll-behavior-y: none; }
input, textarea, select { -webkit-user-select: text; user-select: text; }
html, body { height: 100%; }
body {
font-family: 'Arial', sans-serif;
color: #fff;
background: #0b0d14;
min-height: 100vh;
}
/* Fondale sullo strato FISSO insieme alla sfumatura, non sul
background di body: con `html, body { background }` (toppa
anti-flash) la propagazione alla canvas si disattiva e il
background di body resta ritagliato dentro il suo box, alto un
solo viewport (height: 100%) — scorrendo comparirebbe una fascia
nera sotto l'immagine. Un elemento position:fixed copre sempre il
viewport, qualunque sia l'altezza del contenuto. */
body::before {
content: '';
position: fixed;
inset: 0;
z-index: 0;
/* Stessa parete di geroglifici del resto del menu: il fondale
precedente (Mago Nero / Drago Bianco) era blu e freddo, e
insieme all'interfaccia ciano faceva sembrare questa pagina di
un altro gioco — segnalato dall'utente. */
background:
linear-gradient(180deg, rgba(8,6,5,0.45) 0%, rgba(8,6,5,0.55) 60%, rgba(6,4,3,0.65) 100%),
url('images/background/backgroundMenu.jpg') center/cover no-repeat;
pointer-events: none;
}
/* ============================================================
Topbar: markup/CSS generati da js/ui/topbar.js + js/ui/topbar.css.
Override di .topbar QUI SOTTO: a differenza di ogni altra pagina,
qui NON è sticky (position: relative) — sotto la lobby viene
iniettata l'intera arena di duello (vedi js/multiplayer/mp-lobby.js),
che ha già una propria gestione dello scroll/layout: una topbar
sticky in cima interferirebbe. Comportamento preesistente,
preservato di proposito nel passaggio al componente condiviso.
============================================================ */
.topbar { position: relative; }
/* Contenitore dell'arena di duello: vuoto finché la stanza non si
riempie — vedi loadDuelArena() in js/multiplayer/mp-lobby.js, che ci
inserisce lo stesso markup di duelMonstersCore.html quando è il
momento. */
#arenaMount:empty { display: none; }
/* A duello avviato #arenaMount sta fra <body> e .game-container, che
nell'arena (duelMonstersCore.html) è un figlio DIRETTO del body:
body è una colonna flessibile alta quanto la finestra, e il campo
prende lo spazio che resta (flex: 1, min-height: 0). Un #arenaMount
semplice blocco spezzava quella catena: il campo cresceva quanto il
suo contenuto (919px su una finestra da 900), lo stepper delle fasi
non veniva più compresso e la pagina scorreva — i "layout un po'
disallineati a inizio partita" segnalati dall'utente in Multiplayer.
Qui #arenaMount diventa a sua volta quella colonna, e il campo si
comporta come nella pagina del duello. */
body.mp-in-duello #arenaMount {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<script src="js/ui/page-loader.js"></script>
<div id="topbarMount"></div>
<!-- Fondale animato della lobby: raggi rotanti e granelli dorati.
Puramente decorativi, quindi nascosti agli screen reader. -->
<div class="mp-rays" aria-hidden="true"></div>
<div class="mp-motes" id="mpMotes" aria-hidden="true"></div>
<div class="mp-lobby-wrap" id="mpLobbyScreen">
<div class="mp-lobby" id="mpLobby" data-schermata="ingresso">
<!-- ============ Ingresso ============ -->
<div data-vista="ingresso">
<div class="mp-screen">
<h2 class="mp-title">Duello Multiplayer</h2>
<p class="mp-sub">Sfida un amico online, in tempo reale</p>
<div class="mp-choice">
<button type="button" class="mp-tab active" id="mpTabCreate"><span class="mp-tab-icon">🏛️</span>Crea Stanza</button>
<button type="button" class="mp-tab" id="mpTabJoin"><span class="mp-tab-icon">🔑</span>Entra con un codice</button>
</div>
<div class="mp-lobby-panel" id="mpPanelCreate">
<p>Apri una stanza, scegli arena e musica mentre aspetti, e il duello parte appena arriva il tuo avversario.</p>
<button type="button" class="mp-btn mp-btn--primary" id="mpCreateBtn">Crea Stanza</button>
</div>
<div class="mp-lobby-panel" id="mpPanelJoin" style="display:none;">
<p>Inserisci il codice che ti ha dato chi ha creato la stanza.</p>
<input type="text" id="mpJoinCode" maxlength="5" placeholder="A3K9Q" class="mp-code-input">
<button type="button" class="mp-btn mp-btn--primary" id="mpJoinBtn">Entra nella Stanza</button>
</div>
<!-- Nell'uso normale l'indirizzo non va toccato: sta
chiuso qui dentro invece di occupare il primo posto
della pagina, ma resta a portata di mano per chi
sviluppa contro un server locale. -->
<details class="mp-advanced">
<summary>Server</summary>
<input type="text" id="mpServerUrl" value="wss://millennium-duel-arena-relay.onrender.com" placeholder="wss://tuo-server.onrender.com">
<!--<small class="mp-hint">Lascia com'è per giocare sul server pubblico. Se nessuno gioca da un po', la prima connessione può richiedere fino a un minuto: il server gratuito si sta risvegliando.</small>-->
</details>
</div>
</div>
<!-- ============ Sala d'attesa ============ -->
<div data-vista="attesa">
<div class="mp-screen">
<div class="mp-room-code-box" id="mpRoomCodeBox">
<span class="mp-room-code-label">Codice della stanza</span>
<div class="mp-code-tiles" id="mpCodeTiles"></div>
<!-- Il codice anche come testo unico: le tessere qui
sopra sono una lettera per elemento, comode da
animare ma scomode da leggere per chiunque
(una persona con screen reader, o un test) voglia
il codice intero in un colpo solo. -->
<span class="mp-room-code" id="mpRoomCodeValue" hidden>-----</span>
<button type="button" class="mp-copy-btn" id="mpCopyBtn">📋 Copia codice</button>
</div>
<div class="mp-seats">
<div class="mp-seat mp-seat--you">
<div class="mp-seat-avatar">🧑</div>
<div class="mp-seat-name" id="mpSeatYouName">Tu</div>
<div class="mp-seat-role" id="mpSeatYouRole">Pronto</div>
</div>
<div class="mp-vs">VS</div>
<div class="mp-seat mp-seat--empty" id="mpSeatOpponent">
<!-- La lama di scansione è un elemento suo: i due
pseudo-elementi del posto disegnano già le
parentesi d'angolo. -->
<span class="mp-seat-scan"></span>
<div class="mp-seat-avatar" id="mpSeatOppAvatar">❔</div>
<div class="mp-seat-name" id="mpSeatOppName"><span class="mp-dots">In attesa</span></div>
<div class="mp-seat-role" id="mpSeatOppRole">Posto libero</div>
</div>
</div>
<p class="mp-occupancy"><strong id="mpOccupancy">1</strong> / 2 duellanti nella stanza</p>
<!-- Il mazzo è una scelta PERSONALE: sta fuori dal
riquadro delle impostazioni condivise, che l'ospite
vede in sola lettura, perché qui invece sceglie
anche lui. -->
<!-- I due riquadri sono riempiti da js/ui/duel-setup.js,
lo stesso componente usato prima di un Duello
Libero. Restano separati perché hanno un padrone
diverso: il mazzo è personale, arena e musica le
decide chi ha creato la stanza. -->
<div class="mp-own" id="mpDeckGroup">
<div class="mp-setup-head">
<span class="mp-setup-title">Il tuo mazzo</span>
<span class="mp-setup-note">Lo scegli solo per te</span>
</div>
<div id="mpDecks"></div>
</div>
<div class="mp-setup" id="mpSetup">
<div class="mp-setup-head">
<span class="mp-setup-title">Arena e musica</span>
<span class="mp-setup-note" id="mpSetupNote">Le scegli tu: valgono per entrambi</span>
</div>
<div id="mpSetupMount"></div>
</div>
<!-- Il duello non comincia da sé quando la stanza si
riempie: parte quando LO DICONO ENTRAMBI. Prima si
faceva partire un conto alla rovescia appena il
secondo giocatore entrava, e chi stava ancora
scegliendo il mazzo si trovava dentro al duello
senza averlo deciso. Questa barra resta spenta
finché il posto avversario è vuoto. -->
<div class="mp-ready" id="mpReadyBar" hidden>
<div class="mp-ready-stati">
<span class="mp-ready-stato" id="mpReadyYou">
<span class="mp-ready-spia"></span> Tu
</span>
<span class="mp-ready-stato" id="mpReadyOpp">
<span class="mp-ready-spia"></span> Avversario
</span>
</div>
<button type="button" class="mp-ready-btn" id="mpReadyBtn">Sono pronto</button>
<p class="mp-ready-nota" id="mpReadyNota">Il duello comincia quando siete pronti tutti e due.</p>
</div>
</div>
</div>
<!-- Unica riga di stato per entrambe le schermate: fuori dalle
due viste, così non sparisce passando dall'una all'altra. -->
<div class="mp-lobby-status" id="mpStatus"></div>
</div>
</div>
<!-- Arena di duello: vuota finché non si trova un avversario, poi
riempita a runtime da js/multiplayer/mp-lobby.js (loadDuelArena())
con lo stesso markup di duelMonstersCore.html — vedi lì per il
perché. -->
<div id="arenaMount"></div>
<script src="js/save-manager.js"></script>
<script src="js/audio/audio-manager.js"></script>
<script src="js/native/app-back-button.js"></script>
<script src="js/native/haptics.js"></script>
<script src="js/native/native-save-backup.js"></script>
<script>
// Come ogni altra pagina "di menu" (duello-libero.html ecc.): musica
// principale in loop finché non si entra davvero in un duello — a quel
// punto lo script audio dell'arena (dentro duelMonstersCore.html) la
// sostituisce da sé con la colonna sonora del duello.
initAudioManager();
</script>
<script src="js/ui/icon-library.js"></script>
<script src="js/ui/deck-switcher.js"></script>
<script src="js/ui/topbar.js"></script>
<script>
PageTopbar.render('#topbarMount', { icon: 'multiplayer', title: 'Duello Multiplayer' });
Icons.hydrate();
</script>
<script src="js/ui/deck-box.js"></script>
<!-- Servono al gruppo "Carte ammesse" del selettore: CARD_ORIGIN_LABELS e
le provenienze inventate dal giocatore. cards-db.js non esegue nulla
al caricamento e non richiede il database carte completo, quindi si
può includere da solo (stessa nota in duello-libero.html). Quando
l'arena verrà caricata, loadDuelArena salterà questi due file invece
di rieseguirli — vedi il commento lì. -->
<script src="js/data/cards-db.js"></script>
<script src="js/data/custom-taxonomy.js"></script>
<!-- Controllo "il mio mazzo rispetta le Carte ammesse?" mentre si è
ancora in sala, quando il mazzo si può ancora cambiare. -->
<script src="js/data/custom-cards.js"></script>
<script src="js/data/card-origins.generated.js"></script>
<script src="js/data/deck-legality.js"></script>
<script src="js/data/arena-options.js"></script>
<script src="js/ui/duel-setup.js"></script>
<!-- La morra si gioca fra i due giocatori veri (le mosse viaggiano, vedi
morraCinese in js/multiplayer/mp-lobby.js) subito dopo il conto alla
rovescia e prima che l'arena venga caricata. -->
<script src="js/ui/duel-rps.js"></script>
<script src="js/multiplayer/network.js"></script>
<script src="js/multiplayer/mp-passo-comune.js"></script>
<script src="js/multiplayer/mp-lobby.js"></script>
</body>
</html>