From 75a107bf8de5da7ca2bd8994095c51867ed8f393 Mon Sep 17 00:00:00 2001 From: cmena92 Date: Fri, 30 Oct 2026 08:33:43 -0600 Subject: [PATCH] Companion: it reacts to a touch, with four new faces (#33) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Art first: guino (a wink), sonrojado (pink cheeks, happy eyes), mareado (spiral eyes, a wobbly mouth) and nam (chewing, a «w» mouth), in the one source www/games/companero/expresiones.js. design/companero/expresiones.mjs now checks 72 pairs, 0 differences outside the eyes and the mouth. In the app: - Touching the companion (or Enter) shows its wish and gets a short reaction that is never the same twice running: a laugh, a wink, a blush or a spin. Five touches within 3 s leave it dizzy for 2 s, a funny face and never a punishment. A touch adds bond under the same 30 s rule as the free actions and never touches coins. - Eating chews (nam) and a bubble bath blushes; a fulfilled wish is still joy. - Under reduced motion the spin is just the happy face. One fix found while checking: the wish bubble covered a hat. The card now keeps room above the drawing, and a test runs every wish in the five languages at 390 px to make sure none covers the companion. --- COMPANERO.md | 3 +- design/companero/expresiones.json | 4 +- design/companero/expresiones.mjs | 8 +- design/companero/expresiones/bear-guino.svg | 10 + design/companero/expresiones/bear-mareado.svg | 10 + design/companero/expresiones/bear-nam.svg | 10 + .../companero/expresiones/bear-sonrojado.svg | 10 + .../companero/expresiones/capybara-guino.svg | 17 + .../expresiones/capybara-mareado.svg | 17 + design/companero/expresiones/capybara-nam.svg | 17 + .../expresiones/capybara-sonrojado.svg | 17 + design/companero/expresiones/cat-guino.svg | 10 + design/companero/expresiones/cat-mareado.svg | 10 + design/companero/expresiones/cat-nam.svg | 10 + .../companero/expresiones/cat-sonrojado.svg | 10 + design/companero/expresiones/conejo-guino.svg | 23 + .../companero/expresiones/conejo-mareado.svg | 23 + design/companero/expresiones/conejo-nam.svg | 23 + .../expresiones/conejo-sonrojado.svg | 23 + design/companero/expresiones/fox-guino.svg | 10 + design/companero/expresiones/fox-mareado.svg | 10 + design/companero/expresiones/fox-nam.svg | 10 + .../companero/expresiones/fox-sonrojado.svg | 10 + design/companero/expresiones/nutria-guino.svg | 18 + .../companero/expresiones/nutria-mareado.svg | 18 + design/companero/expresiones/nutria-nam.svg | 18 + .../expresiones/nutria-sonrojado.svg | 18 + design/companero/expresiones/owl-guino.svg | 10 + design/companero/expresiones/owl-mareado.svg | 10 + design/companero/expresiones/owl-nam.svg | 10 + .../companero/expresiones/owl-sonrojado.svg | 10 + design/companero/expresiones/panda-guino.svg | 21 + .../companero/expresiones/panda-mareado.svg | 21 + design/companero/expresiones/panda-nam.svg | 21 + .../companero/expresiones/panda-sonrojado.svg | 21 + design/companero/vista-previa.html | 1056 ++++++++++++++++- tools/test-companero.js | 75 +- www/games/companero/cuidado.js | 4 +- www/games/companero/expresiones.js | 29 +- www/games/companero/index.html | 50 +- 40 files changed, 1629 insertions(+), 76 deletions(-) create mode 100644 design/companero/expresiones/bear-guino.svg create mode 100644 design/companero/expresiones/bear-mareado.svg create mode 100644 design/companero/expresiones/bear-nam.svg create mode 100644 design/companero/expresiones/bear-sonrojado.svg create mode 100644 design/companero/expresiones/capybara-guino.svg create mode 100644 design/companero/expresiones/capybara-mareado.svg create mode 100644 design/companero/expresiones/capybara-nam.svg create mode 100644 design/companero/expresiones/capybara-sonrojado.svg create mode 100644 design/companero/expresiones/cat-guino.svg create mode 100644 design/companero/expresiones/cat-mareado.svg create mode 100644 design/companero/expresiones/cat-nam.svg create mode 100644 design/companero/expresiones/cat-sonrojado.svg create mode 100644 design/companero/expresiones/conejo-guino.svg create mode 100644 design/companero/expresiones/conejo-mareado.svg create mode 100644 design/companero/expresiones/conejo-nam.svg create mode 100644 design/companero/expresiones/conejo-sonrojado.svg create mode 100644 design/companero/expresiones/fox-guino.svg create mode 100644 design/companero/expresiones/fox-mareado.svg create mode 100644 design/companero/expresiones/fox-nam.svg create mode 100644 design/companero/expresiones/fox-sonrojado.svg create mode 100644 design/companero/expresiones/nutria-guino.svg create mode 100644 design/companero/expresiones/nutria-mareado.svg create mode 100644 design/companero/expresiones/nutria-nam.svg create mode 100644 design/companero/expresiones/nutria-sonrojado.svg create mode 100644 design/companero/expresiones/owl-guino.svg create mode 100644 design/companero/expresiones/owl-mareado.svg create mode 100644 design/companero/expresiones/owl-nam.svg create mode 100644 design/companero/expresiones/owl-sonrojado.svg create mode 100644 design/companero/expresiones/panda-guino.svg create mode 100644 design/companero/expresiones/panda-mareado.svg create mode 100644 design/companero/expresiones/panda-nam.svg create mode 100644 design/companero/expresiones/panda-sonrojado.svg diff --git a/COMPANERO.md b/COMPANERO.md index c87284f9..dd1e2bcb 100644 --- a/COMPANERO.md +++ b/COMPANERO.md @@ -74,7 +74,8 @@ Los consumibles no suman a ese total: cuestan de 5 a 15 y son opcionales. - **Vínculo:** +1 por cada cuidado y +3 extra al cumplir el deseo de la visita. Una misma acción gratuita suma como mucho una vez cada 30 segundos, pero se puede hacer siempre. - **Cinco niveles**, a los 0, 10, 25, 50 y 100 puntos. El 100 es el máximo y se muestra desde el principio. - **Un deseo por visita**, distinto del de la visita anterior: probar una comida, un baño de burbujas, algo nuevo para ponerse, jugar o caricias. Primero se elige el tipo, todos igual de probables, y después cuál comida: así las siete comidas no acaparan los deseos. -- **Escenas:** comer lleva la comida a la boca, el jabón da un baño de burbujas y jugar trae una pelota que rebota. Con movimiento reducido se ve la misma escena, quieta. +- **Tocarlo:** responde con risa, guiño, sonrojo o un giro, nunca dos veces seguidas igual, y vuelve a mostrar su deseo. Cinco toques en 3 segundos lo marean un momento: es un gesto gracioso, no un castigo. Suma vínculo con la regla de 30 segundos y no da monedas. +- **Escenas:** comer lleva la comida a la boca, el jabón da un baño de burbujas y jugar trae una pelota que rebota. Al comer mastica («ñam») y en el baño se sonroja. Con movimiento reducido se ve la misma escena, quieta. ## Datos diff --git a/design/companero/expresiones.json b/design/companero/expresiones.json index d32c7ce0..26d0ce6c 100644 --- a/design/companero/expresiones.json +++ b/design/companero/expresiones.json @@ -1,6 +1,6 @@ { - "nota": "Expresiones del compañero (#9, #18). Solo cambian ojos y boca; las coordenadas copian las de cada cara. tipo: punto (ojo oscuro), pupila (búho: pupila sobre el disco claro) o panda (ojo claro con pupila sobre la mancha). Sin boca: el búho (pico) y el conejo (dientes).", - "expresiones": ["contento", "feliz", "antojado", "dormido", "sorprendido"], + "nota": "Expresiones del compañero (#9, #18, #33). Solo cambian ojos y boca; las coordenadas copian las de cada cara. tipo: punto (ojo oscuro), pupila (búho: pupila sobre el disco claro) o panda (ojo claro con pupila sobre la mancha). Sin boca: el búho (pico) y el conejo (dientes).", + "expresiones": ["contento", "feliz", "antojado", "dormido", "sorprendido", "guino", "sonrojado", "mareado", "nam"], "animales": { "fox": { "tipo": "punto", "color": "#3A2620", "ojos": [[46, 58, 5], [74, 58, 5]], "boca": { "d": "M54 82c2 3 10 3 12 0", "cx": 60, "cy": 83.5, "ancho": 12 } }, "owl": { "tipo": "pupila", "color": "#2C2620", "ojos": [[45, 52, 6.5], [75, 52, 6.5]] }, diff --git a/design/companero/expresiones.mjs b/design/companero/expresiones.mjs index 66da17ae..da37db9a 100644 --- a/design/companero/expresiones.mjs +++ b/design/companero/expresiones.mjs @@ -1,7 +1,7 @@ -// The companion's five expressions (#9, #18): contento (the face as drawn), feliz, antojado, -// dormido and sorprendido. Only the eyes and the mouth change: the face's own eyes and mouth are +// The companion's expressions (#9, #18, #33): contento (the face as drawn), feliz, antojado, +// dormido, sorprendido, guino (a wink), sonrojado (blushing), mareado (dizzy) and nam (chewing). Only the eyes and the mouth change: the face's own eyes and mouth are // taken out and new ones, marked data-expr, are drawn at the same coordinates (expresiones.json). -// Run on its own, it writes the 40 animal and expression pairs to expresiones/ and checks them: +// Run on its own, it writes the 72 animal and expression pairs to expresiones/ and checks them: // valid SVG in headless Chrome, and nothing but the eyes, the mouth and the sleeping «z» differs // from the contento face. // Usage: node design/companero/expresiones.mjs @@ -68,5 +68,5 @@ document.getElementById("r").textContent=JSON.stringify(t.map((s,i)=>new DOMPars fallas.forEach((f) => console.error("✗ " + f)); const fuera = fallas.filter((f) => f.includes("fuera de ojos")).length; console.log(`${archivos.length} expresiones, ${fuera} diferencias fuera de rasgos`); - if (archivos.length !== 40 || fallas.length) process.exit(1); + if (archivos.length !== Object.keys(datos.animales).length * expresiones.length || fallas.length) process.exit(1); } diff --git a/design/companero/expresiones/bear-guino.svg b/design/companero/expresiones/bear-guino.svg new file mode 100644 index 00000000..5f52aeec --- /dev/null +++ b/design/companero/expresiones/bear-guino.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/bear-mareado.svg b/design/companero/expresiones/bear-mareado.svg new file mode 100644 index 00000000..a35016e2 --- /dev/null +++ b/design/companero/expresiones/bear-mareado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/bear-nam.svg b/design/companero/expresiones/bear-nam.svg new file mode 100644 index 00000000..7d5c76f4 --- /dev/null +++ b/design/companero/expresiones/bear-nam.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/bear-sonrojado.svg b/design/companero/expresiones/bear-sonrojado.svg new file mode 100644 index 00000000..cd32928f --- /dev/null +++ b/design/companero/expresiones/bear-sonrojado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/capybara-guino.svg b/design/companero/expresiones/capybara-guino.svg new file mode 100644 index 00000000..38f95c15 --- /dev/null +++ b/design/companero/expresiones/capybara-guino.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/capybara-mareado.svg b/design/companero/expresiones/capybara-mareado.svg new file mode 100644 index 00000000..1f5f3a26 --- /dev/null +++ b/design/companero/expresiones/capybara-mareado.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/capybara-nam.svg b/design/companero/expresiones/capybara-nam.svg new file mode 100644 index 00000000..08e4fccf --- /dev/null +++ b/design/companero/expresiones/capybara-nam.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/capybara-sonrojado.svg b/design/companero/expresiones/capybara-sonrojado.svg new file mode 100644 index 00000000..64e9a1da --- /dev/null +++ b/design/companero/expresiones/capybara-sonrojado.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/cat-guino.svg b/design/companero/expresiones/cat-guino.svg new file mode 100644 index 00000000..94a64a47 --- /dev/null +++ b/design/companero/expresiones/cat-guino.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/cat-mareado.svg b/design/companero/expresiones/cat-mareado.svg new file mode 100644 index 00000000..52ede540 --- /dev/null +++ b/design/companero/expresiones/cat-mareado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/cat-nam.svg b/design/companero/expresiones/cat-nam.svg new file mode 100644 index 00000000..d9fed4c7 --- /dev/null +++ b/design/companero/expresiones/cat-nam.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/cat-sonrojado.svg b/design/companero/expresiones/cat-sonrojado.svg new file mode 100644 index 00000000..a1d2e663 --- /dev/null +++ b/design/companero/expresiones/cat-sonrojado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/conejo-guino.svg b/design/companero/expresiones/conejo-guino.svg new file mode 100644 index 00000000..82012712 --- /dev/null +++ b/design/companero/expresiones/conejo-guino.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/conejo-mareado.svg b/design/companero/expresiones/conejo-mareado.svg new file mode 100644 index 00000000..0ce17f64 --- /dev/null +++ b/design/companero/expresiones/conejo-mareado.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/conejo-nam.svg b/design/companero/expresiones/conejo-nam.svg new file mode 100644 index 00000000..407413cc --- /dev/null +++ b/design/companero/expresiones/conejo-nam.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/conejo-sonrojado.svg b/design/companero/expresiones/conejo-sonrojado.svg new file mode 100644 index 00000000..8110f8bd --- /dev/null +++ b/design/companero/expresiones/conejo-sonrojado.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/fox-guino.svg b/design/companero/expresiones/fox-guino.svg new file mode 100644 index 00000000..0f1d43c2 --- /dev/null +++ b/design/companero/expresiones/fox-guino.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/fox-mareado.svg b/design/companero/expresiones/fox-mareado.svg new file mode 100644 index 00000000..90924d2f --- /dev/null +++ b/design/companero/expresiones/fox-mareado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/fox-nam.svg b/design/companero/expresiones/fox-nam.svg new file mode 100644 index 00000000..8c32cb6a --- /dev/null +++ b/design/companero/expresiones/fox-nam.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/fox-sonrojado.svg b/design/companero/expresiones/fox-sonrojado.svg new file mode 100644 index 00000000..dfa077be --- /dev/null +++ b/design/companero/expresiones/fox-sonrojado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/nutria-guino.svg b/design/companero/expresiones/nutria-guino.svg new file mode 100644 index 00000000..116fda7b --- /dev/null +++ b/design/companero/expresiones/nutria-guino.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/nutria-mareado.svg b/design/companero/expresiones/nutria-mareado.svg new file mode 100644 index 00000000..6e218b91 --- /dev/null +++ b/design/companero/expresiones/nutria-mareado.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/nutria-nam.svg b/design/companero/expresiones/nutria-nam.svg new file mode 100644 index 00000000..707bea7d --- /dev/null +++ b/design/companero/expresiones/nutria-nam.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/nutria-sonrojado.svg b/design/companero/expresiones/nutria-sonrojado.svg new file mode 100644 index 00000000..a3e0e5f0 --- /dev/null +++ b/design/companero/expresiones/nutria-sonrojado.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/owl-guino.svg b/design/companero/expresiones/owl-guino.svg new file mode 100644 index 00000000..02bc236f --- /dev/null +++ b/design/companero/expresiones/owl-guino.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/owl-mareado.svg b/design/companero/expresiones/owl-mareado.svg new file mode 100644 index 00000000..bb87f4a0 --- /dev/null +++ b/design/companero/expresiones/owl-mareado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/owl-nam.svg b/design/companero/expresiones/owl-nam.svg new file mode 100644 index 00000000..1cc96531 --- /dev/null +++ b/design/companero/expresiones/owl-nam.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/owl-sonrojado.svg b/design/companero/expresiones/owl-sonrojado.svg new file mode 100644 index 00000000..49695eea --- /dev/null +++ b/design/companero/expresiones/owl-sonrojado.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/design/companero/expresiones/panda-guino.svg b/design/companero/expresiones/panda-guino.svg new file mode 100644 index 00000000..21408ec5 --- /dev/null +++ b/design/companero/expresiones/panda-guino.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/panda-mareado.svg b/design/companero/expresiones/panda-mareado.svg new file mode 100644 index 00000000..ebdb5366 --- /dev/null +++ b/design/companero/expresiones/panda-mareado.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/panda-nam.svg b/design/companero/expresiones/panda-nam.svg new file mode 100644 index 00000000..4c66e8c9 --- /dev/null +++ b/design/companero/expresiones/panda-nam.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/expresiones/panda-sonrojado.svg b/design/companero/expresiones/panda-sonrojado.svg new file mode 100644 index 00000000..6af8272a --- /dev/null +++ b/design/companero/expresiones/panda-sonrojado.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/design/companero/vista-previa.html b/design/companero/vista-previa.html index e0fc2a6f..d4920f50 100644 --- a/design/companero/vista-previa.html +++ b/design/companero/vista-previa.html @@ -1381,6 +1381,482 @@ +
nutria

guino

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + + +
nutria

sonrojado

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + + +
nutria

mareado

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + + +
nutria

nam

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + +
nutria

56 px

contento

@@ -1389,7 +1865,364 @@ - + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + + +
nutria

feliz

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + + +
nutria

antojado

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + + + +
nutria

dormido

+ + + + + + + +
fox
@@ -1399,7 +2232,7 @@ - +
owl
@@ -1409,7 +2242,7 @@ - +
cat
@@ -1419,7 +2252,7 @@ - +
bear
@@ -1433,10 +2266,10 @@ - + - - + +
capybara
@@ -1455,11 +2288,11 @@ - + - +
conejo
@@ -1476,11 +2309,11 @@ - - + + - - + +
panda
@@ -1494,13 +2327,13 @@ - + - + - + -
nutria

feliz

+
nutria

sorprendido

@@ -1508,7 +2341,7 @@ - +
fox
@@ -1518,7 +2351,7 @@ - +
owl
@@ -1528,7 +2361,7 @@ - +
cat
@@ -1538,7 +2371,7 @@ - +
bear
@@ -1555,7 +2388,7 @@ - +
capybara
@@ -1578,7 +2411,7 @@ - +
conejo
@@ -1599,7 +2432,7 @@ - +
panda
@@ -1617,9 +2450,9 @@ - + -
nutria

antojado

+
nutria

guino

@@ -1627,7 +2460,7 @@ - +
fox
@@ -1637,7 +2470,7 @@ - +
owl
@@ -1647,7 +2480,7 @@ - +
cat
@@ -1657,7 +2490,7 @@ - +
bear
@@ -1674,7 +2507,7 @@ - +
capybara
@@ -1697,7 +2530,7 @@ - +
conejo
@@ -1718,7 +2551,7 @@ - +
panda
@@ -1736,9 +2569,9 @@ - + -
nutria

dormido

+
nutria

sonrojado

@@ -1746,7 +2579,7 @@ - +
fox
@@ -1756,7 +2589,7 @@ - +
owl
@@ -1766,7 +2599,7 @@ - +
cat
@@ -1776,7 +2609,7 @@ - +
bear
@@ -1793,7 +2626,7 @@ - +
capybara
@@ -1816,7 +2649,7 @@ - +
conejo
@@ -1837,7 +2670,7 @@ - +
panda
@@ -1855,9 +2688,9 @@ - + -
nutria

sorprendido

+
nutria

mareado

@@ -1865,7 +2698,7 @@ - +
fox
@@ -1875,7 +2708,7 @@ - +
owl
@@ -1885,7 +2718,7 @@ - +
cat
@@ -1895,7 +2728,7 @@ - +
bear
@@ -1912,7 +2745,7 @@ - +
capybara
@@ -1935,7 +2768,7 @@ - +
conejo
@@ -1956,7 +2789,7 @@ - +
panda
@@ -1974,7 +2807,126 @@ - + + +
nutria

nam

+ + + + + + + + + +
fox
+ + + + + + + + + +
owl
+ + + + + + + + + +
cat
+ + + + + + + + + +
bear
+ + + + + + + + + + + + + + + + +
capybara
+ + + + + + + + + + + + + + + + + + + + + + +
conejo
+ + + + + + + + + + + + + + + + + + + + +
panda
+ + + + + + + + + + + + + + + +
nutria

Comidas y escenas de cuidado (#32)

La pose quieta de cada escena: la que se ve con movimiento reducido y donde termina la animación.

Comidas · 48 px

diff --git a/tools/test-companero.js b/tools/test-companero.js index 3a5f3c1a..1c104f4c 100644 --- a/tools/test-companero.js +++ b/tools/test-companero.js @@ -410,7 +410,10 @@ function testExpresionesFuente() { total++; if (expresar(A.cuerpos[id], id, e, A.expresiones) === fs.readFileSync(path.join(dir, id + '-' + e + '.svg'), 'utf8')) iguales++; } - ok(total === 40 && iguales === 40, 'la app dibuja las 40 expresiones idénticas a las aprobadas en design/companero/expresiones/ (' + iguales + ' de ' + total + ')'); + ok(total === 72 && iguales === 72, 'la app dibuja las 72 expresiones idénticas a las aprobadas en design/companero/expresiones/ (' + iguales + ' de ' + total + ')'); + ok(['guino', 'sonrojado', 'mareado', 'nam'].every((e) => EXPRESIONES.includes(e)), 'las cuatro de #33: guiño, sonrojado, mareado y ñam'); + const r = require('child_process').spawnSync(process.execPath, [path.join(ROOT, 'design/companero/expresiones.mjs')], { encoding: 'utf8' }); + ok(r.status === 0 && /72 expresiones, 0 diferencias fuera de rasgos/.test(r.stdout), 'design/companero/expresiones.mjs: ' + (r.stdout.trim() || r.stderr.trim().split('\n')[0])); } // Chrome driven over its DevTools protocol, with Node's own fetch and WebSocket: no dependency @@ -824,13 +827,26 @@ async function testEscenario() { async function testGlobo() { head('El globo del deseo se va solo y vuelve al tocar al compañero (#31)'); - await navegador(async ({ abrir, evaluar }) => { + await navegador(async ({ abrir, evaluar, cdp }) => { await abrir('/games/companero/index.html'); await evaluar(preparar({ monedas: 300 })); await abrir('/games/companero/index.html'); const visible = `(() => { const g = document.querySelector('[data-deseo]'); return g.dataset.visible === '1' && getComputedStyle(g).visibility !== 'hidden'; })()`; - const deseo = await evaluar(`document.querySelector('[data-deseo]').dataset.deseo`); + let deseo = await evaluar(`document.querySelector('[data-deseo]').dataset.deseo`); ok(await evaluar(visible), 'al abrir, el globo se ve'); + // every wish and the thanks, in the five languages, at 390 px: the bubble stays above the drawing + const T = textos(), tapan = []; + await cdp('Emulation.setDeviceMetricsOverride', { width: 390, height: 740, deviceScaleFactor: 1, mobile: true }); + for (const l of LANGS) { + await evaluar(preparar({ lang: l, monedas: 300 })); await abrir('/games/companero/index.html'); + for (const k of Object.keys(T[l]).filter((x) => /^deseo/.test(x))) { + const pisa = await evaluar(`(() => { const g = document.querySelector('[data-deseo]'); g.textContent = ${JSON.stringify(T[l][k])}; return g.getBoundingClientRect().bottom > document.querySelector('.cp-dibujo svg').getBoundingClientRect().top + 1; })()`); + if (pisa) tapan.push(l + ':' + k); + } + } + ok(!tapan.length, 'ningún deseo, en ningún idioma, tapa al compañero a 390 px' + (tapan.length ? ' (' + tapan.join(', ') + ')' : '')); + await evaluar(preparar({ monedas: 300 })); await abrir('/games/companero/index.html'); + deseo = await evaluar(`document.querySelector('[data-deseo]').dataset.deseo`); await evaluar(pausa(4400)); ok(!(await evaluar(visible)), 'a los 4 s se oculta solo'); const alto = await evaluar(`document.querySelector('.cp-compa').getBoundingClientRect().top`); @@ -896,6 +912,59 @@ async function testEscenasPagina() { }); } +/* ------------------------------------------------ touching it, and four new faces (#33) */ + +function testToqueLogica() { + head('Tocar al compañero: vínculo con la regla de 30 s, sin monedas (#33)'); + if (!K) return ok(false, 'sin módulo'); + const x = companero({ saldo: 40 }); + ok(x.c.hecho({ accion: 'tocar' }).ok && x.c.vinculo().puntos === 1 && x.m.saldo() === 40, 'tocarlo suma 1 de vínculo y no toca el saldo'); + x.pasar(5000); x.c.hecho({ accion: 'tocar' }); + ok(x.c.vinculo().puntos === 1, 'tocarlo otra vez dentro de 30 s no vuelve a sumar'); + ok(!K.ACCIONES.includes('tocar'), 'tocar no es un botón más de Cuidado: es el compañero mismo'); +} + +async function testToques() { + head('Tocar al compañero: una reacción distinta cada vez, y mareado con cinco toques (#33)'); + await navegador(async ({ abrir, evaluar, cdp }) => { + await abrir('/games/companero/index.html'); + await evaluar(preparar({ monedas: 80 })); + await abrir('/games/companero/index.html'); + const estado = `({ r: document.querySelector('.cp-compa').dataset.reaccion, e: document.querySelector('.cp-compa').dataset.expresion, v: +document.querySelector('[data-vinculo]').dataset.vinculo, m: +localStorage.getItem('chogan.app.coins') })`; + const tocar = async () => { await evaluar(`document.querySelector('.cp-compa').click(); true`); await evaluar(pausa(300)); return evaluar(estado); }; + const v0 = await evaluar(`+document.querySelector('[data-vinculo]').dataset.vinculo`); + const vistos = []; + for (let i = 0; i < 4; i++) vistos.push(await tocar()); + const CARA = { feliz: 'feliz', guino: 'guino', sonrojado: 'sonrojado', giro: 'feliz' }; + ok(vistos.every((x) => CARA[x.r] && x.e === CARA[x.r]), 'cada toque da risa, guiño, sonrojado o un giro, con su cara (' + vistos.map((x) => x.r).join(', ') + ')'); + ok(vistos.every((x, i) => !i || x.r !== vistos[i - 1].r), 'dos toques seguidos nunca dan la misma reacción'); + ok(vistos.every((x) => x.e !== 'mareado'), 'cuatro toques en menos de 3 s no lo marean'); + const quinto = await tocar(); + ok(quinto.r === 'mareado' && quinto.e === 'mareado', 'el quinto toque en menos de 3 s lo deja mareado'); + ok(quinto.m === 80 && quinto.v === v0 + 1, 'tocarlo no da ni quita monedas, y suma vínculo una sola vez (' + v0 + ' → ' + quinto.v + ')'); + await evaluar(pausa(2300)); + ok(await evaluar(`document.querySelector('.cp-compa').dataset.expresion`) !== 'mareado', 'el mareo se va solo en poco más de 2 s'); + + // the faces of eating and bathing, for something the visit did not wish for (a wish fulfilled is «feliz») + const deseo = await evaluar(`document.querySelector('[data-deseo]').dataset.deseo`); + const comida = ['fruta', 'zanahoria'].find((c) => c !== deseo); + await evaluar(`document.querySelector('[data-consumible="${comida}"]').click(); true`); await evaluar(pausa(200)); await evaluar(aceptar); await evaluar(pausa(150)); + ok(await evaluar(`document.querySelector('.cp-compa').dataset.expresion`) === 'nam', 'al comer (' + comida + '), mastica: ñam'); + await evaluar(pausa(2300)); + if (deseo !== 'banio') { + await evaluar(`document.querySelector('[data-consumible="jabon"]').click(); true`); await evaluar(pausa(200)); await evaluar(aceptar); await evaluar(pausa(150)); + ok(await evaluar(`document.querySelector('.cp-compa').dataset.expresion`) === 'sonrojado', 'en el baño de burbujas se sonroja'); + } else ok(true, 'la visita quería un baño: su cara es la de un deseo cumplido'); + + await cdp('Emulation.setEmulatedMedia', { features: [{ name: 'prefers-reduced-motion', value: 'reduce' }] }); + await abrir('/games/companero/index.html'); + // the dice fixed on the last of the four reactions, the spin, so the check never depends on luck + await evaluar(`Math.random = () => 0.99; true`); + const giro = (await tocar()).r === 'giro'; + ok(giro && await evaluar(`document.querySelector('.cp-compa').getAnimations({ subtree: true }).length === 0`), 'con movimiento reducido, el giro es solo la cara feliz, sin girar'); + }); +} + /* ------------------------------------------------ run */ const GROUPS = [...fs.readFileSync(__filename, 'utf8').matchAll(/^(?:async )?function (test[A-Za-z0-9]+)\(\) \{/gm)].map((m) => m[1]); diff --git a/www/games/companero/cuidado.js b/www/games/companero/cuidado.js index 12b2519d..d616ab33 100644 --- a/www/games/companero/cuidado.js +++ b/www/games/companero/cuidado.js @@ -25,6 +25,8 @@ { id: 'caricias', acciones: ['acariciar', 'cepillar'] } ]; var ACCIONES = ['acariciar', 'cepillar', 'jugar']; + // touching the companion itself (#33) is care too, with the same 30 s rule, but not a button + var GRATIS = ACCIONES.concat(['tocar']); // A wish is first a kind, all as likely, then which food: seven foods do not crowd out the rest var COMIDAS = DESEOS.filter(function (d) { return d.usar && d.usar !== 'jabon'; }).map(function (d) { return d.id; }); var TIPOS = [COMIDAS, ['banio'], ['ropa'], ['jugar'], ['caricias']]; @@ -78,7 +80,7 @@ function hecho(ev) { var suma = 0, t = ahora(); if (ev.accion) { - if (ACCIONES.indexOf(ev.accion) < 0) return { ok: false }; + if (GRATIS.indexOf(ev.accion) < 0) return { ok: false }; var antes = d.ultimas[ev.accion]; if (typeof antes !== 'number' || t - antes >= ESPERA_MS || t < antes) { suma += POR_CUIDADO; d.ultimas[ev.accion] = t; } } else if (ev.usar) suma += POR_CUIDADO; diff --git a/www/games/companero/expresiones.js b/www/games/companero/expresiones.js index fc157771..189e4d9a 100644 --- a/www/games/companero/expresiones.js +++ b/www/games/companero/expresiones.js @@ -9,7 +9,7 @@ })(this, function () { 'use strict'; - var EXPRESIONES = ['contento', 'feliz', 'antojado', 'dormido', 'sorprendido']; + var EXPRESIONES = ['contento', 'feliz', 'antojado', 'dormido', 'sorprendido', 'guino', 'sonrojado', 'mareado', 'nam']; function n(x) { return +x.toFixed(2); } function trazo(d, color, ancho) { @@ -21,6 +21,18 @@ function arriba(x, y, r, color, a) { return trazo('M' + n(x - r) + ' ' + n(y + r * 0.4) + 'q' + n(r) + ' ' + n(-r * 1.8) + ' ' + n(2 * r) + ' 0', color, a); } function abajo(x, y, r, color, a) { return trazo('M' + n(x - r) + ' ' + n(y) + 'q' + n(r) + ' ' + n(r * 0.9) + ' ' + n(2 * r) + ' 0', color, a); } function brillo(x, y, r) { return punto(x - r * 0.35, y - r * 0.35, r * 0.3, '#FFFFFF'); } + // @: dizzy, a small spiral that winds out from the eye's centre + function espiral(x, y, r, color, a) { + var s = r * 0.25; + return trazo('M' + n(x - s) + ' ' + n(y) + 'a' + n(s) + ' ' + n(s) + ' 0 1 1 ' + n(2 * s) + ' 0a' + n(2 * s) + ' ' + n(2 * s) + ' 0 1 1 ' + n(-3.5 * s) + ' 0a' + n(3 * s) + ' ' + n(3 * s) + ' 0 1 1 ' + n(5.5 * s) + ' 0', color, a); + } + // pink cheeks under the eyes, for «sonrojado» + function mejillas(a) { + return a.ojos.map(function (o, i) { + var x = o[0] + (i ? 4 : -4), y = o[1] + o[2] * 2.4; + return ''; + }).join(''); + } function ojos(a, e) { var out = []; @@ -32,6 +44,9 @@ else if (e === 'dormido') out.push(abajo(x, y, r, a.color, 2.4)); else if (e === 'antojado') out.push(punto(x, y, r, a.color), punto(px + 1.4, py - 0.9, pr, a.pupila), brillo(px + 1.4, py - 0.9, pr * 1.6)); else if (e === 'sorprendido') out.push(punto(x, y, r * 1.3, a.color), punto(x, y, pr, a.pupila)); + else if (e === 'guino') out.push(i ? arriba(x, y, r, a.color, 2.4) : punto(x, y, r, a.color) + punto(px, py, pr, a.pupila)); + else if (e === 'sonrojado' || e === 'nam') out.push(arriba(x, y, r, a.color, 2.4)); + else if (e === 'mareado') out.push(espiral(x, y, r * 1.1, a.color, 1.6)); } else { // punto: a dark eye; pupila: the owl's pupil on its light disc, which stays if (e === 'feliz') out.push(arriba(x, y, r, a.color, a.tipo === 'pupila' ? 3.4 : 2.8)); @@ -41,6 +56,10 @@ var rr = a.tipo === 'pupila' ? r * 0.6 : r * 1.3; // wide eyes: a bigger dot, or a smaller pupil out.push(punto(x, y, rr, a.color), brillo(x, y, rr)); } + // a wink: the left eye as drawn, the right one closed in joy + else if (e === 'guino') out.push(i ? arriba(x, y, r, a.color, a.tipo === 'pupila' ? 3.4 : 2.8) : punto(x, y, r, a.color)); + else if (e === 'sonrojado' || e === 'nam') out.push(arriba(x, y, r, a.color, a.tipo === 'pupila' ? 3.4 : 2.8)); + else if (e === 'mareado') out.push(espiral(x, y, r * 1.2, a.color, a.tipo === 'pupila' ? 2 : 1.6)); } }); return out.join(''); @@ -56,6 +75,12 @@ return pre + '' + trazo('M' + n(cx - w * 0.3) + ' ' + n(cy) + 'c1.2 1.8 ' + n(w * 0.6 - 1.2) + ' 1.8 ' + n(w * 0.6) + ' 0', c, 3); if (e === 'dormido') return pre + trazo('M' + n(cx - w * 0.2) + ' ' + n(cy + 0.5) + 'h' + n(w * 0.4), c, 3); if (e === 'sorprendido') return pre + ''; + // a closed smile, for the wink and the blush + if (e === 'guino' || e === 'sonrojado') return pre + trazo('M' + n(cx - w / 2) + ' ' + n(cy - 1) + 'c2 3.5 ' + n(w - 2) + ' 3.5 ' + w + ' 0', c, 3); + // a wobbly line + if (e === 'mareado') return pre + trazo('M' + n(cx - w / 2) + ' ' + n(cy) + 'q' + n(w / 4) + ' -3 ' + n(w / 2) + ' 0t' + n(w / 2) + ' 0', c, 3); + // chewing: a «w» + if (e === 'nam') return pre + trazo('M' + n(cx - w / 2) + ' ' + n(cy - 1) + 'q' + n(w / 4) + ' 3.5 ' + n(w / 2) + ' 0q' + n(w / 4) + ' 3.5 ' + n(w / 2) + ' 0', c, 3); return ''; } @@ -76,7 +101,7 @@ if (e === 'contento' || !a || EXPRESIONES.indexOf(e) < 0) return svg; var top = datos.cabeza[animal]; var zeta = e === 'dormido' ? trazo('M84 ' + (top - 4) + 'h5l-5 6h5M92 ' + (top - 12) + 'h3.5l-3.5 4.5h3.5', '#8A8F9C', 1.8) : ''; - var nuevo = ojos(a, e) + boca(a, e) + zeta; + var nuevo = (e === 'sonrojado' ? mejillas(a) : '') + ojos(a, e) + boca(a, e) + zeta; // the new features go at the end of the face, over everything the face draws return quitarRasgos(svg, a).replace(/<\/g>\s*<\/svg>\s*$/, nuevo + '\n\n'); } diff --git a/www/games/companero/index.html b/www/games/companero/index.html index edfd1bd0..6cdecce2 100644 --- a/www/games/companero/index.html +++ b/www/games/companero/index.html @@ -15,11 +15,12 @@ /* top: the game area's own top padding, so it sticks right under the bar with no gap for the options to show through */ .cp-escenario { position: sticky; top: calc(-1 * var(--s-3)); z-index: 2; display: flex; flex-direction: column; gap: var(--s-2); margin: calc(-1 * var(--s-3)) calc(-1 * var(--s-3)) 0; padding: var(--s-3) var(--s-3) var(--s-2); background: var(--c-bg); } - .cp-compa { position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--s-1, 4px); padding: var(--s-5) 0 var(--s-2); background: var(--c-surface); + /* the top padding is the wish's room: the bubble never covers a hat (#33) */ + .cp-compa { position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--s-1, 4px); padding: 56px 0 var(--s-2); background: var(--c-surface); border-radius: var(--r-lg); box-shadow: var(--sh-1); cursor: pointer; -webkit-tap-highlight-color: transparent; } .cp-compa:focus-visible { outline: 3px solid var(--game); outline-offset: 2px; } - .cp-dibujo svg { width: min(34vw, 150px); height: auto; display: block; } - @media (max-height: 600px) { .cp-dibujo svg { width: 100px; } .cp-compa { padding-top: var(--s-4); } } + .cp-dibujo svg { width: min(30vw, 136px); height: auto; display: block; } + @media (max-height: 600px) { .cp-dibujo svg { width: 96px; } } /* one row per section that slides sideways, card by card */ .cp-fila { display: flex; gap: var(--s-2); overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scroll-padding-inline: 2px; padding: 2px 2px var(--s-2); scrollbar-width: thin; } @@ -40,8 +41,8 @@ .cp-subtitulo { margin: var(--s-2) 0 0; font-size: .9rem; opacity: .8; } /* the wish floats over the top of the card, so showing or hiding it never moves the companion */ .cp-globo { position: absolute; top: var(--s-2); inset-inline: var(--s-3); z-index: 1; margin: 0 auto; width: fit-content; max-width: calc(100% - 2 * var(--s-3)); - padding: var(--s-2) var(--s-3); border-radius: var(--r-lg); background: var(--c-surface); box-shadow: var(--sh-2, var(--sh-1)); - text-align: center; font-weight: 600; transition: opacity .3s ease, visibility .3s; } + padding: 6px var(--s-3); border-radius: var(--r-lg); background: var(--c-surface); box-shadow: var(--sh-2, var(--sh-1)); + text-align: center; font-weight: 600; font-size: .92rem; line-height: 1.25; transition: opacity .3s ease, visibility .3s; } .cp-globo[data-visible="0"] { opacity: 0; visibility: hidden; } @media (prefers-reduced-motion: reduce) { .cp-globo { transition: none; } } .cp-globo[data-cumplido="1"] { border: 2px solid var(--game); } @@ -50,6 +51,9 @@ .cp-accion { min-height: 56px; justify-content: center; font-weight: 600; } @keyframes cp-salto { 0%, 100% { transform: translateY(0); } 40% { transform: translateY(-8%); } } .cp-salto { animation: cp-salto .5s ease; } + /* a tap's spin (#33): a quick turn on the spot */ + @keyframes cp-giro { 0% { transform: rotateY(0); } 100% { transform: rotateY(360deg); } } + .cp-giro { animation: cp-giro .6s ease-in-out; } /* The care scenes (#32): the resting pose in escenas.js is the still picture; these only move it */ .cp-esc-bocado, .cp-esc-burbuja, .cp-esc-pelota { transform-box: fill-box; transform-origin: center; } .cp-esc-bocado { animation: cp-comer 1.8s ease-in-out forwards; } @@ -65,7 +69,7 @@ @keyframes cp-pelota { 0%, 30%, 60%, 90%, 100% { transform: translateY(0); } 15% { transform: translateY(-28px) rotate(120deg); } 45% { transform: translateY(-18px) rotate(240deg); } 75% { transform: translateY(-8px) rotate(330deg); } } .cp-item .cp-icono svg { width: 40px; height: 40px; } - @media (prefers-reduced-motion: reduce) { .cp-salto, .cp-esc-bocado, .cp-esc-burbuja, .cp-esc-pelota { animation: none; } } + @media (prefers-reduced-motion: reduce) { .cp-salto, .cp-giro, .cp-esc-bocado, .cp-esc-burbuja, .cp-esc-pelota { animation: none; } } @@ -364,15 +368,16 @@ function reduceMotion() { return !!(C.ui.reduceMotion && C.ui.reduceMotion()); } // A face for a few seconds after something happens, then back to the opening one - function mostrar(cara, ms, conEscena) { + function mostrar(cara, ms, conEscena, movimiento) { momento = cara; + compa.dataset.reaccion = ''; escena = conEscena || null; if (relojMomento) clearTimeout(relojMomento); relojMomento = setTimeout(function () { momento = null; escena = null; render(); }, ms); render(); if (!reduceMotion()) { var svg = lienzo.querySelector('svg'); - if (svg) svg.classList.add('cp-salto'); + if (svg) svg.classList.add(movimiento || 'cp-salto'); } } // Asleep only while the screen stays open and untouched; never because of time away (#9) @@ -392,9 +397,30 @@ if (relojGlobo) clearTimeout(relojGlobo); relojGlobo = setTimeout(function () { globo.dataset.visible = '0'; }, 4000); } - compa.addEventListener('click', function () { C.feedback('tap'); mostrarGlobo(); }); + // Touching the companion (#33): it shows its wish and reacts, never the same way twice running. + // Five touches within 3 s leave it dizzy for a moment: a funny face, never a punishment. + var REACCIONES = ['feliz', 'guino', 'sonrojado', 'giro']; + var toques = [], ultimaReaccion = null; + function tocarCompa() { + var t = Date.now(); + toques = toques.filter(function (x) { return t - x < 3000; }); + toques.push(t); + mostrarGlobo(); + K.hecho({ accion: 'tocar' }); + C.feedback('tap'); + var r; + if (toques.length >= 5) { toques = []; r = 'mareado'; mostrar('mareado', 2000); } + else { + var opciones = REACCIONES.filter(function (x) { return x !== ultimaReaccion; }); + r = opciones[Math.floor(Math.random() * opciones.length)]; + mostrar(r === 'giro' ? 'feliz' : r, 1500, null, r === 'giro' ? 'cp-giro' : 'cp-salto'); + } + ultimaReaccion = r; + compa.dataset.reaccion = r; + } + compa.addEventListener('click', tocarCompa); compa.addEventListener('keydown', function (e) { - if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); mostrarGlobo(); } + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); tocarCompa(); } }); // A mouse wheel slides a row sideways while the row can still move that way, then scrolls the page raiz.addEventListener('wheel', function (e) { @@ -410,7 +436,9 @@ if (!r || !r.ok) return; if (r.cumplioDeseo) mostrarGlobo(); C.feedback(r.cumplioDeseo ? 'good' : 'tap'); - mostrar('feliz', conEscena ? 2000 : 2500, conEscena); + // a wish fulfilled is joy; otherwise eating chews (ñam) and a bath blushes + var cara = r.cumplioDeseo || !conEscena ? 'feliz' : { comer: 'nam', banio: 'sonrojado' }[conEscena.tipo] || 'feliz'; + mostrar(cara, conEscena ? 2000 : 2500, conEscena); } function escenaDe(id) { return id === 'jabon' ? { tipo: 'banio' } : { tipo: 'comer', comida: id }; } function cuidar(ev) { reaccionar(K.hecho(ev), ev.accion === 'jugar' ? { tipo: 'jugar' } : null); }