mirror of
https://github.com/denshooter/ttt-site.git
synced 2026-01-21 12:43:04 +01:00
212 lines
8.9 KiB
HTML
212 lines
8.9 KiB
HTML
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<meta http-equiv="Cache-Control" content="no-store" />
|
||
<title>Verbinde …</title>
|
||
<style>
|
||
:root{
|
||
--bg:#0b0f14; --card:#0f1620; --muted:#8aa0b3; --accent:#73d7ff; --ok:#8affc1; --warn:#ffc46b;
|
||
}
|
||
*{box-sizing:border-box} html,body{height:100%}
|
||
body{
|
||
margin:0; font:16px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,Ubuntu,"Helvetica Neue",Arial;
|
||
color:#e8f0f7; background:
|
||
radial-gradient(1200px 800px at 80% -10%,#132033 0%,transparent 60%),
|
||
linear-gradient(180deg,#0b0f14,#0b0f14 60%,#0d1420);
|
||
}
|
||
.container{min-height:100%; display:flex; align-items:center; justify-content:center; padding:24px}
|
||
.card{
|
||
width:min(960px,94vw); background:linear-gradient(180deg,var(--card),#0c121a);
|
||
border:1px solid #1a2634; border-radius:16px; padding:26px 28px; box-shadow:0 10px 30px rgba(0,0,0,.35)
|
||
}
|
||
h1{margin:0 0 8px; font-size:28px; letter-spacing:.2px}
|
||
.meta{display:flex; flex-wrap:wrap; gap:10px 18px; color:var(--muted); font-size:14px; margin-bottom:18px}
|
||
.badge{border:1px solid #243447; padding:6px 10px; border-radius:999px; background:#111926}
|
||
.grid{display:grid; grid-template-columns:1fr 1fr; gap:18px}
|
||
.box{border:1px solid #1a2634; border-radius:14px; padding:16px; background:#0c131c}
|
||
a{color:var(--accent); text-decoration:none}
|
||
.logo{font-weight:700; letter-spacing:.5px}
|
||
|
||
/* Stepper */
|
||
.stepper{display:flex; align-items:center; gap:12px; margin-bottom:10px}
|
||
.step{display:flex; align-items:center; gap:10px; color:var(--muted); font-size:14px}
|
||
.dot{width:14px; height:14px; border-radius:50%; background:#1a2534; border:1px solid #253247}
|
||
.step.active .dot{background:var(--accent); box-shadow:0 0 0 4px rgba(115,215,255,.18)}
|
||
.step.done .dot{background:var(--ok)}
|
||
.line{height:2px; flex:1; background:#1a2534}
|
||
|
||
/* Bars (indeterminate) */
|
||
.bar{height:10px; background:#0b1420; border-radius:999px; overflow:hidden; position:relative; margin-top:8px}
|
||
.fill{height:100%; width:35%; background:linear-gradient(90deg,var(--accent),var(--ok))}
|
||
.bar.indet .fill{animation:indetMove 1.15s linear infinite}
|
||
@keyframes indetMove{0%{transform:translateX(-120%)}100%{transform:translateX(300%)}}
|
||
|
||
/* states & text */
|
||
.row{display:grid; gap:10px}
|
||
.label{display:flex; justify-content:space-between; gap:12px; font-size:14px; color:#cfe3f5}
|
||
.sub{color:var(--muted); font-size:12px; margin-top:4px; white-space:nowrap; text-overflow:ellipsis; overflow:hidden}
|
||
.kbd{border:1px solid #2a3a4f; background:#0b1320; border-radius:6px; padding:2px 6px}
|
||
.tip{opacity:.95; font-size:14px}
|
||
.footer{display:flex; gap:12px; flex-wrap:wrap; margin-top:18px; color:var(--muted); font-size:13px}
|
||
.small{font-size:12px; color:#9fb3c9}
|
||
|
||
/* freeze animation when done */
|
||
.bar.done .fill{animation:none; width:100%}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container"><div class="card">
|
||
|
||
<div class="meta">
|
||
<span class="badge">Server: <span id="sv">…</span></span>
|
||
<span class="badge">Map: <span id="map">…</span></span>
|
||
<span class="badge">Modus: <span id="gm">…</span></span>
|
||
<span class="badge">Du: <span id="me">…</span></span>
|
||
</div>
|
||
|
||
<h1 class="logo">Willkommen auf <span id="sv2">deinem Server</span> 👋</h1>
|
||
|
||
<!-- Stepper -->
|
||
<div class="stepper">
|
||
<div class="step active" id="st-work"><div class="dot"></div>Workshop</div>
|
||
<div class="line"></div>
|
||
<div class="step" id="st-srv"><div class="dot"></div>Server-Content</div>
|
||
<div class="line"></div>
|
||
<div class="step" id="st-init"><div class="dot"></div>Initialisierung</div>
|
||
</div>
|
||
|
||
<div class="grid">
|
||
<div class="box">
|
||
<strong>Fortschritt</strong>
|
||
<div class="row" style="margin-top:6px">
|
||
<!-- Workshop -->
|
||
<div>
|
||
<div class="label"><span>Workshop-Downloads</span><span id="w-right">lädt …</span></div>
|
||
<div class="bar indet" id="w-bar"><div class="fill"></div></div>
|
||
<div class="sub" id="w-sub">Warte auf Workshop …</div>
|
||
</div>
|
||
<!-- Server -->
|
||
<div>
|
||
<div class="label"><span>Server-Content</span><span id="s-right">lädt …</span></div>
|
||
<div class="bar indet" id="s-bar"><div class="fill"></div></div>
|
||
<div class="sub" id="s-sub">Warte auf Server-Dateien …</div>
|
||
</div>
|
||
<!-- Init -->
|
||
<div>
|
||
<div class="label"><span>Initialisierung</span><span id="i-right">wartet …</span></div>
|
||
<div class="bar indet" id="i-bar"><div class="fill"></div></div>
|
||
<div class="sub" id="i-sub">Warte auf Client-Info …</div>
|
||
</div>
|
||
</div>
|
||
<div class="small" style="margin-top:10px">Datei: <span id="file">–</span></div>
|
||
</div>
|
||
|
||
<div class="box">
|
||
<strong>Tipps</strong>
|
||
<ul class="tip" style="margin:10px 0 0 18px">
|
||
<li>Drücke <span class="kbd">F1</span> für Hilfe/Regeln.</li>
|
||
<li><span class="kbd">F2</span> öffnet den TTT2-Shop.</li>
|
||
<li><span class="kbd">Tab</span> zeigt die Rollen-Übersicht.</li>
|
||
</ul>
|
||
<div class="footer">
|
||
<span>Website: <a href="https://ttt.dk0.dev">ttt.dk0.dev</a></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div></div>
|
||
|
||
<script>
|
||
(() => {
|
||
const $ = id => document.getElementById(id);
|
||
|
||
// Spielername aus Query (bevorzugt), sonst SteamID
|
||
const qp = new URLSearchParams(location.search);
|
||
const qpName = decodeURIComponent((qp.get('name')||qp.get('nick')||qp.get('player')||qp.get('n')||'').replace(/\+/g,' ')).trim();
|
||
|
||
// Grunddaten aus URL für sofortige Anzeige
|
||
$('map').textContent = qp.get('map') || 'Unbekannt';
|
||
$('me').textContent = qpName || qp.get('sid') || '—';
|
||
|
||
// Step/Phase-Helpers
|
||
const steps = {
|
||
work: { stepEl: $('st-work'), bar: $('w-bar'), right: $('w-right'), sub: $('w-sub') },
|
||
srv: { stepEl: $('st-srv'), bar: $('s-bar'), right: $('s-right'), sub: $('s-sub') },
|
||
init: { stepEl: $('st-init'), bar: $('i-bar'), right: $('i-right'), sub: $('i-sub') }
|
||
};
|
||
let active = 'work';
|
||
|
||
function setActive(key){
|
||
['work','srv','init'].forEach(k=>{
|
||
const c = steps[k].stepEl.classList;
|
||
c.remove('active');
|
||
});
|
||
steps[key].stepEl.classList.add('active');
|
||
active = key;
|
||
}
|
||
|
||
function markDone(key, text){
|
||
steps[key].stepEl.classList.add('done');
|
||
steps[key].bar.classList.remove('indet');
|
||
steps[key].bar.classList.add('done');
|
||
steps[key].right.textContent = text || 'fertig';
|
||
}
|
||
|
||
// ===== GMod Callbacks =====
|
||
window.GameDetails = function(serverName, serverUrl, mapName, maxPlayers, steamID, gamemode){
|
||
$('sv').textContent = serverName || 'Unbekannt';
|
||
$('sv2').textContent = serverName || 'deinem Server';
|
||
$('gm').textContent = gamemode || 'TTT2';
|
||
if (mapName) $('map').textContent = mapName;
|
||
|
||
// Name bevorzugen, sonst SteamID
|
||
$('me').textContent = qpName || steamID || '—';
|
||
};
|
||
|
||
// Wir ignorieren die Prozent-Callbacks absichtlich (falsch/inkonsistent).
|
||
window.SetFilesTotal = function(){ /* noop (indeterminierte Balken) */ };
|
||
window.SetFilesNeeded = function(){ /* noop (indeterminierte Balken) */ };
|
||
|
||
window.DownloadingFile = function(path){
|
||
$('file').textContent = (path||'').split('/').slice(-3).join('/');
|
||
// Kleiner UX-Touch: zeige Datei unter der aktuell aktiven Phase
|
||
if (active === 'work') steps.work.sub.textContent = 'Lade: ' + $('file').textContent;
|
||
else if (active === 'srv') steps.srv.sub.textContent = 'Lade: ' + $('file').textContent;
|
||
};
|
||
|
||
window.SetStatusChanged = function(status){
|
||
const s = (status||'').toLowerCase();
|
||
|
||
// rudimentäres Phasen-Mapping nur für Optik
|
||
if (s.includes('workshop')) { setActive('work'); steps.work.sub.textContent = status; }
|
||
else if (s.includes('download') || s.includes('materials') || s.includes('models') || s.includes('.bsp') || s.includes('file')) {
|
||
steps.work.stepEl.classList.add('done'); setActive('srv'); steps.srv.sub.textContent = status;
|
||
}
|
||
else if (s.includes('sending client info') || s.includes('precaching') || s.includes('client info') || s.includes('parsing')) {
|
||
steps.work.stepEl.classList.add('done'); steps.srv.stepEl.classList.add('done');
|
||
setActive('init'); steps.init.sub.textContent = status;
|
||
// wenn wir hier sind, frieren wir die Balken hübsch ein
|
||
markDone('work'); markDone('srv'); markDone('init','bereit');
|
||
}
|
||
|
||
// falls ein „complete“ kommt, markiere die aktuelle Phase als done
|
||
if (s.includes('complete')) {
|
||
if (active === 'work') { markDone('work'); setActive('srv'); }
|
||
else if (active === 'srv') { markDone('srv'); setActive('init'); }
|
||
}
|
||
};
|
||
|
||
// Fallback, falls GMod gar nichts ruft (nur für Browser-Preview)
|
||
if (document.visibilityState !== 'hidden') {
|
||
// steppt automatisch durch, rein für Demo/Preview
|
||
setTimeout(()=>{ setActive('srv'); }, 2000);
|
||
setTimeout(()=>{ setActive('init'); }, 4000);
|
||
setTimeout(()=>{ markDone('work'); markDone('srv'); markDone('init','bereit'); }, 6000);
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|