feat(vida): jeito de gente e console com o porquê de cada decisão
Console (console.py + tela Console no painel):
- cada parte registra o que fez e o motivo: modo vida (com necessidades
e humor), sentidos, celular (ordens e o que o rosto fez), IA local,
aprendizado e ideias do dia; últimas 400 linhas, ao vivo pelo SSE;
- tela com o "agora" (humor, necessidades, o que está fazendo, próxima
decisão, última decisão, jeito), filtros, detalhes, pausa e busca.
Jeito de gente:
- saudade: a festa cresce com o tempo fora (aceno, "Oi! Você voltou!",
"Senti sua falta!"); bom dia lembrando de ontem;
- pedidos com motivo ("Brinca comigo?", "Me faz um carinho?", mostrar a
figurinha nova), no máximo 1 a cada 20 min;
- larga atividade no meio quando fica chato; personalidade (curiosidade,
brincadeira, timidez) que pesa nos gestos e amadurece com o convívio;
- no rosto: acordar grogue, sonhar com algo do dia (balão no motor),
hesitar antes de gestos, errar na atividade e ficar sem graça, olhar
nos olhos e desviar, sorrir de volta (FaceLandmarker no rastreador do
painel) e reagir à luz (sensor de luz no app 0.10.0);
- ordem "dizer" com legenda e voz de robô.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -595,6 +595,10 @@ function atividade(nome, segundos, receita) {
|
||||
if (!window.MotorRosto || !window.MotorRosto.atividade) return;
|
||||
if (nome === "parar" || !nome) { window.MotorRosto.atividade(null); return; }
|
||||
if (r.dormindo) { r.dormindo = false; animar("surpreso"); }
|
||||
lembrarDoDia(nome, receita);
|
||||
const agora = performance.now() / 1000, dur = Number(segundos) || 0;
|
||||
HUMANO.oops = dur > 20 && Math.random() < 0.35 ? { ini: agora + dur * (0.3 + 0.4 * Math.random()) } : null;
|
||||
if (HUMANO.oops) HUMANO.oops.fim = HUMANO.oops.ini + 2.2;
|
||||
if (window.MotorRosto.atividade(nome, segundos, receita) && nome === "cantar") tocarSom("musica");
|
||||
}
|
||||
|
||||
@@ -618,6 +622,91 @@ function tocarExpressaoCriada(texto) {
|
||||
if (rc.maos && rc.maos !== "nenhuma" && window.MotorRosto && window.MotorRosto.maos) window.MotorRosto.maos(rc.maos);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- jeito de gente
|
||||
Acordar grogue, sonhar dormindo, hesitar antes de agir, errar na atividade e ficar sem graça,
|
||||
olhar nos olhos e desviar, sorrir de volta para quem sorri e reagir à luz do ambiente. */
|
||||
const HUMANO = {
|
||||
dormiaAntes: false, dormiuEm: 0, grogueIni: 0, grogueAte: 0, bocejou: true,
|
||||
sonhoProx: 0, sonhoAte: 0, sonhos: ['estrela', 'bola', 'nota', 'coracao', 'livro', 'flor'],
|
||||
oops: null, // {ini, fim}: um tropeço no meio da atividade
|
||||
evita: { ate: 0, prox: 0, dx: 0, dy: 0 }, // desvio do olhar durante o contato visual
|
||||
sorriso: { desde: 0, ultimo: 0 },
|
||||
luz: { lux: null, media: null, sonolentoAte: 0, ofuscadoAte: 0, mudou: 0 },
|
||||
};
|
||||
const OBJETO_DA_ATIVIDADE = { bola: 'bola', ler: 'livro', cafe: 'xicara', cantar: 'nota', violao: 'nota', pintar: 'pincel',
|
||||
regar: 'flor', telescopio: 'estrela', malhar: 'halter', bolhas: 'bolha', videogame: 'controle', dancar: 'estrela',
|
||||
meditar: 'coracao', cozinhar: 'chama', computador: 'controle', pescar: 'bolha', pipa: 'estrela', skate: 'estrela',
|
||||
corda: 'estrela' };
|
||||
function lembrarDoDia(nome, receita) { // o que ele fez hoje vira assunto dos sonhos
|
||||
const obj = receita && Array.isArray(receita.objetos) && receita.objetos[0] ? receita.objetos[0].tipo : OBJETO_DA_ATIVIDADE[nome];
|
||||
if (obj) HUMANO.sonhos = [obj].concat(HUMANO.sonhos.filter((o) => o !== obj)).slice(0, 8);
|
||||
}
|
||||
function falarFrase(texto, segundos) { // "dizer ..." do modo vida: legenda + voz de robô
|
||||
if (window.carinhaSemServidor || !texto || r.dormindo) return;
|
||||
if (window.MotorRosto) window.MotorRosto.faixa('legenda', { texto, segundos: segundos || Math.max(4, Math.min(14, texto.length / 9)) });
|
||||
falar(texto);
|
||||
}
|
||||
function hesitar(fn, gesto) { // às vezes pensa um instante antes de agir
|
||||
if (['surpreso', 'piscar', 'piscar2'].indexOf(gesto) >= 0 || Math.random() > 0.3) return fn();
|
||||
setTimeout(fn, 150 + Math.random() * 450);
|
||||
}
|
||||
function humanoTick(t) {
|
||||
// acordou depois de dormir um tempo: fica grogue (olhos pesados, lento) e boceja
|
||||
if (r.dormindo && !HUMANO.dormiaAntes) HUMANO.dormiuEm = t;
|
||||
if (!r.dormindo && HUMANO.dormiaAntes && t - HUMANO.dormiuEm > 180) {
|
||||
HUMANO.grogueIni = t; HUMANO.grogueAte = t + 30 + Math.random() * 20; HUMANO.bocejou = false;
|
||||
}
|
||||
HUMANO.dormiaAntes = r.dormindo;
|
||||
if (!HUMANO.bocejou && t - HUMANO.grogueIni > 1.6 && !r.dormindo && !r.kf) { HUMANO.bocejou = true; animar('bocejar'); }
|
||||
// dormindo (com a tela acesa): de vez em quando sonha com algo do dia
|
||||
if (r.dormindo && !SONO.apagada && !window.carinhaSemServidor) {
|
||||
if (!HUMANO.sonhoProx) HUMANO.sonhoProx = t + 40 + Math.random() * 60;
|
||||
if (t >= HUMANO.sonhoProx) {
|
||||
const dur = 6 + Math.random() * 4;
|
||||
HUMANO.sonhoAte = t + dur; HUMANO.sonhoProx = t + dur + 45 + Math.random() * 90;
|
||||
const obj = HUMANO.sonhos[Math.floor(Math.random() * Math.min(4, HUMANO.sonhos.length))];
|
||||
if (window.MotorRosto && window.MotorRosto.sonho) window.MotorRosto.sonho(obj, dur);
|
||||
}
|
||||
} else HUMANO.sonhoProx = 0;
|
||||
}
|
||||
function grogue(t) { // 1 = acabou de acordar, 0 = esperto
|
||||
return t < HUMANO.grogueAte ? Math.min(1, (HUMANO.grogueAte - t) / (HUMANO.grogueAte - HUMANO.grogueIni)) : 0;
|
||||
}
|
||||
function piscarHumano(t) { animar(grogue(t) > 0.2 || t < HUMANO.luz.sonolentoAte ? 'piscar2' : 'piscar'); }
|
||||
function desvioDoOlhar(t) { // olha nos olhos 2 a 5 s, desvia um instante e volta
|
||||
const ev = HUMANO.evita;
|
||||
if (t >= ev.prox) {
|
||||
const dura = Math.random() < 0.7 ? 0.6 + Math.random() * 0.7 : 0;
|
||||
ev.ate = t + dura; ev.prox = ev.ate + 2 + Math.random() * 3;
|
||||
ev.dx = (Math.random() < 0.5 ? -1 : 1) * (8 + Math.random() * 8); ev.dy = 3 + Math.random() * 5;
|
||||
}
|
||||
return t < ev.ate ? ev : null;
|
||||
}
|
||||
function contagio(t, agora) { // sorriu para ele (rastreamento do painel): sorri de volta
|
||||
const S = HUMANO.sorriso;
|
||||
if (t.tipo !== 'rosto' || typeof t.sorriso !== 'number' || r.dormindo || ATIV) { S.desde = 0; return; }
|
||||
if (t.sorriso > 0.55) { if (!S.desde) S.desde = agora; } else S.desde = 0;
|
||||
if (S.desde && agora - S.desde > 0.6 && agora - S.ultimo > 20 && !r.kf) {
|
||||
S.ultimo = agora; S.desde = 0;
|
||||
animar(t.sorriso > 0.8 ? 'rir' : 'feliz');
|
||||
}
|
||||
}
|
||||
function luzAmbiente(lux) { // sensor de luz do celular (lux)
|
||||
lux = Number(lux);
|
||||
if (!Number.isFinite(lux) || lux < 0) return;
|
||||
const L = HUMANO.luz, t = performance.now() / 1000, antes = L.media;
|
||||
L.lux = lux;
|
||||
L.media = antes === null ? lux : antes + (lux - antes) * 0.15;
|
||||
if (antes === null || t - L.mudou < 8) return;
|
||||
if (lux < 3 && antes > 25) { // apagaram a luz: fica com sono
|
||||
L.mudou = t; L.sonolentoAte = t + 60; L.media = lux;
|
||||
if (!r.dormindo && !r.kf) animar('bocejar');
|
||||
} else if (lux > 80 && antes < 8) { // acenderam a luz de repente: aperta os olhos
|
||||
L.mudou = t; L.ofuscadoAte = t + 1.8; L.media = lux;
|
||||
if (r.dormindo && !sonoRespeitado() && PERFIL !== PERFIS.calmo) r.dormindo = false;
|
||||
}
|
||||
}
|
||||
|
||||
function repouso(t) { // pose calma: olha devagar, "respira"
|
||||
if (r.dormindo) {
|
||||
r.forma = "dormir"; r.forma2 = "dormir"; r.boca = "sono";
|
||||
@@ -626,6 +715,7 @@ function repouso(t) { // pose calma: olha devagar, "respira
|
||||
r.esc = 1.0 + 0.03 * Math.sin(t * 1.4);
|
||||
r.dx = 0;
|
||||
r.dy = 3.0 * Math.sin(t * 1.4);
|
||||
if (t < HUMANO.sonhoAte) { r.dx = 3.2 * Math.sin(t * 6.5); r.boca = 'sorriso'; } // sonhando
|
||||
return;
|
||||
}
|
||||
r.forma = "aberto"; r.forma2 = null;
|
||||
@@ -638,7 +728,7 @@ function repouso(t) { // pose calma: olha devagar, "respira
|
||||
const longe = Math.random() < 0.25;
|
||||
OLHAR.ax = (Math.random() * 2 - 1) * (longe ? 18 : 7);
|
||||
OLHAR.ay = (Math.random() * 2 - 1) * (longe ? 8 : 3);
|
||||
OLHAR.prox = t + 1.2 + Math.random() * 3;
|
||||
OLHAR.prox = t + (1.2 + Math.random() * 3) * (1 + 2 * grogue(t)); // grogue: olhar lento
|
||||
}
|
||||
OLHAR.x += (OLHAR.ax - OLHAR.x) * 0.3;
|
||||
OLHAR.y += (OLHAR.ay - OLHAR.y) * 0.3;
|
||||
@@ -651,9 +741,23 @@ function repouso(t) { // pose calma: olha devagar, "respira
|
||||
if (ATIV.forma) r.forma = ATIV.forma;
|
||||
if (ATIV.boca) r.boca = ATIV.boca;
|
||||
if (ATIV.ab) r.ab = ATIV.ab;
|
||||
const o = HUMANO.oops; // errou: susto e depois fica sem graça
|
||||
if (o && t >= o.ini && t < o.fim) {
|
||||
if (t - o.ini < 0.5) { r.forma = 'surpreso'; r.boca = 'aberta'; r.ab = 1.1; }
|
||||
else { r.forma = 'feliz'; r.boca = 'sorriso'; r.cor = lerpCor(r.cor, ROSA, 0.5); r.dy += 6; }
|
||||
}
|
||||
} else if (ZANGA >= 0.7) { // alegria caindo: emburrado (a cor escolhida fica)
|
||||
r.forma = "bravo";
|
||||
}
|
||||
const g = grogue(t);
|
||||
if (g > 0) { // acabou de acordar: olhos pesados, cor apagada, olhar baixo
|
||||
r.ab = Math.min(r.ab, 1 - 0.5 * g);
|
||||
r.cor = lerpCor(r.cor, corEscura(0.7), 0.35 * g);
|
||||
if (g > 0.5 && !ATIV) r.boca = 'sono';
|
||||
r.dx *= 1 - 0.5 * g; r.dy = r.dy * (1 - 0.5 * g) + 3 * g;
|
||||
}
|
||||
if (t < HUMANO.luz.sonolentoAte) r.ab = Math.min(r.ab, 0.78);
|
||||
if (t < HUMANO.luz.ofuscadoAte) { r.ab = Math.min(r.ab, 0.3); r.forma = 'feliz'; }
|
||||
}
|
||||
|
||||
function desenhar() {
|
||||
@@ -667,7 +771,9 @@ function desenhar() {
|
||||
const seguindo = TRACK.alvo && tAgora - TRACK.visto < .35 && !r.dormindo;
|
||||
const mirando = (MOV.ativo && (tAgora - MOV.ultimoMov) < 1.2) || !!ATIV;
|
||||
const mover = seguindo ? .55 : (r.kf !== null || r.dormindo) ? 0.25 : (mirando ? 0.25 : 0.0);
|
||||
const dx = seguindo ? MOV.olharX : r.dx, dy = seguindo ? MOV.olharY : r.dy;
|
||||
const ev = seguindo && TRACK.alvo.tipo === 'rosto' ? desvioDoOlhar(tAgora) : null;
|
||||
const dx = seguindo ? (ev ? MOV.olharX * 0.3 + ev.dx : MOV.olharX) : r.dx;
|
||||
const dy = seguindo ? (ev ? MOV.olharY * 0.3 + ev.dy : MOV.olharY) : r.dy;
|
||||
const olhos = [[E1, ab, r.forma, +1], [E2, ab2, f2, -1]];
|
||||
const comMotor = ROSTO.motor && window.MotorRosto;
|
||||
OLHOS.forEach((g) => { g.style.display = comMotor ? "none" : ""; });
|
||||
@@ -751,6 +857,7 @@ function contarAprendi(texto) {
|
||||
function trata(ordem) {
|
||||
const bruta = (ordem || "").trim();
|
||||
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; }
|
||||
ordem = bruta.toLowerCase();
|
||||
if (!ordem) return;
|
||||
if(ordem==='recarregar') {
|
||||
@@ -797,14 +904,16 @@ function trata(ordem) {
|
||||
if (aviso) { aviso.classList.remove("cutucado"); void aviso.offsetWidth; aviso.classList.add("cutucado"); }
|
||||
return;
|
||||
}
|
||||
r.dormindo = false; animar("surpreso");
|
||||
const longo = r.dormindo && performance.now() / 1000 - HUMANO.dormiuEm > 180;
|
||||
r.dormindo = false;
|
||||
if (!longo) animar("surpreso"); // sono longo: acorda devagar, grogue (humanoTick)
|
||||
} else if (ordem === "aleatorio") {
|
||||
animar(LIVRES[Math.floor(Math.random() * LIVRES.length)]);
|
||||
} else if (SEQS[ordem]) {
|
||||
if (window.MotorRosto) {
|
||||
if (ordem === "musica") window.MotorRosto.faixa("equalizador", { segundos: 2.3 });
|
||||
}
|
||||
animar(ordem);
|
||||
hesitar(() => animar(ordem), ordem);
|
||||
} else if (ordem === "calma") {
|
||||
// E-Stop do painel: interrompe a animação e volta ao rosto neutro.
|
||||
r.fila = []; r.kf = null; r.boca = "neutra"; r.dormindo = false;
|
||||
@@ -1121,6 +1230,7 @@ function rastrear(t) {
|
||||
(r.dormindo && !configRecebida?.cameraSono)) return;
|
||||
const agora=performance.now()/1000;
|
||||
TRACK.alvo=t; TRACK.visto=agora; MOV.ativo=true;MOV.visto=agora;MOV.cx=t.x;MOV.cy=t.y;
|
||||
contagio(t, agora);
|
||||
MOV.olharX+=((t.x-.5)*2*MOV.olharMax-MOV.olharX)*.65;
|
||||
MOV.olharY+=((t.y-.5)*2*MOV.olharMax*.6-MOV.olharY)*.65;
|
||||
if(t.atividade===true) {
|
||||
@@ -1306,6 +1416,7 @@ const AUDIO = {
|
||||
};
|
||||
if (window.eilik) window.eilik.audio = AUDIO; // so depois do const: sem TDZ
|
||||
if (window.eilik) window.eilik.camera = CAM; // so depois do const: sem TDZ
|
||||
if (window.eilik) window.eilik.luz = luzAmbiente; // sensor de luz do celular (o app chama)
|
||||
|
||||
let configRecebida = null;
|
||||
function configurarApp(c) {
|
||||
@@ -1353,6 +1464,7 @@ async function sincronizarApp() {
|
||||
bateria: window.carinhaBateria,
|
||||
aparelho: window.carinhaAparelho,
|
||||
video: window.carinhaVideo,
|
||||
luz: HUMANO.luz.lux,
|
||||
}) });
|
||||
}
|
||||
} catch (e) { /* servidor ausente: animação continua */ }
|
||||
@@ -1448,6 +1560,7 @@ function laco(t) {
|
||||
|
||||
checarSilencio(t / 1000);
|
||||
atualizarFeicao(t / 1000);
|
||||
humanoTick(t / 1000);
|
||||
|
||||
ATIV = window.MotorRosto && window.MotorRosto.olharAtividade && !r.dormindo ? window.MotorRosto.olharAtividade(t / 1000) : null;
|
||||
if (r.dormindo && window.MotorRosto && window.MotorRosto.atividadeAtual && window.MotorRosto.atividadeAtual()) atividade(null);
|
||||
@@ -1461,7 +1574,7 @@ function laco(t) {
|
||||
if (r.dormindo) {
|
||||
proxPiscar = proxGesto = t + 3600000;
|
||||
} else if (t >= proxPiscar) {
|
||||
animar("piscar");
|
||||
piscarHumano(t / 1000);
|
||||
proxPiscar = t + 1600 + Math.random() * 3500;
|
||||
} else if (AUTO && t >= proxGesto && !ATIV) {
|
||||
animar(gestoSolto());
|
||||
|
||||
Reference in New Issue
Block a user