import { el, clear, onTeardown } from '../lib/dom.js'; import { loadSnapshot } from '../data/snapshot.js'; import { Sprite } from '../components/Sprite.js'; import { whosThat, recordResult } from '../store/whosThat.js'; import { prefersReducedMotion } from '../store/settings.js'; const loose = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, ''); const pretty = (name) => name.replace(/-/g, ' '); /** * Hidden mini-game — reachable by the Konami code (↑↑↓↓←→←→BA) or seven * quick taps on the nav wordmark, and by the bare #/whos-that hash once * you know it. Guess the Pokémon from its silhouette. */ export async function WhosThatView() { const view = el('section', { class: 'view wtp' }); const snap = await loadSnapshot(); const pool = snap.species; let current = null; let revealed = false; let wrong = 0; let streak = 0; const art = el('div', { class: 'wtp__art' }); const caption = el('div', { class: 'wtp__caption' }, 'Who’s that Pokémon?'); const hint = el('div', { class: 'wtp__hint' }); const feedback = el('div', { class: 'wtp__feedback' }); const scoreEl = el('div', { class: 'wtp__score' }); const input = el('input', { class: 'wtp__input', type: 'text', placeholder: 'Your guess…', autocomplete: 'off', autocapitalize: 'off', autocorrect: 'off', spellcheck: 'false', onkeydown: (e) => { if (e.key === 'Enter') submitGuess(); }, }); const guessBtn = el( 'button', { class: 'button wtp__go', type: 'button', onclick: submitGuess }, 'Guess', ); const revealBtn = el( 'button', { class: 'button button--ghost', type: 'button', onclick: () => reveal(false) }, 'Reveal', ); const nextBtn = el('button', { class: 'button', type: 'button', onclick: next }, 'Next →'); function renderScore() { const s = whosThat.get(); const pct = s.plays ? Math.round((s.wins / s.plays) * 100) : 0; clear(scoreEl).append( el('span', { class: 'wtp__pill' }, `Streak ${streak}`), el('span', { class: 'wtp__pill' }, `Best ${s.best}`), el('span', { class: 'wtp__pill wtp__pill--dim' }, `${s.wins}/${s.plays} · ${pct}%`), ); } function syncButtons() { input.hidden = revealed; guessBtn.hidden = revealed; revealBtn.hidden = revealed; nextBtn.hidden = !revealed; } function next() { current = pool[Math.floor(Math.random() * pool.length)]; revealed = false; wrong = 0; art.classList.remove('is-revealed'); clear(art).append(Sprite(current.id, { style: 'official', size: 260, alt: 'Mystery Pokémon' })); caption.textContent = 'Who’s that Pokémon?'; hint.textContent = ''; feedback.textContent = ''; feedback.className = 'wtp__feedback'; input.value = ''; syncButtons(); input.focus(); } function reveal(win) { revealed = true; art.classList.add('is-revealed'); caption.textContent = `It’s ${pretty(current.name)}! #${String(current.id).padStart(4, '0')}`; if (win) { streak += 1; feedback.textContent = wrong === 0 ? 'Nailed it!' : 'Got there!'; feedback.className = 'wtp__feedback is-good'; } else { streak = 0; feedback.textContent = 'That one got away.'; feedback.className = 'wtp__feedback is-bad'; } recordResult(win, streak); renderScore(); syncButtons(); if (win && !prefersReducedMotion()) { art.animate( [{ transform: 'scale(0.9)' }, { transform: 'scale(1.05)' }, { transform: 'scale(1)' }], { duration: 380, easing: 'cubic-bezier(.34,1.4,.64,1)' }, ); } nextBtn.focus(); } function submitGuess() { if (revealed || !input.value.trim()) return; if (loose(input.value) === loose(current.name)) { reveal(true); return; } wrong += 1; input.value = ''; feedback.className = 'wtp__feedback is-bad'; if (wrong === 1) { feedback.textContent = 'Nope — try again.'; hint.textContent = `${loose(current.name).length} letters`; } else if (wrong === 2) { feedback.textContent = 'Last clue…'; hint.textContent = `Starts with “${current.name[0].toUpperCase()}”, ${loose(current.name).length} letters`; } else { reveal(false); // three strikes return; } input.focus(); } view.append( el('nav', { class: 'lookup__nav' }, el('a', { class: 'link', href: '#/' }, '‹ Dex')), el( 'header', { class: 'view__header' }, el('h1', {}, 'Who’s that Pokémon?'), el('p', {}, 'Name it from the silhouette. Three misses and it’s revealed.'), ), scoreEl, el('div', { class: 'wtp__stage' }, art), caption, hint, el('div', { class: 'wtp__controls' }, input, guessBtn, revealBtn, nextBtn), feedback, ); renderScore(); next(); const off = whosThat.subscribe(() => { if (revealed) renderScore(); }); onTeardown(view, off); return view; }