feat(jogos): telão com contagem regressiva, jogadas lado a lado e resultado

- comando "telao" (validado: texto curto, tom, símbolos permitidos, até
  2 lados) e letreiro por cima do rosto: título enorme que estoura e
  pisca, as duas jogadas (VOCÊ ✊ × ✋ CARINHA), cor por resultado;
- pedra, papel e tesoura: 3, 2, 1 piscando, "JÁ!", o que ele leu da sua
  mão, "VENCI!!!" / "VOCÊ GANHOU!" / "EMPATE!" com placar e o campeão;
  quantos dedos e achou! também usam o telão;
- o cartão do painel mostra o mesmo destaque.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
julio
2026-10-11 22:33:09 -03:00
co-authored by Claude Opus 5.5
parent 3d954efc03
commit 82f9673692
6 changed files with 218 additions and 6 deletions
+43
View File
@@ -20,6 +20,27 @@
@keyframes aparecerFigura{from{opacity:0}to{opacity:1}}
@media(prefers-reduced-motion:reduce){.iconeOlho,.figurinhaDinamica{animation:none}}
#fps{position:fixed;left:4px;top:2px;color:#2b3a4a;font:12px monospace;display:none}
/* telão dos jogos: por cima do rosto, texto grande na cor dos olhos (o resultado tem cor própria) */
#telao{position:fixed;inset:0;z-index:8;display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:2vh;background:rgba(0,0,0,.9);font-family:system-ui,sans-serif;color:var(--cor);text-align:center;pointer-events:none}
#telao .titulo{font-weight:900;font-size:clamp(40px,24vh,240px);line-height:1;letter-spacing:.02em;
text-shadow:0 0 .25em currentColor}
#telao .sub{font-size:clamp(14px,6vh,48px);font-weight:600;opacity:.9}
#telao .lados{display:flex;align-items:center;gap:5vw}
#telao .lado{display:flex;flex-direction:column;align-items:center;gap:1vh;min-width:22vw}
#telao .rot{font-size:clamp(12px,5vh,36px);font-weight:700;letter-spacing:.1em;text-transform:uppercase;opacity:.85}
#telao .sim{font-size:clamp(40px,26vh,220px);line-height:1}
#telao .nome{font-size:clamp(12px,5vh,36px);font-weight:600}
#telao .x{font-size:clamp(24px,12vh,110px);font-weight:300;opacity:.7}
#telao.tom-eu .titulo{color:#ffd166}
#telao.tom-voce .titulo{color:#7ae582}
#telao.tom-empate .titulo{color:#cfe8ff}
#telao.tom-conta .titulo{font-size:clamp(60px,48vh,420px)}
#telao.entra .titulo,#telao.entra .lados{animation:telaoEstoura .45s cubic-bezier(.2,1.6,.4,1)}
#telao.piscar.entra .titulo{animation:telaoEstoura .45s cubic-bezier(.2,1.6,.4,1),telaoPisca .5s steps(2) .45s infinite}
@keyframes telaoEstoura{from{transform:scale(.3);opacity:0}to{transform:scale(1);opacity:1}}
@keyframes telaoPisca{50%{opacity:.25}}
@media(prefers-reduced-motion:reduce){#telao *{animation:none!important}}
/* sem a placa: placa ✕ celular na cor dos olhos. Deitado, o rosto dormindo fica onde está e o
desenho fica em pé no canto direito; em pé, o rosto sobe e o desenho vai embaixo. */
#semServidor{position:fixed;right:2.5vw;top:50%;transform:translateY(-50%);width:min(24vw,42vh);
@@ -646,6 +667,27 @@ function falarFrase(texto, segundos) { // "dizer ..." do modo vida: le
if (window.MotorRosto) window.MotorRosto.faixa('legenda', { texto, segundos: segundos || Math.max(4, Math.min(14, texto.length / 9)) });
falar(texto);
}
/* Telão dos jogos: contagem regressiva piscando, as duas jogadas lado a lado e o resultado grande. */
let telaoAte = null;
function mostrarTelao(json) {
let t;
try { t = JSON.parse(json); } catch (e) { return; }
let el = document.getElementById('telao');
if (!el) { el = document.createElement('div'); el.id = 'telao'; document.body.appendChild(el); }
const esc = (s) => String(s || '').replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
const lados = (t.lados || []).map((l) =>
'<div class="lado"><span class="rot">' + esc(l.rotulo) + '</span><span class="sim">' + esc(l.simbolo) + '</span>' +
'<span class="nome">' + esc(l.nome) + '</span></div>').join('<div class="x">×</div>');
el.className = 'tom-' + (t.tom || 'neutro') + (t.piscar ? ' piscar' : '');
el.style.setProperty('--cor', 'rgb(' + CIANO.map(Math.round).join(',') + ')');
el.innerHTML = (lados ? '<div class="lados">' + lados + '</div>' : '') +
(t.titulo ? '<div class="titulo">' + esc(t.titulo) + '</div>' : '') + (t.sub ? '<div class="sub">' + esc(t.sub) + '</div>' : '');
el.hidden = false;
void el.offsetWidth; el.classList.add('entra'); // reinicia a animação a cada letreiro
if (r.dormindo) r.dormindo = false;
clearTimeout(telaoAte);
telaoAte = setTimeout(() => { el.hidden = true; }, (t.segundos || 2.5) * 1000);
}
let olhosAte = null;
function mostrarNosOlhos(texto) { // "✌️ 2.5": símbolo nos olhos; "- 0" apaga
const i = texto.lastIndexOf(' ');
@@ -868,6 +910,7 @@ function trata(ordem) {
if (/^aprendi /i.test(bruta)) { contarAprendi(bruta.slice(8).trim()); return; } // sem minúsculas: é uma frase
if (/^dizer /i.test(bruta)) { falarFrase(bruta.slice(6).trim()); return; }
if (/^olhos /i.test(bruta)) { mostrarNosOlhos(bruta.slice(6).trim()); return; } // jogos: ✊ ✋ ✌️ e números
if (/^telao /i.test(bruta)) { mostrarTelao(bruta.slice(6)); return; } // letreiro grande dos jogos
ordem = bruta.toLowerCase();
if (!ordem) return;
if(ordem==='recarregar') {