Weitere Fehler im Chastity ingame game behoben
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
This commit is contained in:
@@ -22,22 +22,36 @@
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 0.5rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
.game-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
color: var(--color-text);
|
||||
white-space: pre-wrap;
|
||||
height: 14rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.game-timer {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-primary);
|
||||
text-align: center;
|
||||
margin: 0.75rem 0;
|
||||
letter-spacing: 0.04em;
|
||||
height: 4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.game-timer.active { opacity: 1; }
|
||||
.game-timer.urgent { color: #e74c3c; }
|
||||
.game-btn-row {
|
||||
margin-top: 1rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
|
||||
.level-display {
|
||||
display: flex;
|
||||
@@ -82,6 +96,20 @@
|
||||
}
|
||||
.group-item input[type=radio] { accent-color: var(--color-primary); }
|
||||
|
||||
.toy-item {
|
||||
display: flex; align-items: center; gap: 0.6rem;
|
||||
padding: 0.6rem 0.85rem; border-radius: 8px;
|
||||
background: var(--color-card); border: 1px solid var(--color-secondary);
|
||||
margin-bottom: 0.5rem; cursor: pointer; transition: border-color 0.15s; user-select: none;
|
||||
}
|
||||
.toy-item.is-checked { border-color: var(--color-primary); }
|
||||
.toy-item input { accent-color: var(--color-primary); flex-shrink: 0; width: 14px; height: 14px; cursor: pointer; }
|
||||
.toy-item span { flex: 1; min-width: 0; }
|
||||
.toy-item-name { font-size: 0.95rem; font-weight: 600; color: var(--color-text); }
|
||||
.toy-item-desc { display: block; font-size: 0.8rem; color: var(--color-muted); margin-top: 0.15rem; }
|
||||
.toy-item-img { width: 38px; height: 38px; object-fit: cover; border-radius: 6px; flex-shrink: 0; }
|
||||
.toys-hint { font-size: 0.85rem; color: var(--color-muted); margin: 0 0 1rem; line-height: 1.5; }
|
||||
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
@@ -130,6 +158,21 @@
|
||||
<!-- Freigegebene Locks (checkLocks-Meldungen) -->
|
||||
<div id="lockMessages" class="lock-messages" style="display:none;"></div>
|
||||
|
||||
<!-- Toy-Auswahl vor Spielstart -->
|
||||
<div id="toyBox" style="display:none;">
|
||||
<div class="game-card">
|
||||
<div class="game-label">Verfügbare Toys</div>
|
||||
<p class="toys-hint">
|
||||
Deaktiviere Toys, die nicht zur Verfügung stehen.
|
||||
Aufgaben, die diese benötigen, werden deaktiviert.
|
||||
</p>
|
||||
<div id="toyToggleList"></div>
|
||||
<div style="margin-top:1.25rem;">
|
||||
<button class="btn-primary" onclick="handleToyConfirm()">▶ Spiel starten</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Initialisierung: Gruppe wählen -->
|
||||
<div id="initBox" class="init-box" style="display:none;">
|
||||
<h2>Spiel-Set auswählen</h2>
|
||||
@@ -143,22 +186,22 @@
|
||||
<!-- Laufendes Spiel -->
|
||||
<div id="gameBox" style="display:none;">
|
||||
|
||||
<!-- Task oder Lock in Queue -->
|
||||
<div id="queueBox" class="game-card" style="display:none;">
|
||||
<div class="game-label" id="queueLabel"></div>
|
||||
<div class="game-text" id="queueText"></div>
|
||||
<div style="display:flex;gap:0.6rem;margin-top:1.1rem;">
|
||||
<button class="btn-primary" id="btnOk" onclick="handleOk()">OK</button>
|
||||
<!-- Einheitliche Spielkarte -->
|
||||
<div id="gameCard" class="game-card" style="display:none;">
|
||||
<div class="game-label" id="gameLabel"></div>
|
||||
<div class="game-text" id="gameText"></div>
|
||||
<div class="game-timer" id="gameTimer"></div>
|
||||
<div class="game-btn-row">
|
||||
<button class="btn-primary" id="gameBtn" onclick="handleGameBtn()" style="width:100%;height:100%;"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Aktive Aufgabe (läuft) -->
|
||||
<div id="activeBox" class="game-card" style="display:none;">
|
||||
<div class="game-label">Aktive Aufgabe</div>
|
||||
<div class="game-text" id="activeText"></div>
|
||||
<div class="game-timer" id="activeTimer" style="display:none;"></div>
|
||||
<div style="display:flex;gap:0.6rem;margin-top:1.1rem;">
|
||||
<button class="btn-primary" id="btnErledigt" onclick="handleErledigt()">✓ Erledigt</button>
|
||||
<!-- Release-Text (Sperren) -->
|
||||
<div id="lockReleaseBox" class="game-card" style="display:none;">
|
||||
<div class="game-label">🔓 Sperre aufgehoben</div>
|
||||
<div class="game-text" id="releaseText"></div>
|
||||
<div style="margin-top:1.1rem;">
|
||||
<button class="btn-primary" id="btnReleaseOk">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -168,7 +211,12 @@
|
||||
<h2>Level 6 erreicht!</h2>
|
||||
<div class="game-label" id="finisherTitle"></div>
|
||||
<div class="game-text" id="finisherText" style="margin-top:0.5rem;text-align:left;"></div>
|
||||
<button class="btn-primary" onclick="completeGame()" style="margin-top:1.25rem;">✓ Spiel beenden</button>
|
||||
<button class="btn-primary" id="btnFinisherOk" style="margin-top:1.25rem;">✓ OK</button>
|
||||
</div>
|
||||
|
||||
<!-- Debug -->
|
||||
<div style="margin-top:1.5rem;">
|
||||
<button onclick="debugExit()" style="width:100%;padding:0.45rem;border-radius:8px;border:1px dashed #666;background:transparent;color:#666;font-size:0.78rem;cursor:pointer;">🐛 Debug exit</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -187,17 +235,18 @@
|
||||
const params = new URLSearchParams(location.search);
|
||||
const lockId = params.get('lockId');
|
||||
const autoGameSetId = params.get('gameSetId');
|
||||
let _state = null;
|
||||
let _timerInt = null;
|
||||
let _pendingIsLock = false;
|
||||
let _pendingHasDuration = false;
|
||||
const freshStart = params.get('fresh') === '1';
|
||||
let _resolvedGameSetId = autoGameSetId;
|
||||
let _state = null;
|
||||
let _timerInt = null;
|
||||
let _gameAction = null; // 'queue-start' | 'queue-done' | 'active-running' | 'active-done'
|
||||
|
||||
function goBack() {
|
||||
if (lockId) location.href = '/games/chastity/activelock.html?lockId=' + lockId;
|
||||
else history.back();
|
||||
}
|
||||
|
||||
async function completeGame() {
|
||||
async function debugExit() {
|
||||
const url = '/lock-game/complete' + (lockId ? '?lockId=' + lockId : '');
|
||||
await fetch(url, { method: 'POST' });
|
||||
goBack();
|
||||
@@ -207,40 +256,134 @@
|
||||
|
||||
async function boot() {
|
||||
try {
|
||||
const r = await fetch('/lock-game/state');
|
||||
if (r.status === 404) {
|
||||
if (autoGameSetId) {
|
||||
await autoStartGame(autoGameSetId);
|
||||
} else {
|
||||
await loadGroups();
|
||||
if (!freshStart) {
|
||||
const r = await fetch('/lock-game/state');
|
||||
if (r.ok) {
|
||||
_state = await r.json();
|
||||
hide('loadingHint');
|
||||
await runGameLoop();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
if (r.status !== 404) throw new Error('Fehler beim Laden des Spielzustands');
|
||||
}
|
||||
|
||||
// Fresh start or no existing game: resolve gameSetId, then show toy selection
|
||||
let gameSetId = autoGameSetId;
|
||||
if (!gameSetId && lockId) {
|
||||
try {
|
||||
const lockR = await fetch('/keyholder/cardlock/' + lockId);
|
||||
if (lockR.ok) {
|
||||
const lockData = await lockR.json();
|
||||
gameSetId = lockData.gameSetId || null;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
_resolvedGameSetId = gameSetId;
|
||||
if (gameSetId) {
|
||||
await loadAndShowToys(gameSetId);
|
||||
} else {
|
||||
await loadGroups();
|
||||
}
|
||||
if (!r.ok) throw new Error('Fehler beim Laden des Spielzustands');
|
||||
_state = await r.json();
|
||||
hide('loadingHint');
|
||||
await runGameLoop();
|
||||
} catch (e) {
|
||||
showError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function autoStartGame(gameSetId) {
|
||||
// ── Toy-Auswahl ──────────────────────────────────────────────────────────
|
||||
|
||||
async function loadAndShowToys(gameSetId) {
|
||||
try {
|
||||
const r = await fetch('/lock-game/init?aufgabenGruppeId=' + gameSetId, { method: 'POST' });
|
||||
if (!r.ok) throw new Error('Initialisierung fehlgeschlagen');
|
||||
const stateR = await fetch('/lock-game/state');
|
||||
_state = await stateR.json();
|
||||
const r = await fetch('/lock-game/toys?aufgabenGruppeId=' + gameSetId);
|
||||
if (!r.ok) throw new Error('Fehler beim Laden der Toys');
|
||||
const toys = await r.json();
|
||||
hide('loadingHint');
|
||||
await runGameLoop();
|
||||
|
||||
const list = document.getElementById('toyToggleList');
|
||||
if (toys.length === 0) {
|
||||
list.innerHTML = '<p style="font-size:0.85rem;color:var(--color-muted);font-style:italic;margin:0;">'
|
||||
+ 'Keine Toys erforderlich – alle Aufgaben werden gespielt.</p>';
|
||||
} else {
|
||||
list.innerHTML = toys.map(t => `
|
||||
<label class="toy-item is-checked">
|
||||
<input type="checkbox" value="${esc(t.toyId)}" checked>
|
||||
<span>
|
||||
<span class="toy-item-name">${esc(t.name)}</span>
|
||||
${t.beschreibung ? `<span class="toy-item-desc">${esc(t.beschreibung)}</span>` : ''}
|
||||
</span>
|
||||
${t.bild ? `<img class="toy-item-img" src="data:image/png;base64,${t.bild}" alt="">` : ''}
|
||||
</label>`).join('');
|
||||
|
||||
list.addEventListener('change', e => {
|
||||
const cb = e.target;
|
||||
if (cb.type === 'checkbox') cb.closest('.toy-item')?.classList.toggle('is-checked', cb.checked);
|
||||
}, { once: false });
|
||||
}
|
||||
show('toyBox');
|
||||
} catch (e) {
|
||||
showError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToyConfirm() {
|
||||
const excludedToyIds = [];
|
||||
document.querySelectorAll('#toyToggleList input[type="checkbox"]').forEach(cb => {
|
||||
if (!cb.checked) excludedToyIds.push(cb.value);
|
||||
});
|
||||
hide('toyBox');
|
||||
show('loadingHint');
|
||||
try {
|
||||
await startWithExcludedToys(_resolvedGameSetId, excludedToyIds);
|
||||
} catch (e) {
|
||||
showError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function startWithExcludedToys(gameSetId, excludedToyIds) {
|
||||
const params = new URLSearchParams({ aufgabenGruppeId: gameSetId });
|
||||
excludedToyIds.forEach(id => params.append('excludedToyIds', id));
|
||||
const r = await fetch('/lock-game/init?' + params.toString(), { method: 'POST' });
|
||||
|
||||
if (r.status === 422) {
|
||||
const body = await r.json().catch(() => ({}));
|
||||
await showValidationError(body.error || 'Das Aufgaben-Set ist nicht vollständig.');
|
||||
return;
|
||||
}
|
||||
if (!r.ok) throw new Error('Initialisierung fehlgeschlagen');
|
||||
|
||||
const stateR = await fetch('/lock-game/state');
|
||||
_state = await stateR.json();
|
||||
hide('loadingHint');
|
||||
await runGameLoop();
|
||||
}
|
||||
|
||||
async function showValidationError(msg) {
|
||||
hide('loadingHint');
|
||||
showError('Das Spiel kann nicht gestartet werden: ' + msg
|
||||
+ ' Du wirst in Kürze zurückgeleitet und erhältst eine Strafe.');
|
||||
|
||||
if (lockId) {
|
||||
fetch('/lock-game/penalty?lockId=' + lockId, { method: 'POST' }).catch(() => {});
|
||||
}
|
||||
|
||||
let secs = 5;
|
||||
const interval = setInterval(() => {
|
||||
const box = document.getElementById('errorBox');
|
||||
if (box) box.textContent = 'Das Spiel kann nicht gestartet werden: ' + msg
|
||||
+ ` Rückleitung in ${--secs}s…`;
|
||||
if (secs <= 0) { clearInterval(interval); goBack(); }
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function loadGroups() {
|
||||
const r = await fetch('/lock-game/groups');
|
||||
const groups = r.ok ? await r.json() : [];
|
||||
|
||||
if (groups.length === 1) {
|
||||
_resolvedGameSetId = groups[0].gruppenId;
|
||||
await loadAndShowToys(groups[0].gruppenId);
|
||||
return;
|
||||
}
|
||||
|
||||
hide('loadingHint');
|
||||
const list = document.getElementById('groupList');
|
||||
if (groups.length === 0) {
|
||||
@@ -270,40 +413,39 @@
|
||||
if (!sel) return;
|
||||
hide('initBox');
|
||||
show('loadingHint');
|
||||
try {
|
||||
const r = await fetch('/lock-game/init?aufgabenGruppeId=' + sel.value, { method: 'POST' });
|
||||
if (!r.ok) throw new Error('Initialisierung fehlgeschlagen');
|
||||
const stateR = await fetch('/lock-game/state');
|
||||
_state = await stateR.json();
|
||||
hide('loadingHint');
|
||||
await runGameLoop();
|
||||
} catch (e) {
|
||||
showError(e.message);
|
||||
}
|
||||
_resolvedGameSetId = sel.value;
|
||||
await loadAndShowToys(sel.value);
|
||||
}
|
||||
|
||||
// ── Game Loop ─────────────────────────────────────────────────────────────
|
||||
|
||||
function setGameCard(label, text, action, btnLabel) {
|
||||
document.getElementById('gameLabel').textContent = label;
|
||||
document.getElementById('gameText').textContent = text;
|
||||
const timerEl = document.getElementById('gameTimer');
|
||||
timerEl.classList.remove('active', 'urgent');
|
||||
timerEl.textContent = '';
|
||||
_gameAction = action;
|
||||
document.getElementById('gameBtn').textContent = btnLabel;
|
||||
}
|
||||
|
||||
async function runGameLoop() {
|
||||
hide('queueBox');
|
||||
hide('activeBox');
|
||||
hide('gameCard');
|
||||
hide('finisherBox');
|
||||
clearTimer();
|
||||
|
||||
if (_state.level >= 6) {
|
||||
await showFinisher();
|
||||
await showFinisherFlow();
|
||||
return;
|
||||
}
|
||||
|
||||
renderLevelBar(_state.level);
|
||||
|
||||
// Aktive Aufgabe läuft noch
|
||||
if (_state.activeTask) {
|
||||
showActiveTask(_state.activeTask, _state.activeTaskEnd);
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue leer → nächsten Task holen
|
||||
if (!_state.taskInQueue && !_state.lockInQueue) {
|
||||
await fetch('/lock-game/next-task', { method: 'POST' });
|
||||
const r = await fetch('/lock-game/state');
|
||||
@@ -317,90 +459,155 @@
|
||||
if (_state.lockInQueue) {
|
||||
let sperre;
|
||||
try { sperre = JSON.parse(_state.lockInQueue); } catch { sperre = {}; }
|
||||
_pendingIsLock = true;
|
||||
_pendingHasDuration = !!(sperre.minutenVon || sperre.minutenBis);
|
||||
document.getElementById('queueLabel').textContent = '🔒 Neue Sperre';
|
||||
document.getElementById('queueText').textContent = sperre.text || _state.lockInQueue;
|
||||
setGameCard('🔒 Neue Sperre', sperre.text || sperre.kurzText || '', 'queue-start', '▶ Starten');
|
||||
} else if (_state.taskInQueue) {
|
||||
let aufgabe;
|
||||
try { aufgabe = JSON.parse(_state.taskInQueue); } catch { aufgabe = {}; }
|
||||
_pendingIsLock = false;
|
||||
_pendingHasDuration = !!(aufgabe.sekundenVon || aufgabe.sekundenBis);
|
||||
document.getElementById('queueLabel').textContent = '🎯 Neue Aufgabe';
|
||||
document.getElementById('queueText').textContent = aufgabe.text || _state.taskInQueue;
|
||||
const hasDuration = !!(aufgabe.sekundenVon || aufgabe.sekundenBis);
|
||||
setGameCard('🎯 Neue Aufgabe', aufgabe.text || '', hasDuration ? 'queue-start' : 'queue-done',
|
||||
hasDuration ? '▶ Starten' : '✓ Erledigt');
|
||||
}
|
||||
show('gameBox');
|
||||
show('queueBox');
|
||||
show('gameCard');
|
||||
}
|
||||
|
||||
function showActiveTask(text, endIso) {
|
||||
document.getElementById('activeText').textContent = text;
|
||||
show('gameBox');
|
||||
show('activeBox');
|
||||
show('gameCard');
|
||||
const timerEl = document.getElementById('gameTimer');
|
||||
timerEl.classList.remove('active', 'urgent');
|
||||
timerEl.textContent = '';
|
||||
document.getElementById('gameLabel').textContent = 'Aktive Aufgabe';
|
||||
document.getElementById('gameText').textContent = text;
|
||||
|
||||
if (endIso) {
|
||||
const end = new Date(endIso);
|
||||
startTimer(end, document.getElementById('activeTimer'));
|
||||
if (end > Date.now()) {
|
||||
_gameAction = 'active-running';
|
||||
document.getElementById('gameBtn').textContent = '✕ Abbrechen';
|
||||
startTimer(end, timerEl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_gameAction = 'active-done';
|
||||
document.getElementById('gameBtn').textContent = '✓ Erledigt';
|
||||
}
|
||||
|
||||
function handleGameBtn() {
|
||||
switch (_gameAction) {
|
||||
case 'queue-start': doQueueStart(); break;
|
||||
case 'queue-done': doQueueDone(); break;
|
||||
case 'active-running': doCancelCountdown(); break;
|
||||
case 'active-done': doErledigt(); break;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleOk() {
|
||||
// Locks prüfen (nach jeder Aktion)
|
||||
await checkAndShowLocks();
|
||||
|
||||
if (_pendingIsLock || _pendingHasDuration) {
|
||||
// Task/Sperre aktivieren und Timer starten
|
||||
async function doQueueStart() {
|
||||
try {
|
||||
await checkAndShowLocks();
|
||||
const r = await fetch('/lock-game/apply-task', { method: 'POST' });
|
||||
if (!r.ok) { showError('Fehler'); return; }
|
||||
if (!r.ok) { showError('Fehler beim Starten'); return; }
|
||||
const stateR = await fetch('/lock-game/state');
|
||||
_state = await stateR.json();
|
||||
} else {
|
||||
// Keine Dauer → sofort nächsten Task
|
||||
await fetch('/lock-game/next-task', { method: 'POST' });
|
||||
const r = await fetch('/lock-game/state');
|
||||
_state = await r.json();
|
||||
}
|
||||
await runGameLoop();
|
||||
await runGameLoop();
|
||||
} catch (e) { showError(e.message || 'Fehler (Starten)'); }
|
||||
}
|
||||
|
||||
async function handleErledigt() {
|
||||
await checkAndShowLocks();
|
||||
async function doQueueDone() {
|
||||
try {
|
||||
await checkAndShowLocks();
|
||||
const applyR = await fetch('/lock-game/apply-task', { method: 'POST' });
|
||||
if (!applyR.ok) { showError('Fehler beim Anwenden'); return; }
|
||||
const nextR = await fetch('/lock-game/next-task', { method: 'POST' });
|
||||
if (!nextR.ok) { showError('Fehler beim Ziehen'); return; }
|
||||
const stateR = await fetch('/lock-game/state');
|
||||
_state = await stateR.json();
|
||||
await runGameLoop();
|
||||
} catch (e) { showError(e.message || 'Fehler (Erledigt)'); }
|
||||
}
|
||||
|
||||
if (_state.level >= 6) {
|
||||
await showFinisher();
|
||||
return;
|
||||
}
|
||||
function doCancelCountdown() {
|
||||
clearTimer();
|
||||
_gameAction = 'active-done';
|
||||
document.getElementById('gameBtn').textContent = '✓ Erledigt';
|
||||
}
|
||||
|
||||
await fetch('/lock-game/next-task', { method: 'POST' });
|
||||
const r = await fetch('/lock-game/state');
|
||||
_state = await r.json();
|
||||
await runGameLoop();
|
||||
async function doErledigt() {
|
||||
try {
|
||||
await checkAndShowLocks();
|
||||
if (_state.level >= 6) { await showFinisherFlow(); return; }
|
||||
const r = await fetch('/lock-game/next-task', { method: 'POST' });
|
||||
if (!r.ok) { showError('Fehler beim Ziehen'); return; }
|
||||
const stateR = await fetch('/lock-game/state');
|
||||
_state = await stateR.json();
|
||||
await runGameLoop();
|
||||
} catch (e) { showError(e.message || 'Fehler (Erledigt)'); }
|
||||
}
|
||||
|
||||
async function checkAndShowLocks() {
|
||||
const r = await fetch('/lock-game/check-locks', { method: 'POST' });
|
||||
if (!r.ok) return;
|
||||
const texts = await r.json();
|
||||
if (texts && texts.length > 0) {
|
||||
const valid = texts ? texts.filter(t => t != null && t !== '') : [];
|
||||
if (valid.length > 0) {
|
||||
const box = document.getElementById('lockMessages');
|
||||
box.innerHTML = texts.map(t => `<p>🔓 ${esc(t)}</p>`).join('');
|
||||
box.innerHTML = valid.map(t => `<p>🔓 ${esc(t)}</p>`).join('');
|
||||
show('lockMessages');
|
||||
await new Promise(res => setTimeout(res, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
async function showFinisher() {
|
||||
async function showFinisherFlow() {
|
||||
show('gameBox');
|
||||
const r = await fetch('/lock-game/finisher');
|
||||
if (!r.ok) {
|
||||
document.getElementById('finisherTitle').textContent = '';
|
||||
document.getElementById('finisherText').textContent = 'Glückwunsch – du hast Level 6 erreicht!';
|
||||
} else {
|
||||
const f = await r.json();
|
||||
document.getElementById('finisherTitle').textContent = f.kurzText || '';
|
||||
document.getElementById('finisherText').textContent = f.text || '';
|
||||
}
|
||||
show('finisherBox');
|
||||
hide('gameCard');
|
||||
hide('lockReleaseBox');
|
||||
hide('finisherBox');
|
||||
|
||||
// 1. Release-Texte sequenziell anzeigen
|
||||
try {
|
||||
const r = await fetch('/lock-game/release-locks');
|
||||
if (r.ok) {
|
||||
const texts = await r.json();
|
||||
for (const text of texts) {
|
||||
await waitForReleaseOk(text);
|
||||
}
|
||||
}
|
||||
} catch (_) { /* ignorieren */ }
|
||||
|
||||
// 2. Finisher laden und Zeit messen
|
||||
const finisherStartTime = Date.now();
|
||||
let finisher = null;
|
||||
try {
|
||||
const r = await fetch('/lock-game/finisher');
|
||||
if (r.ok) finisher = await r.json();
|
||||
} catch (_) { /* ignorieren */ }
|
||||
|
||||
document.getElementById('finisherTitle').textContent = finisher?.kurzText || '';
|
||||
document.getElementById('finisherText').textContent = finisher?.text || 'Glückwunsch – du hast Level 6 erreicht!';
|
||||
|
||||
// 3. Warten bis Nutzer OK drückt
|
||||
await new Promise(resolve => {
|
||||
document.getElementById('btnFinisherOk').onclick = resolve;
|
||||
show('finisherBox');
|
||||
});
|
||||
|
||||
// 4. Zeit berechnen und Spiel beenden
|
||||
const timeInMinutes = Math.round((Date.now() - finisherStartTime) / 60000);
|
||||
const params = new URLSearchParams({ timeInMinutes });
|
||||
if (lockId) params.set('lockId', lockId);
|
||||
await fetch('/lock-game/complete?' + params.toString(), { method: 'POST' });
|
||||
goBack();
|
||||
}
|
||||
|
||||
function waitForReleaseOk(text) {
|
||||
return new Promise(resolve => {
|
||||
document.getElementById('releaseText').textContent = text;
|
||||
document.getElementById('btnReleaseOk').onclick = () => {
|
||||
hide('lockReleaseBox');
|
||||
resolve();
|
||||
};
|
||||
show('lockReleaseBox');
|
||||
});
|
||||
}
|
||||
|
||||
// ── Level-Bar ─────────────────────────────────────────────────────────────
|
||||
@@ -413,8 +620,12 @@
|
||||
|
||||
// ── Timer ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function playSound(src) {
|
||||
try { new Audio(src).play().catch(() => {}); } catch (_) {}
|
||||
}
|
||||
|
||||
function startTimer(endDate, el) {
|
||||
el.style.display = '';
|
||||
el.classList.add('active');
|
||||
clearTimer();
|
||||
_timerInt = setInterval(() => {
|
||||
const diff = Math.max(0, Math.round((endDate - Date.now()) / 1000));
|
||||
@@ -422,7 +633,12 @@
|
||||
const s = String(diff % 60).padStart(2, '0');
|
||||
el.textContent = m + ':' + s;
|
||||
el.classList.toggle('urgent', diff < 30);
|
||||
if (diff === 0) clearTimer();
|
||||
if (diff === 0) {
|
||||
clearTimer();
|
||||
playSound('/audio/alarm.mp3');
|
||||
_gameAction = 'active-done';
|
||||
document.getElementById('gameBtn').textContent = '✓ Erledigt';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user