Wimmel-Suchspiel: finde 3 versteckte Fuesse in einer randvollen Szene. - Node/Express-Server generiert Szenen prozedural aus einer gemeinsamen Library (library/props = Deko, library/feet = Fuesse); Spezial-Level (emoji) unter scenes/ - Serverseitige Trefferpruefung (nicht per DOM austrickbar) - Frontend rendert die Szene als ein Bild -> fluessiges Zoom/Pan (panzoom) - Dark Mode, kein Start-Screen, Installationsskript + Deploy-Doku (Proxmox LXC) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
1.8 KiB
JavaScript
71 lines
1.8 KiB
JavaScript
// Die "Gesucht"-Leiste oben: zeigt die 3 zu findenden Fuesse und hakt gefundene ab.
|
|
|
|
const EMOJI_BASE = '/emoji/';
|
|
|
|
export class Legend {
|
|
constructor(container, counterEl) {
|
|
this.container = container;
|
|
this.counterEl = counterEl;
|
|
this.found = new Set();
|
|
this.total = 0;
|
|
}
|
|
|
|
render(targets) {
|
|
this.found.clear();
|
|
this.total = targets.length;
|
|
this.container.innerHTML = '';
|
|
|
|
for (const t of targets) {
|
|
const card = document.createElement('div');
|
|
card.className = 'legend-item';
|
|
card.dataset.id = t.id;
|
|
|
|
const thumb = document.createElement('div');
|
|
thumb.className = 'legend-thumb';
|
|
|
|
if (t.assetUrl) {
|
|
const img = document.createElement('img');
|
|
img.src = t.assetUrl;
|
|
img.alt = t.name || 'Fuss';
|
|
thumb.appendChild(img);
|
|
} else if (t.glyph) {
|
|
// Emoji: Bild aus Set, sonst Textzeichen
|
|
const img = document.createElement('img');
|
|
img.alt = t.name || 'Fuss';
|
|
img.onerror = () => {
|
|
const span = document.createElement('span');
|
|
span.className = 'legend-emoji';
|
|
span.textContent = t.glyph;
|
|
img.replaceWith(span);
|
|
};
|
|
img.src = EMOJI_BASE + t.codepoint + '.png';
|
|
thumb.appendChild(img);
|
|
}
|
|
|
|
card.appendChild(thumb);
|
|
this.container.appendChild(card);
|
|
}
|
|
|
|
this._updateCounter();
|
|
}
|
|
|
|
markFound(id) {
|
|
if (this.found.has(id)) return false;
|
|
this.found.add(id);
|
|
const card = this.container.querySelector(`.legend-item[data-id="${id}"]`);
|
|
if (card) card.classList.add('found');
|
|
this._updateCounter();
|
|
return true;
|
|
}
|
|
|
|
allFound() {
|
|
return this.found.size >= this.total && this.total > 0;
|
|
}
|
|
|
|
_updateCounter() {
|
|
if (this.counterEl) {
|
|
this.counterEl.textContent = `${this.found.size} / ${this.total}`;
|
|
}
|
|
}
|
|
}
|