Files
ttt-site/loading/index.html
2025-08-29 18:05:57 +02:00

212 lines
8.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>