feat(vida): 20 atividades e necessidades no estilo The Sims
Quatorze cenas novas para o bonequinho: cochilar na caminha (cobertor respirando e Zzz), café (vapor e gole), cozinhar (vira a panqueca no fogão), regar a plantinha (cresce e dá flor), violão, dançar (globo espelhado), pular corda, meditar flutuando, telescópio (céu estrelado e estrela cadente), pipa com rabiola, bolhas de sabão, skate (com ollie), computador (código aparecendo) e pescar (o peixe pula preso na linha). Necessidades puxam comportamento, como no The Sims: - energia < 40%: às vezes cochila; < 20%: dorme de verdade com sono respeitado (som comum, movimento e PIR não acordam) até 95%; acordar de propósito (toque, painel) encerra o sono pesado; - alegria abaixo de 60%: o rosto vai ficando emburrado (zanga 0 a 1: testa franzida, boca para baixo, olhos bravos); < 30%: mal-humorado; - diversão < 45%: faz uma atividade de 2 a 5 min escolhida pelo pique (cansado prefere calmas, café dá energia); pode ser interrompido; - durante a atividade a diversão sobe e a energia cai conforme o tipo. Com o modo vida ligado o celular não adormece mais por 20 s de silêncio: quem decide o sono é a energia. A cor escolhida nunca muda com a zanga. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+419
-7
@@ -524,12 +524,26 @@
|
||||
// vale para qualquer rosto (SVG ou motor). O carinha.html pergunta para onde olhar
|
||||
// (olharAtividade) e chama palco() a cada quadro.
|
||||
const ATIVIDADES = {
|
||||
ler: { nome: 'Lendo', emoji: '📖', dur: [25, 45] },
|
||||
cantar: { nome: 'Cantando', emoji: '🎤', dur: [18, 30] },
|
||||
bola: { nome: 'Jogando bola', emoji: '⚽', dur: [20, 35] },
|
||||
videogame: { nome: 'Videogame', emoji: '🎮', dur: [25, 45] },
|
||||
pintar: { nome: 'Pintando', emoji: '🎨', dur: [25, 40] },
|
||||
malhar: { nome: 'Malhando', emoji: '🏋️', dur: [15, 25] },
|
||||
ler: { nome: 'Lendo', emoji: '📖', dur: [25, 45] },
|
||||
cantar: { nome: 'Cantando', emoji: '🎤', dur: [18, 30] },
|
||||
bola: { nome: 'Jogando bola', emoji: '⚽', dur: [20, 35] },
|
||||
videogame: { nome: 'Videogame', emoji: '🎮', dur: [25, 45] },
|
||||
pintar: { nome: 'Pintando', emoji: '🎨', dur: [25, 40] },
|
||||
malhar: { nome: 'Malhando', emoji: '🏋️', dur: [15, 25] },
|
||||
dormir: { nome: 'Cochilando', emoji: '🛏️', dur: [40, 80] },
|
||||
cafe: { nome: 'Tomando café', emoji: '☕', dur: [20, 35] },
|
||||
cozinhar: { nome: 'Cozinhando', emoji: '🍳', dur: [20, 35] },
|
||||
regar: { nome: 'Regando planta', emoji: '🪴', dur: [20, 30] },
|
||||
violao: { nome: 'Tocando violão', emoji: '🎸', dur: [20, 35] },
|
||||
dancar: { nome: 'Dançando', emoji: '💃', dur: [15, 30] },
|
||||
corda: { nome: 'Pulando corda', emoji: '🪢', dur: [15, 25] },
|
||||
meditar: { nome: 'Meditando', emoji: '🧘', dur: [25, 45] },
|
||||
telescopio: { nome: 'Olhando estrelas', emoji: '🔭', dur: [25, 40] },
|
||||
pipa: { nome: 'Soltando pipa', emoji: '🪁', dur: [20, 35] },
|
||||
bolhas: { nome: 'Bolhas de sabão', emoji: '🫧', dur: [18, 30] },
|
||||
skate: { nome: 'Andando de skate', emoji: '🛹', dur: [18, 30] },
|
||||
computador: { nome: 'No computador', emoji: '💻', dur: [25, 45] },
|
||||
pescar: { nome: 'Pescando', emoji: '🎣', dur: [30, 50] },
|
||||
};
|
||||
const ativ = { nome: null, t0: 0, dur: 0, fim: 0 };
|
||||
let palcoTela = null, pctx = null;
|
||||
@@ -582,6 +596,38 @@
|
||||
const p = pincel(k); info.x = 16 + lado * 5 + p.x * 4; info.y = -1 + p.y * 5;
|
||||
} else if (ativ.nome === 'malhar') {
|
||||
const f = forca(k); info.x = lado * 14; info.y = 6 - f * 12; info.ab = f > 0.85 ? 0.55 : 1;
|
||||
} else if (ativ.nome === 'dormir') {
|
||||
info.forma = 'dormir'; info.boca = 'sono'; info.x = lado * 6; info.y = 4;
|
||||
} else if (ativ.nome === 'cafe') {
|
||||
const f = (k % 5) / 5, gole = f > 0.55 && f < 0.92;
|
||||
info.x = lado * 12; info.y = gole ? -3 : 3; if (gole) info.forma = 'feliz';
|
||||
} else if (ativ.nome === 'cozinhar') {
|
||||
const cc = (k % 2.6) / 2.6, h = cc < 0.55 ? 4 * (cc / 0.55) * (1 - cc / 0.55) : 0;
|
||||
info.x = lado * 16; info.y = 4 - h * 14;
|
||||
} else if (ativ.nome === 'regar') {
|
||||
info.x = lado * 18; info.y = 8;
|
||||
} else if (ativ.nome === 'violao') {
|
||||
info.x = lado * 8 + 3 * Math.sin(k * 1.1); info.y = 6 + 1.5 * Math.sin(k * 2.2); info.boca = 'sorriso';
|
||||
if (Math.sin(k * 0.7) > 0.3) info.forma = 'feliz';
|
||||
} else if (ativ.nome === 'dancar') {
|
||||
info.x = lado * 10 + 6 * Math.sin(k * 5); info.y = -2 + 4 * Math.abs(Math.sin(k * 5)); info.forma = 'feliz'; info.boca = 'sorriso';
|
||||
} else if (ativ.nome === 'corda') {
|
||||
const th = k * Math.PI * 2 * 0.85; info.x = lado * 12; info.y = 4 - Math.pow(Math.max(0, Math.cos(th)), 0.6) * 10;
|
||||
} else if (ativ.nome === 'meditar') {
|
||||
info.forma = 'dormir'; info.boca = 'sorriso'; info.x = lado * 6; info.y = -2 + 2 * Math.sin(k * 1.2);
|
||||
} else if (ativ.nome === 'telescopio') {
|
||||
info.x = lado * 16; info.y = -11;
|
||||
} else if (ativ.nome === 'pipa') {
|
||||
info.x = lado * 14 + 4 * Math.sin(k * 0.8); info.y = -11 + 2 * Math.cos(k * 1.1);
|
||||
} else if (ativ.nome === 'bolhas') {
|
||||
info.x = lado * 14 + 4 * Math.sin(k * 0.9); info.y = -8 + 3 * Math.sin(k * 0.6);
|
||||
} else if (ativ.nome === 'skate') {
|
||||
info.x = lado * 12 + 7 * Math.sin(k * 0.9); info.y = 6;
|
||||
} else if (ativ.nome === 'computador') {
|
||||
info.x = lado * 18 + Math.sin(k * 20) * 0.6; info.y = 2 + ((k * 2.5) % 1) * 1.5;
|
||||
} else if (ativ.nome === 'pescar') {
|
||||
const cc = k % 8; info.x = lado * 18; info.y = 7;
|
||||
if (cc > 6.2 && cc < 7.6) { info.forma = 'feliz'; info.boca = 'sorriso'; info.y = 0; }
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@@ -618,8 +664,9 @@
|
||||
caixa(c, -s * 0.66, -s * 0.98, s * 1.32, s * 0.98, s * 0.28); c.fill(); c.stroke();
|
||||
const ox = (p.olhoX || 0) * s * 0.12, oy = (p.olhoY || 0) * s * 0.1;
|
||||
const pisca = (k % 3.7) < 0.12 ? 0.15 : 1;
|
||||
c.save(); c.fillStyle = css(cor);
|
||||
c.save(); c.fillStyle = css(cor); c.strokeStyle = css(cor);
|
||||
for (const lado of [-1, 1]) {
|
||||
if (p.fechados) { c.lineWidth = Math.max(1.5, s * 0.07); c.beginPath(); c.arc(lado * s * 0.27, -s * 0.52, s * 0.12, 0.2, Math.PI - 0.2); c.stroke(); continue; }
|
||||
c.beginPath(); c.ellipse(lado * s * 0.27 + ox, -s * 0.5 + oy, s * 0.11, s * 0.16 * pisca, 0, 0, Math.PI * 2); c.fill();
|
||||
}
|
||||
c.restore(); c.restore();
|
||||
@@ -644,6 +691,56 @@
|
||||
c.beginPath(); c.moveTo(x + s * 0.2, y - s * 0.7); c.quadraticCurveTo(x + s * 0.5, y - s * 0.55, x + s * 0.42, y - s * 0.3); c.stroke();
|
||||
}
|
||||
|
||||
const ROSA_ = [255, 112, 176], AMARELO_ = [255, 218, 96], VERDE_ = [120, 255, 160], LARANJA_ = [255, 150, 70];
|
||||
const SENTADO = { sentado: true, pE: [-1.35, 1.35], pD: [1.35, -1.35] };
|
||||
const rnd = (i, sem) => { const a = Math.sin(i * 12.9898 + sem * 78.233) * 43758.5453; return a - Math.floor(a); };
|
||||
function estrela(c, x, y, r) {
|
||||
c.beginPath();
|
||||
for (let i = 0; i < 10; i++) { const a = -Math.PI / 2 + i * Math.PI / 5, rr = i % 2 ? r * 0.45 : r; c.lineTo(x + Math.cos(a) * rr, y + Math.sin(a) * rr); }
|
||||
c.closePath(); c.fill();
|
||||
}
|
||||
function vapor(c, x, y, s, k, cor) { // três fios subindo e ondulando
|
||||
c.save(); c.strokeStyle = css(cor, 0.7); c.lineWidth = Math.max(1, s * 0.04);
|
||||
const alfa = c.globalAlpha;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const f = (k * 0.5 + i / 3) % 1, bx = x + (i - 1) * s * 0.1, by = y - f * s * 0.7;
|
||||
c.globalAlpha = alfa * (1 - f) * Math.min(1, f / 0.2);
|
||||
c.beginPath(); c.moveTo(bx, by); c.bezierCurveTo(bx + s * 0.1, by - s * 0.12, bx - s * 0.1, by - s * 0.24, bx, by - s * 0.36); c.stroke();
|
||||
}
|
||||
c.restore();
|
||||
}
|
||||
function xicara(c, x, y, s, cor) {
|
||||
c.fillStyle = claro(cor, 1); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
c.beginPath(); c.moveTo(x - s * 0.18, y - s * 0.2); c.lineTo(x + s * 0.18, y - s * 0.2); c.lineTo(x + s * 0.14, y + s * 0.08); c.lineTo(x - s * 0.14, y + s * 0.08); c.closePath(); c.fill(); c.stroke();
|
||||
c.beginPath(); c.arc(x + s * 0.2, y - s * 0.07, s * 0.08, -1.4, 1.4); c.stroke();
|
||||
}
|
||||
function chama(c, x, y, alt, s) {
|
||||
c.fillStyle = css(LARANJA_);
|
||||
c.beginPath(); c.moveTo(x, y - alt); c.quadraticCurveTo(x + s * 0.13, y - alt * 0.3, x, y); c.quadraticCurveTo(x - s * 0.13, y - alt * 0.3, x, y - alt); c.fill();
|
||||
c.fillStyle = css(AMARELO_);
|
||||
c.beginPath(); c.moveTo(x, y - alt * 0.6); c.quadraticCurveTo(x + s * 0.06, y - alt * 0.2, x, y); c.quadraticCurveTo(x - s * 0.06, y - alt * 0.2, x, y - alt * 0.6); c.fill();
|
||||
}
|
||||
function zzz(c, x, y, s, k, cor) { // Zs subindo
|
||||
c.save(); c.strokeStyle = css(cor); c.lineCap = 'round'; c.lineJoin = 'round';
|
||||
const alfa = c.globalAlpha;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const f = (k * 0.3 + i / 3) % 1, tam = s * (0.18 + 0.2 * f), zx = x - s * 0.5 * f, zy = y - s * 1.3 * f;
|
||||
c.globalAlpha = alfa * Math.min(1, f / 0.15) * (1 - f); c.lineWidth = Math.max(1.5, tam * 0.2);
|
||||
c.beginPath(); c.moveTo(zx - tam / 2, zy - tam / 2); c.lineTo(zx + tam / 2, zy - tam / 2); c.lineTo(zx - tam / 2, zy + tam / 2); c.lineTo(zx + tam / 2, zy + tam / 2); c.stroke();
|
||||
}
|
||||
c.restore();
|
||||
}
|
||||
function notas(c, x, y, s, k, cor, dir) {
|
||||
c.fillStyle = css(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
const alfa = c.globalAlpha;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const f = (k * 0.45 + i / 3) % 1;
|
||||
c.globalAlpha = alfa * Math.min(1, f / 0.15) * (1 - f);
|
||||
nota(c, x + dir * s * (0.3 + 0.5 * i) + Math.sin(f * 6 + i) * s * 0.2, y - f * s * 1.6, s * 0.7);
|
||||
}
|
||||
c.globalAlpha = alfa;
|
||||
}
|
||||
|
||||
const CENAS = {
|
||||
ler(c, x, y, s, k, cor) {
|
||||
const b = boneco(c, x, y, s, {
|
||||
@@ -786,6 +883,321 @@
|
||||
c.beginPath(); c.arc(b.cabeca.x + s * 0.72, b.cabeca.y + s * 0.3 + g * s * 0.5, s * 0.07, 0, Math.PI * 2); c.fill();
|
||||
}
|
||||
},
|
||||
dormir(c, x, y, s, k, cor) {
|
||||
// cama com cabeceira, travesseiro e cobertor que sobe e desce com a respiração
|
||||
const bx0 = x - s * 1.7, bx1 = x + s * 1.9, topo = y - s * 0.75;
|
||||
c.fillStyle = escuro(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.07);
|
||||
c.beginPath(); c.moveTo(bx0 + s * 0.12, y); c.lineTo(bx0 + s * 0.12, topo); c.moveTo(bx1 - s * 0.12, y); c.lineTo(bx1 - s * 0.12, topo); c.stroke();
|
||||
caixa(c, bx0 - s * 0.08, topo - s * 1.25, s * 0.24, s * 1.3, s * 0.1); c.fill(); c.stroke();
|
||||
caixa(c, bx0, topo - s * 0.3, bx1 - bx0, s * 0.32, s * 0.12); c.fill(); c.stroke();
|
||||
c.fillStyle = claro(cor, 0.9);
|
||||
c.beginPath(); c.ellipse(bx0 + s * 0.85, topo - s * 0.5, s * 0.6, s * 0.24, 0, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
// bonequinho deitado: desenhado em pé e girado 90°, pés à direita, cabeça no travesseiro
|
||||
const e = 0.72, linha = topo - s * 0.3 - s * e * 0.5;
|
||||
c.save(); c.translate(x + s * 1.5, linha); c.rotate(-Math.PI / 2);
|
||||
boneco(c, 0, 0, s * e, { fechados: true, bE: [-0.1, 0], bD: [0.1, 0] }, cor, k);
|
||||
c.restore();
|
||||
const resp = Math.sin(k * 1.4) * s * 0.04;
|
||||
c.fillStyle = css(cor.map((v) => v * 0.45)); c.strokeStyle = css(cor);
|
||||
caixa(c, x - s * 0.3, linha - s * 0.42 - resp, s * 2.15, topo - s * 0.3 - (linha - s * 0.42 - resp) + s * 0.06, s * 0.18); c.fill(); c.stroke();
|
||||
c.strokeStyle = css(cor, 0.5); c.beginPath(); c.moveTo(x - s * 0.1, linha - s * 0.2 - resp); c.lineTo(x + s * 1.7, linha - s * 0.2 - resp); c.stroke();
|
||||
zzz(c, x - s * 0.9, linha - s * 0.9, s, k, cor);
|
||||
},
|
||||
cafe(c, x, y, s, k, cor) {
|
||||
const f = (k % 5) / 5, gole = suave(faixaDe(f, 0.5, 0.64)) * (1 - suave(faixaDe(f, 0.84, 0.97)));
|
||||
const b = boneco(c, x, y, s, Object.assign({}, SENTADO, {
|
||||
olhoX: 0.2, olhoY: 0.3 - gole * 0.8, fechados: gole > 0.85, cabeca: -0.04,
|
||||
bE: [0.15, 0.95], bD: [-0.15 + gole * 1.75, -0.95 - gole * 3.05],
|
||||
}), cor, k);
|
||||
xicara(c, b.mD.x, b.mD.y, s, cor);
|
||||
if (gole < 0.3) vapor(c, b.mD.x, b.mD.y - s * 0.3, s, k, cor);
|
||||
},
|
||||
cozinhar(c, x, y, s, k, cor) {
|
||||
const ciclo = (k % 2.6) / 2.6, voo = ciclo < 0.55 ? ciclo / 0.55 : -1, h = voo >= 0 ? 4 * voo * (1 - voo) : 0;
|
||||
const fx = x + s * 1.0, fw = s * 1.7, fh = s * 1.2;
|
||||
c.fillStyle = escuro(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.07);
|
||||
caixa(c, fx, y - fh, fw, fh, s * 0.08); c.fill(); c.stroke();
|
||||
caixa(c, fx + fw * 0.15, y - fh * 0.72, fw * 0.7, fh * 0.5, s * 0.06); c.stroke();
|
||||
for (let i = 0; i < 3; i++) chama(c, fx + fw * (0.3 + 0.2 * i), y - fh, s * (0.22 + 0.07 * Math.sin(k * 17 + i * 2)), s);
|
||||
const b = boneco(c, x - s * 0.45, y, s, {
|
||||
olhoX: 1, olhoY: 0.3 - h, cabeca: -0.05, bE: [-0.4, 0.3],
|
||||
bD: [1.2 - (voo >= 0 && voo < 0.15 ? 0.25 : 0), 0.35],
|
||||
}, cor, k);
|
||||
const px = b.mD.x + s * 0.62, py = b.mD.y + s * 0.02;
|
||||
c.strokeStyle = css(cor); c.lineWidth = Math.max(2, s * 0.09);
|
||||
c.beginPath(); c.moveTo(b.mD.x, b.mD.y); c.lineTo(px - s * 0.42, py); c.stroke();
|
||||
c.fillStyle = escuro(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
c.beginPath(); c.ellipse(px, py, s * 0.46, s * 0.13, 0, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.save(); c.translate(px, py - s * 0.1 - h * s * 1.6); c.rotate(voo >= 0 ? voo * Math.PI * 2 : 0);
|
||||
c.fillStyle = css(AMARELO_); c.beginPath(); c.ellipse(0, 0, s * 0.32, s * 0.08, 0, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.restore();
|
||||
},
|
||||
regar(c, x, y, s, k, cor) {
|
||||
const cresce = Math.min(1, k / 18), px = x + s * 2.0;
|
||||
// vaso
|
||||
c.fillStyle = escuro(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.07);
|
||||
c.beginPath(); c.moveTo(px - s * 0.42, y - s * 0.6); c.lineTo(px + s * 0.42, y - s * 0.6); c.lineTo(px + s * 0.3, y); c.lineTo(px - s * 0.3, y); c.closePath(); c.fill(); c.stroke();
|
||||
// planta crescendo: caule, folhas e, no fim, a flor
|
||||
const alt = s * (0.3 + 1.25 * cresce), base = y - s * 0.6, balanca = Math.sin(k * 1.5) * s * 0.06;
|
||||
c.strokeStyle = css(VERDE_); c.lineWidth = Math.max(1.5, s * 0.07);
|
||||
c.beginPath(); c.moveTo(px, base); c.quadraticCurveTo(px + balanca, base - alt / 2, px + balanca * 2, base - alt); c.stroke();
|
||||
c.fillStyle = css(VERDE_);
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
if (cresce < i * 0.22) continue;
|
||||
const yy = base - alt * i * 0.25, lado = i % 2 ? 1 : -1;
|
||||
c.beginPath(); c.ellipse(px + balanca * i * 0.5 + lado * s * 0.18, yy, s * 0.2, s * 0.08, lado * -0.5, 0, Math.PI * 2); c.fill();
|
||||
}
|
||||
if (cresce > 0.75) {
|
||||
const f = (cresce - 0.75) / 0.25, fx = px + balanca * 2, fy = base - alt;
|
||||
c.fillStyle = css(ROSA_);
|
||||
for (let i = 0; i < 5; i++) { const a = i * 1.2566 + k * 0.3; c.beginPath(); c.arc(fx + Math.cos(a) * s * 0.16 * f, fy + Math.sin(a) * s * 0.16 * f, s * 0.11 * f, 0, Math.PI * 2); c.fill(); }
|
||||
c.fillStyle = css(AMARELO_); c.beginPath(); c.arc(fx, fy, s * 0.09 * f, 0, Math.PI * 2); c.fill();
|
||||
}
|
||||
const b = boneco(c, x - s * 0.7, y, s, { olhoX: 1, olhoY: 0.8, cabeca: -0.1, inclina: -0.06, bD: [1.35, 0.35], bE: [-0.3, 0.2] }, cor, k);
|
||||
// regador inclinado e as gotas caindo na planta
|
||||
const ang = 0.38, rx = b.mD.x + s * 0.05, ry = b.mD.y;
|
||||
c.save(); c.translate(rx, ry); c.rotate(ang);
|
||||
c.fillStyle = claro(cor, 0.95); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
caixa(c, 0, -s * 0.22, s * 0.6, s * 0.42, s * 0.08); c.fill(); c.stroke();
|
||||
c.beginPath(); c.moveTo(s * 0.55, -s * 0.05); c.lineTo(s * 1.0, -s * 0.32); c.stroke();
|
||||
c.restore();
|
||||
const ex = rx + Math.cos(ang) * s * 1.0 + Math.sin(ang) * s * 0.32, ey = ry + Math.sin(ang) * s * 1.0 - Math.cos(ang) * s * 0.32;
|
||||
c.fillStyle = claro(cor, 1);
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const f = (k * 1.6 + i / 6) % 1;
|
||||
c.beginPath(); c.ellipse(ex + (px - ex) * f * 0.8, ey + (base - alt - ey + s * 0.2) * f * f, s * 0.035, s * 0.07, 0, 0, Math.PI * 2); c.fill();
|
||||
}
|
||||
},
|
||||
violao(c, x, y, s, k, cor) {
|
||||
const toque = Math.sin(k * 11);
|
||||
const b = boneco(c, x, y, s, Object.assign({}, SENTADO, {
|
||||
olhoX: -0.4, olhoY: 0.7, cabeca: 0.08 * Math.sin(k * 1.1),
|
||||
bE: [-1.1, -0.9], bD: [-0.2 + toque * 0.12, -1.0],
|
||||
}), cor, k);
|
||||
// violão atravessado no colo: corpo perto da mão que toca, braço até a outra mão
|
||||
const gx = b.quadril.x + s * 0.3, gy = b.quadril.y - s * 0.35, nx = b.mE.x, ny = b.mE.y;
|
||||
const a = Math.atan2(ny - gy, nx - gx);
|
||||
c.save(); c.translate(gx, gy); c.rotate(a);
|
||||
const L = Math.hypot(nx - gx, ny - gy);
|
||||
c.fillStyle = escuro(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
caixa(c, s * 0.2, -s * 0.08, L - s * 0.1, s * 0.16, s * 0.04); c.fill(); c.stroke();
|
||||
caixa(c, L - s * 0.05, -s * 0.12, s * 0.3, s * 0.24, s * 0.06); c.fill(); c.stroke();
|
||||
c.fillStyle = claro(cor, 0.95);
|
||||
c.beginPath(); c.arc(-s * 0.28, 0, s * 0.42, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.beginPath(); c.arc(s * 0.22, 0, s * 0.32, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.fillStyle = escuro(cor); c.beginPath(); c.arc(s * 0.05, 0, s * 0.11, 0, Math.PI * 2); c.fill();
|
||||
c.strokeStyle = css(cor, 0.7); c.lineWidth = Math.max(1, s * 0.015);
|
||||
for (const d of [-0.04, 0, 0.04]) { c.beginPath(); c.moveTo(-s * 0.5, d * s); c.lineTo(L, d * s); c.stroke(); }
|
||||
c.restore();
|
||||
c.fillStyle = css(cor); c.beginPath(); c.arc(b.mD.x, b.mD.y, s * 0.13, 0, Math.PI * 2); c.fill();
|
||||
notas(c, b.cabeca.x - s * 1.6, b.cabeca.y + s * 0.2, s, k, cor, -1);
|
||||
},
|
||||
dancar(c, x, y, s, k, cor) {
|
||||
const u = (Math.sin(k * 5) + 1) / 2, mexe = Math.sin(k * 5) * 0.18;
|
||||
// globo espelhado e luzinhas no chão
|
||||
const gx = x, gy = y - s * 4.5;
|
||||
c.strokeStyle = css(cor, 0.6); c.lineWidth = Math.max(1, s * 0.03);
|
||||
c.beginPath(); c.moveTo(gx, gy - s * 0.9); c.lineTo(gx, gy - s * 0.35); c.stroke();
|
||||
c.fillStyle = claro(cor, 0.9); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
c.beginPath(); c.arc(gx, gy, s * 0.35, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.strokeStyle = css(cor, 0.5);
|
||||
for (let i = -1; i <= 1; i++) { c.beginPath(); c.ellipse(gx, gy, s * 0.35 * Math.abs(Math.cos(k + i)), s * 0.35, 0, 0, Math.PI * 2); c.stroke(); }
|
||||
const cores = [ROSA_, AMARELO_, VERDE_, cor];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const lx = x + Math.sin(k * 1.3 + i * 1.7) * s * 2.4, alfa = c.globalAlpha;
|
||||
c.globalAlpha = alfa * 0.35; c.fillStyle = css(cores[i % 4]);
|
||||
c.beginPath(); c.ellipse(lx, y, s * 0.35, s * 0.07, 0, 0, Math.PI * 2); c.fill();
|
||||
c.globalAlpha = alfa;
|
||||
}
|
||||
boneco(c, x, y, s, {
|
||||
dx: mexe, inclina: mexe * 0.5, cabeca: -mexe * 0.6, olhoY: -0.2, olhoX: mexe * 3,
|
||||
bE: [-0.7 - 2.1 * u, 0.5], bD: [0.7 + 2.1 * (1 - u), -0.5],
|
||||
pE: [-0.1 - u * 0.25, u * 0.4], pD: [0.1 + (1 - u) * 0.25, -(1 - u) * 0.4],
|
||||
}, cor, k);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const f = (k * 0.7 + i / 4) % 1, alfa = c.globalAlpha;
|
||||
c.globalAlpha = alfa * Math.sin(f * Math.PI); c.fillStyle = css(cores[i]);
|
||||
estrela(c, x + Math.cos(i * 1.9) * s * 1.6, y - s * (2 + i * 0.5) - f * s * 0.4, s * 0.12); c.globalAlpha = alfa;
|
||||
}
|
||||
},
|
||||
corda(c, x, y, s, k, cor) {
|
||||
const th = k * Math.PI * 2 * 0.85, pulo = Math.pow(Math.max(0, Math.cos(th)), 0.6), sobe = pulo * s * 0.45;
|
||||
c.save(); c.globalAlpha *= 0.3 * (1 - pulo * 0.5); c.fillStyle = css(cor);
|
||||
c.beginPath(); c.ellipse(x, y, s * 0.6 * (1 - pulo * 0.3), s * 0.08, 0, 0, Math.PI * 2); c.fill(); c.restore();
|
||||
const b = boneco(c, x, y - sobe, s, {
|
||||
olhoY: -0.2, bE: [-0.75 + 0.15 * Math.sin(th), 0.6], bD: [0.75 - 0.15 * Math.sin(th), -0.6],
|
||||
pE: [-0.06, pulo * 0.3], pD: [0.06, -pulo * 0.3],
|
||||
}, cor, k);
|
||||
// a corda passa por baixo dos pés quando ele está no alto do pulo
|
||||
const my = (b.mE.y + b.mD.y) / 2, embaixo = 2 * (y + s * 0.05) - my, emcima = 2 * (b.cabeca.y - s * 1.0) - my;
|
||||
const cy = (embaixo + emcima) / 2 + Math.cos(th) * (embaixo - emcima) / 2;
|
||||
c.strokeStyle = claro(cor, 1); c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
c.beginPath(); c.moveTo(b.mE.x, b.mE.y); c.quadraticCurveTo(x, cy, b.mD.x, b.mD.y); c.stroke();
|
||||
},
|
||||
meditar(c, x, y, s, k, cor) {
|
||||
const flutua = (Math.sin(k * 1.2) + 1) / 2 * s * 0.35, cy = y - s * 1.7 - flutua;
|
||||
const alfa = c.globalAlpha;
|
||||
for (let i = 0; i < 3; i++) { // aura
|
||||
const f = (k * 0.35 + i / 3) % 1;
|
||||
c.globalAlpha = alfa * 0.35 * (1 - f); c.strokeStyle = css(cor); c.lineWidth = Math.max(1, s * 0.04);
|
||||
c.beginPath(); c.ellipse(x, cy, s * (1.2 + f * 1.3), s * (1.4 + f * 1.3), 0, 0, Math.PI * 2); c.stroke();
|
||||
}
|
||||
c.globalAlpha = alfa * 0.3; c.fillStyle = css(cor);
|
||||
c.beginPath(); c.ellipse(x, y, s * 0.8 * (1 - flutua / s), s * 0.09, 0, 0, Math.PI * 2); c.fill();
|
||||
c.globalAlpha = alfa;
|
||||
boneco(c, x, y - s * 0.3 - flutua, s, {
|
||||
sentado: true, fechados: true, pE: [-1.45, 2.75], pD: [1.45, -2.75], bE: [-0.75, 0.25], bD: [0.75, -0.25],
|
||||
}, cor, k);
|
||||
},
|
||||
telescopio(c, x, y, s, k, cor) {
|
||||
// céu estrelado com uma estrela cadente de vez em quando
|
||||
const alfa = c.globalAlpha;
|
||||
for (let i = 0; i < 16; i++) {
|
||||
c.globalAlpha = alfa * (0.35 + 0.65 * Math.abs(Math.sin(k * (1 + rnd(i, 3)) + i)));
|
||||
c.fillStyle = i % 5 ? claro(cor, 1) : css(AMARELO_);
|
||||
estrela(c, x + s * (0.2 + rnd(i, 1) * 3.4), y - s * (3.4 + rnd(i, 2) * 1.5), s * (0.05 + 0.05 * rnd(i, 4)));
|
||||
}
|
||||
const f = (k % 6) / 1.1;
|
||||
if (f < 1) {
|
||||
const sx = x + s * (3.6 - f * 2.2), sy = y - s * (4.9 - f * 0.8);
|
||||
const g = c.createLinearGradient(sx, sy, sx + s * 0.9, sy - s * 0.33);
|
||||
g.addColorStop(0, claro(cor, 1)); g.addColorStop(1, css(cor, 0));
|
||||
c.globalAlpha = alfa * (1 - f); c.strokeStyle = g; c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
c.beginPath(); c.moveTo(sx, sy); c.lineTo(sx + s * 0.9, sy - s * 0.33); c.stroke();
|
||||
}
|
||||
c.globalAlpha = alfa;
|
||||
const b = boneco(c, x - s * 0.7, y, s, { olhoX: 1, olhoY: -1, cabeca: 0.15, bD: [2.1, 0.5], bE: [-0.3, 0.2] }, cor, k);
|
||||
const ox = b.cabeca.x + s * 0.78, oy = b.cabeca.y + s * 0.3, tx = ox + s * 1.6, ty = oy - s * 0.95;
|
||||
const mx = (ox + tx) / 2, my = (oy + ty) / 2;
|
||||
c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
c.beginPath(); c.moveTo(mx, my); c.lineTo(mx - s * 0.5, y); c.moveTo(mx, my); c.lineTo(mx + s * 0.5, y); c.moveTo(mx, my); c.lineTo(mx, y); c.stroke();
|
||||
c.fillStyle = escuro(cor);
|
||||
c.beginPath(); capsula(c, ox, oy, tx, ty, s * 0.15); c.fill(); c.stroke();
|
||||
c.fillStyle = claro(cor, 1); c.beginPath(); c.arc(tx, ty, s * 0.17, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
},
|
||||
pipa(c, x, y, s, k, cor) {
|
||||
const kx = x + s * (2.3 + 0.45 * Math.sin(k * 0.8)), ky = y - s * (4.2 + 0.35 * Math.cos(k * 1.1)), ang = 0.25 * Math.sin(k * 1.6);
|
||||
const b = boneco(c, x - s * 0.9, y, s, { olhoX: 1, olhoY: -1, cabeca: 0.12, bD: [2.55, 0.35], bE: [-0.4, 0.3] }, cor, k);
|
||||
c.strokeStyle = css(cor, 0.8); c.lineWidth = Math.max(1, s * 0.025);
|
||||
c.beginPath(); c.moveTo(b.mD.x, b.mD.y);
|
||||
c.quadraticCurveTo((b.mD.x + kx) / 2 + s * 0.4, (b.mD.y + ky) / 2 + s * 0.7, kx, ky + s * 0.45); c.stroke();
|
||||
// rabiola com lacinhos
|
||||
c.save(); c.translate(kx, ky); c.rotate(ang);
|
||||
c.strokeStyle = css(cor, 0.8); c.beginPath(); c.moveTo(0, s * 0.5);
|
||||
for (let i = 1; i <= 6; i++) c.lineTo(Math.sin(k * 3 + i) * s * 0.18, s * (0.5 + i * 0.22));
|
||||
c.stroke();
|
||||
for (let i = 2; i <= 6; i += 2) {
|
||||
const lx = Math.sin(k * 3 + i) * s * 0.18, ly = s * (0.5 + i * 0.22);
|
||||
c.fillStyle = css(i % 4 ? ROSA_ : AMARELO_);
|
||||
c.beginPath(); c.moveTo(lx, ly); c.lineTo(lx - s * 0.1, ly - s * 0.06); c.lineTo(lx - s * 0.1, ly + s * 0.06); c.closePath(); c.fill();
|
||||
c.beginPath(); c.moveTo(lx, ly); c.lineTo(lx + s * 0.1, ly - s * 0.06); c.lineTo(lx + s * 0.1, ly + s * 0.06); c.closePath(); c.fill();
|
||||
}
|
||||
const pts = [[0, -0.5], [0.38, 0], [0, 0.5], [-0.38, 0]];
|
||||
const cores = [ROSA_, AMARELO_, cor, VERDE_];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const [ax, ay] = pts[i], [bx, by] = pts[(i + 1) % 4];
|
||||
c.fillStyle = css(cores[i]); c.beginPath(); c.moveTo(0, 0); c.lineTo(ax * s, ay * s); c.lineTo(bx * s, by * s); c.closePath(); c.fill();
|
||||
}
|
||||
c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.04);
|
||||
c.beginPath(); pts.forEach(([px, py], i) => (i ? c.lineTo(px * s, py * s) : c.moveTo(px * s, py * s))); c.closePath(); c.stroke();
|
||||
c.restore();
|
||||
},
|
||||
bolhas(c, x, y, s, k, cor) {
|
||||
const b = boneco(c, x - s * 0.6, y, s, { olhoX: 0.8, olhoY: -0.6, bD: [1.6, -4.0], bE: [-0.3, 0.2] }, cor, k);
|
||||
const wx = b.mD.x + s * 0.35, wy = b.mD.y - s * 0.25;
|
||||
c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
c.beginPath(); c.moveTo(b.mD.x, b.mD.y); c.lineTo(wx - s * 0.1, wy + s * 0.08); c.stroke();
|
||||
c.beginPath(); c.arc(wx, wy, s * 0.12, 0, Math.PI * 2); c.stroke();
|
||||
const alfa = c.globalAlpha;
|
||||
for (let i = 0; i < 9; i++) {
|
||||
const f = (k * 0.22 + i / 9) % 1, r = s * (0.12 + 0.18 * rnd(i, 4));
|
||||
const bx = wx + f * s * (1.6 + 1.4 * rnd(i, 5)) + Math.sin(k * 2 + i) * s * 0.15, by = wy - f * s * (1.6 + rnd(i, 6));
|
||||
c.globalAlpha = alfa * Math.min(1, f / 0.08) * (f > 0.9 ? (1 - f) / 0.1 : 1);
|
||||
c.fillStyle = css(cor, 0.1); c.strokeStyle = css(cor, 0.9); c.lineWidth = Math.max(1, s * 0.03);
|
||||
c.beginPath(); c.arc(bx, by, r, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.strokeStyle = claro(cor, 1); c.beginPath(); c.arc(bx, by, r * 0.65, -2.4, -1.6); c.stroke();
|
||||
}
|
||||
c.globalAlpha = alfa;
|
||||
},
|
||||
skate(c, x, y, s, k, cor) {
|
||||
const pos = Math.sin(k * 0.9) * 1.3, vel = Math.cos(k * 0.9), ciclo = k % 4;
|
||||
const ollie = ciclo < 0.7 ? Math.sin(ciclo / 0.7 * Math.PI) : 0, sobe = ollie * s * 0.6;
|
||||
const bx = x + pos * s, pe = y - s * 0.3 - sobe;
|
||||
c.save(); c.globalAlpha *= 0.3; c.fillStyle = css(cor);
|
||||
c.beginPath(); c.ellipse(bx, y, s * 0.9, s * 0.08, 0, 0, Math.PI * 2); c.fill(); c.restore();
|
||||
c.strokeStyle = css(cor, 0.4); c.lineWidth = Math.max(1, s * 0.03); // risquinhos de velocidade
|
||||
for (let i = 0; i < 3; i++) { const lx = bx - Math.sign(vel) * s * (1.1 + i * 0.25); c.beginPath(); c.moveTo(lx, pe - s * (0.6 + i * 0.4)); c.lineTo(lx - Math.sign(vel) * s * 0.5 * Math.abs(vel), pe - s * (0.6 + i * 0.4)); c.stroke(); }
|
||||
boneco(c, bx, pe, s, {
|
||||
olhoX: vel * 0.8, inclina: -vel * 0.1, bE: [-1.35, 0.25 + 0.2 * Math.sin(k * 3)], bD: [1.35, -0.25 - 0.2 * Math.sin(k * 3)],
|
||||
pE: [-0.25, 0.35], pD: [0.25, -0.35],
|
||||
}, cor, k);
|
||||
c.save(); c.translate(bx, pe); c.rotate(-ollie * 0.25);
|
||||
c.fillStyle = claro(cor, 1); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.05);
|
||||
caixa(c, -s * 0.85, 0, s * 1.7, s * 0.12, s * 0.06); c.fill(); c.stroke();
|
||||
c.fillStyle = escuro(cor);
|
||||
for (const lado of [-1, 1]) { c.beginPath(); c.arc(lado * s * 0.55, s * 0.2, s * 0.1, 0, Math.PI * 2); c.fill(); c.stroke(); }
|
||||
c.restore();
|
||||
},
|
||||
computador(c, x, y, s, k, cor) {
|
||||
const mx0 = x + s * 0.3, mx1 = x + s * 2.7, mt = y - s * 1.45;
|
||||
c.fillStyle = escuro(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
c.beginPath(); c.moveTo(mx0 + s * 0.15, mt); c.lineTo(mx0 + s * 0.15, y); c.moveTo(mx1 - s * 0.15, mt); c.lineTo(mx1 - s * 0.15, y); c.stroke();
|
||||
caixa(c, mx0, mt, mx1 - mx0, s * 0.12, s * 0.04); c.fill(); c.stroke();
|
||||
const nx = mx0 + s * 0.6, nw = s * 1.4, nh = s * 0.95, ny = mt - s * 0.08 - nh;
|
||||
caixa(c, nx - s * 0.12, mt - s * 0.08, nw + s * 0.24, s * 0.08, s * 0.03); c.fill(); c.stroke();
|
||||
caixa(c, nx, ny, nw, nh, s * 0.06); c.fill(); c.stroke();
|
||||
c.save(); caixa(c, nx + s * 0.06, ny + s * 0.06, nw - s * 0.12, nh - s * 0.12, s * 0.03); c.clip();
|
||||
const linhas = Math.floor(k * 2.5), visiveis = 6, ini = Math.max(0, linhas - visiveis);
|
||||
const tintas = [cor, VERDE_, AMARELO_, ROSA_];
|
||||
for (let i = ini; i < linhas; i++) {
|
||||
const yy = ny + s * 0.14 + (i - ini) * s * 0.13, recuo = (rnd(i, 7) < 0.4 ? 1 : 0) * s * 0.15;
|
||||
c.fillStyle = css(tintas[i % 4], 0.85); c.fillRect(nx + s * 0.12 + recuo, yy, (nw - s * 0.4) * (0.3 + 0.6 * rnd(i, 8)), s * 0.05);
|
||||
}
|
||||
if (Math.floor(k * 2) % 2) { c.fillStyle = claro(cor, 1); c.fillRect(nx + s * 0.12, ny + s * 0.14 + Math.min(linhas, visiveis) * s * 0.13, s * 0.04, s * 0.08); }
|
||||
c.restore();
|
||||
const tecla = Math.sin(k * 20) * 0.05;
|
||||
boneco(c, x - s * 0.55, y, s, Object.assign({}, SENTADO, {
|
||||
olhoX: 1, olhoY: 0.2, cabeca: -0.05, bE: [0.9 + tecla, 0.9], bD: [1.35 - tecla, 0.45],
|
||||
}), cor, k);
|
||||
},
|
||||
pescar(c, x, y, s, k, cor) {
|
||||
const ciclo = k % 8, fisgou = ciclo > 6.2 && ciclo < 7.6, puxa = fisgou ? suave((ciclo - 6.2) / 0.5) : 0;
|
||||
// água: ondinhas à direita do píer
|
||||
c.strokeStyle = css(cor, 0.55); c.lineWidth = Math.max(1, s * 0.035);
|
||||
for (let fila = 0; fila < 3; fila++) {
|
||||
c.beginPath();
|
||||
for (let i = 0; i <= 24; i++) {
|
||||
const wx = x + s * (0.5 + i * 0.14), wy = y - s * 0.05 + fila * s * 0.16 + Math.sin(k * 2 + i * 0.8 + fila) * s * 0.04;
|
||||
if (i) c.lineTo(wx, wy); else c.moveTo(wx, wy);
|
||||
}
|
||||
c.stroke();
|
||||
}
|
||||
c.fillStyle = escuro(cor); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
caixa(c, x - s * 2.0, y - s * 0.22, s * 2.5, s * 0.22, s * 0.05); c.fill(); c.stroke();
|
||||
const b = boneco(c, x - s * 0.8, y - s * 0.22, s, Object.assign({}, SENTADO, {
|
||||
olhoX: 1, olhoY: 0.6 - puxa * 1.4, bD: [2.0 - puxa * 0.4, -0.9], bE: [0.5, 1.2],
|
||||
}), cor, k);
|
||||
const vx = b.mD.x + s * (1.4 - puxa * 0.3), vy = b.mD.y - s * (1.6 + puxa * 0.3);
|
||||
c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.06);
|
||||
c.beginPath(); c.moveTo(b.mE.x, b.mE.y); c.lineTo(b.mD.x, b.mD.y); c.lineTo(vx, vy); c.stroke();
|
||||
const belisca = ciclo > 5.5 && ciclo < 6.2 ? Math.sin(k * 30) * s * 0.06 : 0;
|
||||
let lx = x + s * 2.6, ly = y - s * 0.05 + Math.sin(k * 2) * s * 0.04 + belisca;
|
||||
if (fisgou) { // o peixe pula preso na linha
|
||||
lx = x + s * (2.6 - puxa * 0.6); ly = y - s * (0.2 + Math.sin(puxa * Math.PI) * 1.3 + puxa * 0.3);
|
||||
c.save(); c.translate(lx, ly + s * 0.25); c.rotate(Math.sin(k * 14) * 0.4);
|
||||
c.fillStyle = claro(cor, 1); c.strokeStyle = css(cor); c.lineWidth = Math.max(1.5, s * 0.04);
|
||||
c.beginPath(); c.ellipse(0, 0, s * 0.12, s * 0.28, 0, 0, Math.PI * 2); c.fill(); c.stroke();
|
||||
c.beginPath(); c.moveTo(0, s * 0.25); c.lineTo(-s * 0.14, s * 0.45); c.lineTo(s * 0.14, s * 0.45); c.closePath(); c.fill(); c.stroke();
|
||||
c.fillStyle = escuro(cor); c.beginPath(); c.arc(s * 0.04, -s * 0.12, s * 0.03, 0, Math.PI * 2); c.fill();
|
||||
c.restore();
|
||||
}
|
||||
c.strokeStyle = css(cor, 0.85); c.lineWidth = Math.max(1, s * 0.02);
|
||||
c.beginPath(); c.moveTo(vx, vy); c.quadraticCurveTo((vx + lx) / 2, Math.max(vy, ly) - s * (fisgou ? 0.1 : 0.4), lx, ly); c.stroke();
|
||||
if (!fisgou) {
|
||||
c.fillStyle = css(ROSA_); c.beginPath(); c.arc(lx, ly, s * 0.09, Math.PI, 0); c.fill();
|
||||
c.fillStyle = claro(cor, 1); c.beginPath(); c.arc(lx, ly, s * 0.09, 0, Math.PI); c.fill();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/** Desenha a cena da atividade (camada atrás dos olhos). o = { t, cor }. */
|
||||
|
||||
Reference in New Issue
Block a user