Files
wownerogue/html/match.html
T

172 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wownerogue — Multiplayer Arena</title>
<link rel="stylesheet" href="styles/commerce-consent.css">
<style>
@font-face { font-family: PixelOperator; src: url('styles/fonts/pixel-operator/PixelOperator.ttf') format('truetype'); font-display: swap; }
@font-face { font-family: PixelOperator; src: url('styles/fonts/pixel-operator/PixelOperator-Bold.ttf') format('truetype'); font-weight: 700; font-display: swap; }
:root { --bg: #07090c; --panel: #11161b; --panel2: #171d24; --text: #d7dde5; --muted: #8b97a7;
--accent: #55d96b; --gold: #f2b84b; --cyan: #62d8ff; --purple: #a98bff; --line: #303946; }
* { box-sizing: border-box; }
body { min-height: 100vh; margin: 0; background:
radial-gradient(circle at 50% -20%, rgba(73,118,84,.28), transparent 42%),
linear-gradient(180deg, #0b0e12, var(--bg) 42%); color: var(--text);
font-family: PixelOperator, ui-monospace, monospace; }
body::before { content:""; position:fixed; inset:0; pointer-events:none; opacity:.17;
background-image:linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px),linear-gradient(90deg,rgba(255,255,255,.025) 1px,transparent 1px);
background-size:24px 24px; mask-image:linear-gradient(to bottom,#000,transparent 75%); }
#app { position:relative; max-width: 1040px; margin: 0 auto; padding: 18px clamp(12px,3vw,28px) 32px; }
.topbar { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:28px; }
.brand { color:#fff; font-weight:700; letter-spacing:.08em; }
.brand span { color:var(--accent); }
nav { display:flex; gap:8px; }
nav a { color:var(--muted); text-decoration:none; border:1px solid var(--line); border-radius:5px; padding:6px 10px; font-size:13px; }
nav a:hover, nav a:focus-visible { color:#fff; border-color:#627083; }
.hero { margin-bottom:14px; }
.eyebrow { color:var(--accent); font-size:11px; letter-spacing:.18em; text-transform:uppercase; }
h1 { margin: 5px 0 6px; color:#fff; font-size:clamp(24px,5vw,42px); line-height:1; letter-spacing:.02em; }
.hero p { max-width:720px; margin:0; color:var(--muted); line-height:1.45; }
.mode-summary { display:flex; align-items:center; flex-wrap:wrap; gap:7px; margin-top:12px; }
.tag { display:inline-flex; align-items:center; min-height:24px; padding:3px 8px; border:1px solid #344b3a; border-radius:999px;
color:#a7f3b4; background:rgba(40,110,55,.12); font-size:12px; }
#rulesetCatalog { display:flex; gap:6px; flex-wrap:wrap; }
#rulesetCatalog .tag { color:var(--muted); border-color:var(--line); background:rgba(255,255,255,.025); }
#rulesetCatalog .tag.active { color:#a7f3b4; border-color:#3d7b49; }
.muted { color: var(--muted); font-size: 0.85rem; }
#panel { overflow:hidden; background:rgba(17,22,27,.96); border:1px solid var(--line); border-radius:12px;
box-shadow:0 22px 70px rgba(0,0,0,.38), inset 0 1px rgba(255,255,255,.035); }
#queuePanel { padding:clamp(16px,3vw,26px); }
.section-title { margin:0 0 3px; color:#fff; font-size:17px; }
button { font:inherit; background:#212832; border:1px solid #3b4653; color:var(--text); padding:9px 14px; border-radius:6px; cursor:pointer; }
button:hover:not(:disabled), button:focus-visible { border-color:#718095; filter:brightness(1.1); }
button.primary { background:linear-gradient(#67e17a,#42b957); color:#061108; border-color:#7af08b; font-weight:700; box-shadow:0 0 18px rgba(85,217,107,.14); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
.economies { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:10px; margin:14px 0; }
.economy { min-height:92px; padding:12px; text-align:left; border:1px solid var(--line); border-radius:8px; background:var(--panel2); cursor:pointer; }
.economy strong, .economy small { display:block; }
.economy strong { color:#fff; margin-bottom:5px; font-size:14px; }
.economy small { color:var(--muted); font-size:11px; line-height:1.35; }
.economy .availability { margin-top:8px; color:#7ee38d; font-size:10px; letter-spacing:.08em; }
.economy.active { border-color:var(--accent); background:linear-gradient(145deg,rgba(66,185,87,.16),rgba(23,29,36,.95)); box-shadow:0 0 0 1px rgba(85,217,107,.18); }
.economy[data-economy="credits_prestige"].active { border-color:var(--purple); box-shadow:0 0 0 1px rgba(169,139,255,.18); }
.economy[data-economy="crypto_race"].active { border-color:var(--gold); box-shadow:0 0 0 1px rgba(242,184,75,.18); }
.economy.locked { opacity: 0.55; cursor: not-allowed; }
#status { min-height:24px; margin:10px 0; color:#b8c1cc; }
.queue-actions { display:flex; gap:8px; }
#racePanel { position:relative; }
.race-head { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px 12px; border-bottom:1px solid var(--line); background:#0e1318; }
#hud { display:flex; gap:6px; flex-wrap:wrap; font-size:12px; }
#hud span { background:#192029; border:1px solid #2b3541; padding:4px 8px; border-radius:4px; }
#racePlace { color:var(--gold); }
#renderModes { display:flex; gap:4px; }
#renderModes button { padding:4px 8px; font-size:11px; }
#renderModes button.active { border-color:var(--cyan); color:var(--cyan); background:#10222a; }
#renderModes button.locked { opacity:.42; }
#stage { position:relative; width:100%; height:clamp(360px,64vh,650px); background:#000;
border-bottom:1px solid var(--line); isolation:isolate; }
#stage::after { content:""; position:absolute; inset:0; pointer-events:none; z-index:10; box-shadow:inset 0 0 70px rgba(0,0,0,.62); }
#stage canvas { display:block; }
.race-foot { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:10px 12px; }
#raceResult { position:absolute; z-index:30; inset:50% auto auto 50%; transform:translate(-50%,-50%); min-width:min(360px,86%);
padding:18px; text-align:center; color:#fff; background:rgba(8,11,14,.94); border:1px solid var(--gold); border-radius:9px; box-shadow:0 18px 60px #000; }
.touch-pad { display:none; grid-template-areas:". up ." "left down right"; gap:5px; }
.touch-pad button { width:44px; height:40px; padding:0; touch-action:manipulation; }
.touch-pad [data-dir="up"]{grid-area:up}.touch-pad [data-dir="left"]{grid-area:left}.touch-pad [data-dir="down"]{grid-area:down}.touch-pad [data-dir="right"]{grid-area:right}
.hidden { display: none !important; }
@media (max-width:700px), (pointer:coarse) {
#app { padding-inline:10px; }
.topbar { margin-bottom:20px; }
.economies { grid-template-columns:1fr; }
.economy { min-height:0; }
#stage { height:min(58vh,520px); }
.race-head { align-items:flex-start; flex-direction:column; }
.race-foot { align-items:flex-end; }
.touch-pad { display:grid; }
}
@media (max-width:430px) { nav a { padding:5px 7px; font-size:11px; } .brand { font-size:13px; } #renderModes { max-width:100%; overflow-x:auto; } }
@media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto!important; transition:none!important; animation:none!important; } }
</style>
<!-- Render kit -->
<script src="js/render/sceneModel.js"></script>
<script src="js/render/fxLayer.js"></script>
<script src="js/render/skins.js"></script>
<script src="js/render/charCustomize.js"></script>
<script src="js/render/atlas.js"></script>
<script src="js/render/charSprites.js"></script>
<script src="js/render/zoomControl.js"></script>
<script src="js/render/packRegistry.js"></script>
<script src="js/render/assetPacks.js"></script>
<script src="js/render/avatarVisuals.js"></script>
<script src="js/render/asciiRenderer.js"></script>
<script src="js/render/tileRenderer.js"></script>
<script src="js/render/isoRenderer.js"></script>
<script src="js/render/threeRenderer.js"></script>
<script src="/runtime-config.js"></script>
<script type="importmap">
{"imports":{"three":"/vendor/three/0.160.0/three.module.min.js"}}
</script>
<script src="js/render/renderModes.js"></script>
<script src="js/ui/matchUiCopy.js"></script>
</head>
<body>
<div id="app">
<header class="topbar">
<div class="brand">WOWNERO<span>GUE</span> / ARENA</div>
<nav aria-label="Main navigation"><a href="/">← Lobby</a><a href="/tavern">Tavern</a><a href="/responsible-play">Responsible Play</a></nav>
</header>
<section class="hero" aria-labelledby="modeTitle">
<div class="eyebrow">Operator-selected live mode</div>
<h1 id="modeTitle">Dungeon Race</h1>
<p id="modeDescription">Outrun the other delvers, survive the shared dungeon, and reach the exit first.</p>
<div class="mode-summary"><span id="modeBadge" class="tag">FIRST TO EXIT</span><div id="rulesetCatalog" aria-label="Available multiplayer modes"></div></div>
</section>
<div id="panel">
<div id="queuePanel">
<h2 class="section-title">Choose your entry mode</h2>
<div id="economyHelp" class="muted">Each available entry mode keeps its results on a separate leaderboard.</div>
<div id="economies" class="economies" role="group" aria-label="Match entry mode"></div>
<div id="status" role="status" aria-live="polite">Connecting to the arena…</div>
<div class="queue-actions">
<button id="queueBtn" class="primary">Join Queue</button>
<button id="leaveBtn" class="hidden">Leave Queue</button>
</div>
</div>
<div id="racePanel" class="hidden">
<div class="race-head">
<div id="hud" aria-label="Match status">
<span id="raceMode"></span>
<span id="raceEconomy"></span>
<span id="raceTimer"></span>
<span id="racePlace"></span>
</div>
<div id="renderModes" aria-label="Render style"></div>
</div>
<div id="stage" aria-label="Live multiplayer dungeon"></div>
<div id="raceResult" class="hidden" role="status"></div>
<div class="race-foot">
<p id="objectiveHint" class="muted">Arrow keys / WASD · reach the exit (→) first · drag or pinch the camera</p>
<div id="matchDpad" class="touch-pad" role="group" aria-label="Match movement controls">
<button type="button" data-dir="up" aria-label="Move up">▲</button>
<button type="button" data-dir="left" aria-label="Move left">◀</button>
<button type="button" data-dir="down" aria-label="Move down">▼</button>
<button type="button" data-dir="right" aria-label="Move right">▶</button>
</div>
</div>
</div>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="js/legal/commerceConsent.js"></script>
<script src="js/matchClient.js"></script>
</body>
</html>