fix(rosto): parado mais humano; tonto com espirais e bocejo com a mão

Parado ele vivia escondendo o rosto: a IA local (Qwen 0.6B) reagia a cada
som ou "sozinho" a cada 20-60 s e repetia 'esconder' apesar do "varie".

- IA local: cada evento só oferece gestos que combinam (barulho: susto ou
  olhar; movimento: olhar e acenar), os dois últimos ficam fora do enum,
  no máximo a cada 45 s, não interrompe atividade e, com o modo vida
  ligado, não reage a "sozinho".
- Sem ninguém olhando (sem movimento/PIR há 3 min) nada de gesto "para
  alguém" (acenar, tímido, esconder...): ele vive a vida dele e começa
  atividades com mais frequência.
- Gestos soltos no celular: quase sempre sutis (olhar de lado, piscar
  devagar), gesto grande raro e nunca repetido em seguida.
- Olhar em repouso com fixações e sacadas em vez do vaivém contínuo.
- Tonto: espirais girando no lugar dos olhos (todos os rostos, inclusive
  Matriz) e boca de "ó". Bocejo: mão na frente da boca com tapinhas e
  Zzz subindo. As mãos agora aparecem também quando o gesto vem sozinho.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
julio
2026-10-06 21:38:32 -03:00
co-authored by Claude Opus 5.5
parent e73897d9c1
commit ff06f02705
10 changed files with 235 additions and 33 deletions
+25 -1
View File
@@ -107,7 +107,20 @@
}
// Olho plano (vivo, bloco, matriz): corpo + pálpebras recortadas (transparentes).
// Tonto: uma espiral girando no lugar de cada olho (cada um gira para um lado, como no emoji 😵‍💫).
function espiral(c, o, cx, cy, R, lado) {
const voltas = 2.75, giro = (o.t || 0) * 7 * lado;
c.save(); c.strokeStyle = css(o.cor); c.lineWidth = Math.max(2, R * 0.17); c.lineCap = 'round';
c.beginPath();
for (let th = 0; th <= voltas * Math.PI * 2; th += 0.22) {
const rr = R * th / (voltas * Math.PI * 2), a = th * lado + giro;
if (th) c.lineTo(cx + Math.cos(a) * rr, cy + Math.sin(a) * rr); else c.moveTo(cx, cy);
}
c.stroke(); c.restore();
}
function olhoPlano(c, o, p, est, e) {
if (o.espiral) { espiral(c, o, p.cx, p.cy, Math.min(est.w, est.h) * p.ry * 0.5, p.lado || 1); return; }
const respira = 1 + 0.012 * Math.sin(o.t * 1.4);
const temConteudo = !!(o.icone || o.desenho);
if (temConteudo) e = { top: 0, slant: 0, bot: 0, coracao: 0 };
@@ -162,6 +175,7 @@
// Olho lente: esclera, íris, pupila que dilata e pálpebras de metal.
function olhoLente(c, o, p, est, e) {
if (o.espiral) { espiral(c, o, p.cx, p.cy, est.w * p.ry * 0.5, p.lado || 1); return; }
const R = est.w * p.ry * 0.5 * Math.max(1, p.ab) * (1 + 0.01 * Math.sin(o.t * 1.4));
const fecha = Math.min(1, Math.max(o.icone || o.desenho ? 0 : e.top, 1 - Math.min(1, p.ab)));
c.save();
@@ -289,7 +303,7 @@
// ------------------------------------------------------------------ mãos
// Mãozinhas na cor dos olhos que sobem da borda da tela. Duração casada com as sequências
// de rosto de mesmo nome no carinha.html.
const DUR_MAOS = { esconder: 2.6, espiar: 3.2, acenar: 2.4, pensar: 2.8 };
const DUR_MAOS = { esconder: 2.6, espiar: 3.2, acenar: 2.4, pensar: 2.8, bocejar: 2.1 };
const maos = { modo: null, t0: null };
const suave = (x) => { x = Math.max(0, Math.min(1, x)); return x * x * (3 - 2 * x); };
const faixaDe = (k, a, b) => suave((k - a) / (b - a)); // 0→1 entre a e b
@@ -387,6 +401,16 @@
const y = fora + (y0 - fora) * v;
const balanco = k > 0.22 && k < 0.82 ? Math.sin((k - 0.22) * Math.PI * 7) * 0.32 : 0;
mao(c, o, x, y, s * 0.95, -0.18 + balanco, -1, 0, 1, false, true);
} else if (maos.modo === 'bocejar') {
// mão na frente da boca, com tapinhas, e os Zzz subindo do olho
const ida = voltinha(k / 0.25), volta = faixaDe(k, 0.78, 1);
const v = Math.max(0, Math.min(1.08, ida)) * (1 - volta);
const bx = (e.cx + d.cx) / 2 + e.ry * 0.12, by = e.cy + e.ry * 1.2;
const tapa = k > 0.3 && k < 0.75 ? Math.abs(Math.sin((k - 0.3) * Math.PI * 6)) * s * 0.12 : 0;
mao(c, o, bx + e.ry * 0.5 * (1 - Math.min(1, v)), fora + (by + s * 0.55 - fora) * v + tapa, s * 0.9, -0.22, -1, 0.15, 0, false, true, true);
c.save(); c.globalAlpha *= Math.min(1, v);
desenharZzz(c, Object.assign({}, o, { dormindo: true }), olhos, 0.8);
c.restore();
} else if (maos.modo === 'pensar') {
const ida = voltinha(k / 0.22), volta = faixaDe(k, 0.82, 1);
const v = Math.max(0, Math.min(1.08, ida)) * (1 - volta);