diff --git a/COMPANERO.md b/COMPANERO.md index a40e9ae9..6d1b7537 100644 --- a/COMPANERO.md +++ b/COMPANERO.md @@ -85,6 +85,7 @@ 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. +- **Ganas (#39):** de comer, de jugar y de bañarse, de 0 a 3. Suben una cada 3, 4 y 5 partidas terminadas en toda la app (leídas de las estadísticas del núcleo, que el compañero nunca escribe). **Crecen jugando, nunca por el tiempo fuera:** un año sin jugar las deja como estaban. En 3, el globo lo dice («¡Uf, tanto mirarte jugar me abrió el apetito!») y la cara es antojada. Atenderlas las baja a 0, y si estaban en 2 o más suman +2 de vínculo. Si nadie las atiende no pasa nada. - **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. @@ -102,6 +103,7 @@ Todo se guarda en el teléfono, bajo `chogan.companero.`: | Decisión | Por qué | |---|---| | Deseos en vez de necesidades | Las mascotas virtuales suelen enganchar con culpa; Capybara promete no hacerlo. | +| Ganas que crecen jugando, no esperando | El compañero reacciona a lo que hacés sin castigar a quien no entra (decisión del 2026-11-03). | | El compañero es una entrada más de `games.json` | El núcleo queda intacto y sincronizar con el original no da conflictos. | | Lógica pura en módulos propios (`economia.js`, `cuidado.js`, `disenos.js`, `prendas.js`, `expresiones.js`) | Se prueba sin navegador, con el reloj y el azar simulados. | | Una sola fuente para la app y el arte | `design/companero/` usa los mismos módulos, así lo aprobado es exactamente lo que se ve. | diff --git a/tools/test-companero.js b/tools/test-companero.js index d58375d9..807d46ae 100644 --- a/tools/test-companero.js +++ b/tools/test-companero.js @@ -318,9 +318,11 @@ function companero(o) { const x = nuevo({ saldo: o.saldo === undefined ? 500 : o.saldo, datos: o.datos }); let t = o.t || 1e12; let dado = o.dado || (() => 0); - const crearCuidado = () => K.crear({ store: x.s, economia: x.eco, ahora: () => t, azar: () => dado() }); + // the games finished in the whole app, as the core counts them (C.stats.get().plays) + let jugadas = o.partidas || 0; + const crearCuidado = () => K.crear({ store: x.s, economia: x.eco, ahora: () => t, azar: () => dado(), partidas: () => jugadas }); const c = crearCuidado(); - return Object.assign(x, { c, crearCuidado, pasar(ms) { t += ms; }, dado(f) { dado = f; } }); + return Object.assign(x, { c, crearCuidado, pasar(ms) { t += ms; }, dado(f) { dado = f; }, jugar(n) { jugadas += n; } }); } function testCuidadoLogica() { @@ -574,7 +576,7 @@ function testDeseosSinCarencia() { zh: ['饿', '伤心', '难过', '脏', '孤单', '寂寞', '想你'], fa: ['گرسنه', 'غمگین', 'ناراحت', 'کثیف', 'تنها', 'دلتنگ'], }; - const claves = (K ? K.DESEOS.map((d) => 'deseo_' + d.id) : []).concat(['deseoCumplido']); + const claves = (K ? K.DESEOS.map((d) => 'deseo_' + d.id) : []).concat(['deseoCumplido', 'gana_comer', 'gana_jugar', 'gana_banio']); for (const l of Object.keys(PROHIBIDAS)) { const textos = claves.map((k) => T[l] && T[l][k]); ok(textos.every((x) => typeof x === 'string' && x.length > 2), l + ': cada deseo tiene su texto'); @@ -1003,6 +1005,65 @@ async function testRopaNueva() { }); } +/* ------------------------------------------------ wanting things, as you play (#39) */ + +function testGanas() { + head('Ganas de comer, jugar y bañarse: crecen jugando, nunca por ausencia (#39)'); + if (!K || !K.GANAS) return ok(false, 'cuidado.js exporta GANAS'); + ok(JSON.stringify(K.GANAS) === JSON.stringify({ comer: 3, jugar: 4, banio: 5 }), 'una gana sube cada 3 partidas (comer), 4 (jugar) y 5 (bañarse)'); + const x = companero({ saldo: 200, partidas: 40 }); + ok(JSON.stringify(x.c.ganas()) === '{"comer":0,"jugar":0,"banio":0}', 'un perfil que ya jugaba empieza con las ganas en 0, no con las partidas de antes'); + x.jugar(3); + ok(x.c.ganas().comer === 1 && x.c.ganas().jugar === 0, 'tras 3 partidas: comer 1, jugar 0'); + x.jugar(2); + ok(JSON.stringify(x.c.ganas()) === '{"comer":1,"jugar":1,"banio":1}', 'tras 5: las tres en 1'); + x.jugar(30); + ok(JSON.stringify(x.c.ganas()) === '{"comer":3,"jugar":3,"banio":3}', 'tras 35: las tres en 3, el tope'); + x.pasar(365 * 864e5); + ok(JSON.stringify(x.crearCuidado().ganas()) === '{"comer":3,"jugar":3,"banio":3}', 'un año después siguen en 3: nunca bajan solas'); + + const v0 = x.c.vinculo().puntos; + x.eco.comprar('galleta'); + const r = x.c.alimentar('galleta'); + ok(x.c.ganas().comer === 0 && x.c.ganas().jugar === 3, 'cualquier comida baja «comer» a 0, y nada más'); + ok(r.atendio === 'comer' && x.c.vinculo().puntos === v0 + 1 + 2, 'atender una gana en 2 o más suma +2 de vínculo extra (' + v0 + ' → ' + x.c.vinculo().puntos + ')'); + x.c.hecho({ accion: 'jugar' }); + x.eco.comprar('jabon'); x.c.alimentar('jabon'); + ok(JSON.stringify(x.c.ganas()) === '{"comer":0,"jugar":0,"banio":0}', 'jugar baja «jugar» y el jabón, «bañarse»'); + x.jugar(3); const v1 = x.c.vinculo().puntos; + x.pasar(31000); x.eco.comprar('fruta'); x.c.alimentar('fruta'); + ok(x.c.vinculo().puntos === v1 + 1, 'una gana en 1 no da el extra'); + + const y = companero({ partidas: 0 }); + y.pasar(365 * 864e5); + ok(JSON.stringify(y.crearCuidado().ganas()) === '{"comer":0,"jugar":0,"banio":0}', 'un año sin jugar: las ganas siguen en 0'); + const z = companero({ partidas: 0 }); + z.jugar(9); + ok(z.c.ganaFuerte() === 'comer' && z.c.expresionAlAbrir() === 'antojado', 'con una gana en 3, abre antojado y la gana fuerte es la que más creció (comer)'); + ok(z.s.escritas.every((k) => k === 'inventario' || k === 'cuidado'), 'las ganas se guardan en «cuidado»: nunca escribe las estadísticas del núcleo'); + // an old saved care, from before #39, keeps its bond + const w = nuevo({ saldo: 10, datos: { cuidado: { version: 1, puntos: 42, ultimas: {}, deseo: null, ultimoDeseo: null } } }); + const cw = K.crear({ store: w.s, economia: w.eco, partidas: () => 80 }); + ok(cw.vinculo().puntos === 42 && JSON.stringify(cw.ganas()) === '{"comer":0,"jugar":0,"banio":0}', 'un cuidado guardado antes de #39 conserva su vínculo y empieza con las ganas en 0'); +} + +async function testGanasPagina() { + head('Las ganas en la página (#39)'); + await navegador(async ({ abrir, evaluar }) => { + await abrir('/games/companero/index.html'); + await evaluar(preparar({ monedas: 100 })); + await evaluar(`localStorage.setItem('chogan.app.stats', JSON.stringify({ plays: 12, timeMs: 0, byGame: {}, streak: { count: 0 } })); localStorage.setItem('chogan.companero.cuidado', JSON.stringify({ version: 1, puntos: 5, ultimas: {}, deseo: null, ultimoDeseo: null, ganas: { comer: 0, jugar: 4, banio: 12 } })); true`); + await abrir('/games/companero/index.html'); + const g = await evaluar(`[...document.querySelectorAll('.cp-gana')].map((x) => x.dataset.gana + ':' + x.dataset.nivel).join()`); + ok(g === 'comer:3,jugar:2,banio:0', 'tres indicadores con su nivel: comer 3, jugar 2, bañarse 0 (' + g + ')'); + ok(await evaluar(`[...document.querySelectorAll('.cp-gana')].every((x) => x.querySelector('svg') && x.getAttribute('aria-label'))`), 'cada indicador tiene su dibujo y un nombre para el lector de pantalla'); + ok(await evaluar(`document.querySelector('.cp-globo').dataset.gana === 'comer'`) && await evaluar(`document.querySelector('.cp-compa').dataset.expresion === 'antojado'`), 'con ganas de comer en 3, el globo lo dice y la cara es antojada'); + await evaluar(`document.querySelector('[data-consumible="fruta"]').click(); true`); await evaluar(pausa(200)); await evaluar(aceptar); await evaluar(pausa(200)); + ok(await evaluar(`document.querySelector('[data-gana="comer"]').dataset.nivel`) === '0', 'al darle de comer, la gana baja a 0'); + ok(!(await evaluar(`localStorage.getItem('chogan.app.stats')`)).includes('companero'), 'las estadísticas del núcleo quedan intactas'); + }); +} + /* ------------------------------------------------ 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 d616ab33..e828a21f 100644 --- a/www/games/companero/cuidado.js +++ b/www/games/companero/cuidado.js @@ -35,6 +35,10 @@ var POR_CUIDADO = 1, POR_DESEO = 3; // the same free action adds to the bond at most once in this long; it is never blocked var ESPERA_MS = 30000; + // Wanting things (#39): one level more every this many games finished in the whole app, up to 3. + // Games, never time: a year away with no games leaves them as they were. + var GANAS = { comer: 3, jugar: 4, banio: 5 }; + var TOPE = 3, POR_GANA = 2; function vacio() { return { version: VERSION, puntos: 0, ultimas: {}, deseo: null, ultimoDeseo: null }; } function sano(d) { @@ -58,9 +62,28 @@ var store = opts.store, eco = opts.economia; var ahora = opts.ahora || function () { return Date.now(); }; var azar = opts.azar || Math.random; + var partidas = function () { var n = opts.partidas ? opts.partidas() : 0; return typeof n === 'number' && n >= 0 ? Math.floor(n) : 0; }; var d = store.get('cuidado', null); if (!sano(d)) d = vacio(); + // each gana keeps the game count it was last tended at; a missing one starts now, so the games + // played before #39 never count, and a count above the core's (its stats were erased) comes down + if (!d.ganas || typeof d.ganas !== 'object') d.ganas = {}; + var nuevas = false; + Object.keys(GANAS).forEach(function (g) { + var b = d.ganas[g]; + if (typeof b !== 'number' || b < 0 || b > partidas()) { d.ganas[g] = partidas(); nuevas = true; } + }); function guardar() { store.set('cuidado', d); } + // kept at once: else the next opening would start them over from its own count + if (nuevas) guardar(); + function nivel(g) { return Math.min(TOPE, Math.floor((partidas() - d.ganas[g]) / GANAS[g])); } + function ganas() { var r = {}; Object.keys(GANAS).forEach(function (g) { r[g] = nivel(g); }); return r; } + // the gana at the top, if any, in a fixed order: the bubble says it and the face shows it + function ganaFuerte() { + var gs = Object.keys(GANAS); + for (var i = 0; i < gs.length; i++) if (nivel(gs[i]) >= TOPE) return gs[i]; + return null; + } // A visit is each time the space opens: one wish, never the same as the last visit's function visitar() { @@ -86,9 +109,17 @@ } else if (ev.usar) suma += POR_CUIDADO; var cumplio = !!(d.deseo && !d.deseo.cumplido && cumple(d.deseo, ev)); if (cumplio) { d.deseo.cumplido = true; suma += POR_DESEO; } + // tending a gana brings it back to 0, with a little extra when it had grown + var gana = ev.usar === 'jabon' ? 'banio' : ev.usar ? 'comer' : ev.accion === 'jugar' || ev.juego ? 'jugar' : null; + var atendio = null; + if (gana) { + if (nivel(gana) >= 2) suma += POR_GANA; + if (nivel(gana) > 0) atendio = gana; + d.ganas[gana] = partidas(); + } d.puntos = Math.min(MAXIMO, d.puntos + suma); guardar(); - return { ok: true, suma: suma, cumplioDeseo: cumplio }; + return { ok: true, suma: suma, cumplioDeseo: cumplio, atendio: atendio }; } // Feeding or a bubble bath: uses one unit from the economy, then counts as care @@ -105,10 +136,11 @@ } // The face on opening depends only on the visit's wish, never on time away - function expresionAlAbrir() { return d.deseo && !d.deseo.cumplido ? 'antojado' : 'contento'; } + function expresionAlAbrir() { return (d.deseo && !d.deseo.cumplido) || ganaFuerte() ? 'antojado' : 'contento'; } - return { visitar: visitar, deseo: deseo, hecho: hecho, alimentar: alimentar, vinculo: vinculo, expresionAlAbrir: expresionAlAbrir }; + return { visitar: visitar, deseo: deseo, hecho: hecho, alimentar: alimentar, vinculo: vinculo, expresionAlAbrir: expresionAlAbrir, + ganas: ganas, ganaFuerte: ganaFuerte }; } - return { VERSION: VERSION, DESEOS: DESEOS, ACCIONES: ACCIONES, NIVELES: NIVELES, MAXIMO: MAXIMO, crear: crear }; + return { VERSION: VERSION, DESEOS: DESEOS, ACCIONES: ACCIONES, NIVELES: NIVELES, MAXIMO: MAXIMO, GANAS: GANAS, crear: crear }; }); diff --git a/www/games/companero/index.html b/www/games/companero/index.html index fc9286e7..6cb8c236 100644 --- a/www/games/companero/index.html +++ b/www/games/companero/index.html @@ -47,6 +47,12 @@ @media (prefers-reduced-motion: reduce) { .cp-globo { transition: none; } } .cp-globo[data-cumplido="1"] { border: 2px solid var(--game); } .cp-vinculo { margin: 0; text-align: center; font-size: .9rem; } + /* three small gauges of what it wants (#39): an icon and three dots */ + .cp-ganas { display: flex; gap: var(--s-3); justify-content: center; } + .cp-gana { display: flex; align-items: center; gap: 3px; } + .cp-gana svg { width: 18px; height: 18px; } + .cp-punto { width: 6px; height: 6px; border-radius: 50%; background: var(--c-line); } + .cp-punto--on { background: var(--game); } .cp-corazones { color: #D7263D; letter-spacing: 2px; } .cp-accion { min-height: 56px; justify-content: center; font-weight: 600; } @keyframes cp-salto { 0%, 100% { transform: translateY(0); } 40% { transform: translateY(-8%); } } @@ -89,35 +95,35 @@ /* ==== TEXTOS START ==== */ var TEXTOS = { - fa: { p_diadema: 'تل سر', p_mono: 'پاپیون بزرگ', p_perlas: 'گردنبند مروارید', p_vestido: 'پیراهن گل‌دار', p_gala: 'لباس مهمانی', p_falda: 'دامن', p_zapatillas: 'کفش ورزشی', p_botas: 'چکمه', p_sandalias: 'صندل', e_pies: 'پا', c_sandia: 'هندوانه', c_galleta: 'کلوچه', c_helado: 'بستنی', c_te: 'دمنوش', deseo_sandia: 'دوست دارم هندوانه امتحان کنم!', deseo_galleta: 'یک کلوچه چه خوب می‌شد!', deseo_helado: 'یک بستنی چه کیفی دارد!', deseo_te: 'یک دمنوش بنوشیم؟', verDeseo: 'دیدن آرزویش', acariciar: 'نوازش', cepillar: 'برس زدن', jugar: 'بازی', c_fruta: 'میوه', c_zanahoria: 'هویج', c_pastel: 'کیک', c_jabon: 'صابون حباب', cantidad: '×{n}', vinculo: 'پیوند: سطح {n} از {m}', deseoCumplido: 'ممنون! خیلی دوستش داشتم.', deseo_fruta: 'دوست دارم میوه امتحان کنم!', deseo_zanahoria: 'دوست دارم هویج امتحان کنم!', deseo_pastel: 'یک کیک چه خوب می‌شد!', deseo_banio: 'یک حمام کف چه کیفی دارد!', deseo_ropa: 'با یک لباس تازه قشنگ می‌شوم!', deseo_jugar: 'بیا کمی بازی کنیم!', deseo_caricias: 'نوازشم می‌کنی؟', + fa: { gana_comer: 'این همه بازی، اشتهایم را باز کرد!', gana_jugar: 'دیدن بازی‌ات، من را هم هوس بازی انداخت!', gana_banio: 'یک حمام کف الان چه می‌چسبد!', ganaNivel_comer: 'میل به خوردن: {n} از ۳', ganaNivel_jugar: 'میل به بازی: {n} از ۳', ganaNivel_banio: 'میل به حمام: {n} از ۳', p_diadema: 'تل سر', p_mono: 'پاپیون بزرگ', p_perlas: 'گردنبند مروارید', p_vestido: 'پیراهن گل‌دار', p_gala: 'لباس مهمانی', p_falda: 'دامن', p_zapatillas: 'کفش ورزشی', p_botas: 'چکمه', p_sandalias: 'صندل', e_pies: 'پا', c_sandia: 'هندوانه', c_galleta: 'کلوچه', c_helado: 'بستنی', c_te: 'دمنوش', deseo_sandia: 'دوست دارم هندوانه امتحان کنم!', deseo_galleta: 'یک کلوچه چه خوب می‌شد!', deseo_helado: 'یک بستنی چه کیفی دارد!', deseo_te: 'یک دمنوش بنوشیم؟', verDeseo: 'دیدن آرزویش', acariciar: 'نوازش', cepillar: 'برس زدن', jugar: 'بازی', c_fruta: 'میوه', c_zanahoria: 'هویج', c_pastel: 'کیک', c_jabon: 'صابون حباب', cantidad: '×{n}', vinculo: 'پیوند: سطح {n} از {m}', deseoCumplido: 'ممنون! خیلی دوستش داشتم.', deseo_fruta: 'دوست دارم میوه امتحان کنم!', deseo_zanahoria: 'دوست دارم هویج امتحان کنم!', deseo_pastel: 'یک کیک چه خوب می‌شد!', deseo_banio: 'یک حمام کف چه کیفی دارد!', deseo_ropa: 'با یک لباس تازه قشنگ می‌شوم!', deseo_jugar: 'بیا کمی بازی کنیم!', deseo_caricias: 'نوازشم می‌کنی؟', p_gorra: 'کلاه', p_corona: 'تاج', p_lentes: 'عینک', p_lazo: 'پاپیون', p_panuelo: 'دستمال گردن', p_sueter: 'پلیور', p_capa: 'شنل', e_cabeza: 'سر', e_cuello: 'گردن', e_cuerpo: 'بدن', ninguna: 'هیچ‌کدام', ariaPrecio: '{nombre}، {precio}', ariaTuyo: '{nombre}، مال توست', ariaEnUso: '{nombre}، در حال استفاده', ariaPerfil: '{nombre}، در نمایه باز می‌شود', d_natural: 'طبیعی', d_medianoche: 'نیمه‌شب', d_arcoiris: 'رنگین‌کمان', d_manchitas: 'خال‌خالی', d_rayas: 'راه‌راه', d_dorado: 'طلایی', desbloquear: 'با {n} سکه باز شود؟', desbloquearBtn: 'باز کردن', faltan: '{n} سکه کم داری', jugarParaGanar: 'یک بازی کن تا سکه بگیری.', enPerfil: 'در نمایه باز می‌شود', precio: '{n} سکه', tuyo: 'مال توست', monedas: 'سکه', animales: 'حیوان‌ها', colores: 'رنگ‌ها', ropa: 'لباس‌ها', cuidado: 'مراقبت', cargando: 'همراهت را آماده می‌کنیم…', vacio: 'هنوز اینجا چیزی نیست.', sinMonedas: 'هنوز سکه نداری. یک بازی کن تا سکه بگیری.', equipado: 'در حال استفاده', a_fox: 'روباه', a_owl: 'جغد', a_cat: 'گربه', a_bear: 'خرس', a_capybara: 'کاپیبارا', a_conejo: 'خرگوش', a_panda: 'پاندا', a_nutria: 'سمور آبی' }, - en: { p_diadema: 'Headband', p_mono: 'Big bow', p_perlas: 'Pearls', p_vestido: 'Flower dress', p_gala: 'Party dress', p_falda: 'Skirt', p_zapatillas: 'Sneakers', p_botas: 'Boots', p_sandalias: 'Sandals', e_pies: 'Feet', c_sandia: 'Watermelon', c_galleta: 'Cookie', c_helado: 'Ice cream', c_te: 'Herbal tea', deseo_sandia: 'I would love to try some watermelon!', deseo_galleta: 'A cookie would be lovely!', deseo_helado: 'I would love an ice cream!', deseo_te: 'Shall we have some herbal tea?', verDeseo: 'See its wish', acariciar: 'Pet', cepillar: 'Brush', jugar: 'Play', c_fruta: 'Fruit', c_zanahoria: 'Carrot', c_pastel: 'Cake', c_jabon: 'Bubble soap', cantidad: '×{n}', vinculo: 'Bond: level {n} of {m}', deseoCumplido: 'Thank you! I loved it.', deseo_fruta: 'I would love to try some fruit!', deseo_zanahoria: 'I would love to try the carrot!', deseo_pastel: 'A cake would be lovely!', deseo_banio: 'I would love a bubble bath!', deseo_ropa: 'Something new to wear would look nice on me!', deseo_jugar: 'Let us play for a bit!', deseo_caricias: 'Could I have a cuddle?', + en: { gana_comer: 'All that playing gave me an appetite!', gana_jugar: 'Watching you play makes me want to play too!', gana_banio: 'A bubble bath would feel so nice!', ganaNivel_comer: 'Wants to eat: {n} of 3', ganaNivel_jugar: 'Wants to play: {n} of 3', ganaNivel_banio: 'Wants a bath: {n} of 3', p_diadema: 'Headband', p_mono: 'Big bow', p_perlas: 'Pearls', p_vestido: 'Flower dress', p_gala: 'Party dress', p_falda: 'Skirt', p_zapatillas: 'Sneakers', p_botas: 'Boots', p_sandalias: 'Sandals', e_pies: 'Feet', c_sandia: 'Watermelon', c_galleta: 'Cookie', c_helado: 'Ice cream', c_te: 'Herbal tea', deseo_sandia: 'I would love to try some watermelon!', deseo_galleta: 'A cookie would be lovely!', deseo_helado: 'I would love an ice cream!', deseo_te: 'Shall we have some herbal tea?', verDeseo: 'See its wish', acariciar: 'Pet', cepillar: 'Brush', jugar: 'Play', c_fruta: 'Fruit', c_zanahoria: 'Carrot', c_pastel: 'Cake', c_jabon: 'Bubble soap', cantidad: '×{n}', vinculo: 'Bond: level {n} of {m}', deseoCumplido: 'Thank you! I loved it.', deseo_fruta: 'I would love to try some fruit!', deseo_zanahoria: 'I would love to try the carrot!', deseo_pastel: 'A cake would be lovely!', deseo_banio: 'I would love a bubble bath!', deseo_ropa: 'Something new to wear would look nice on me!', deseo_jugar: 'Let us play for a bit!', deseo_caricias: 'Could I have a cuddle?', p_gorra: 'Cap', p_corona: 'Crown', p_lentes: 'Glasses', p_lazo: 'Bow', p_panuelo: 'Bandana', p_sueter: 'Sweater', p_capa: 'Cape', e_cabeza: 'Head', e_cuello: 'Neck', e_cuerpo: 'Body', ninguna: 'None', ariaPrecio: '{nombre}, {precio}', ariaTuyo: '{nombre}, yours', ariaEnUso: '{nombre}, in use', ariaPerfil: '{nombre}, unlocked in your profile', d_natural: 'Natural', d_medianoche: 'Midnight', d_arcoiris: 'Soft rainbow', d_manchitas: 'Spots', d_rayas: 'Stripes', d_dorado: 'Golden', desbloquear: 'Unlock for {n} coins?', desbloquearBtn: 'Unlock', faltan: 'You need {n} more coins', jugarParaGanar: 'Play a game to earn some.', enPerfil: 'Unlocked in your profile', precio: '{n} coins', tuyo: 'Yours', monedas: 'Coins', animales: 'Animals', colores: 'Colours', ropa: 'Clothes', cuidado: 'Care', cargando: 'Getting your companion ready…', vacio: 'Nothing here yet.', sinMonedas: 'You have no coins yet. Play a game to earn some.', equipado: 'In use', a_fox: 'Fox', a_owl: 'Owl', a_cat: 'Cat', a_bear: 'Bear', a_capybara: 'Capybara', a_conejo: 'Rabbit', a_panda: 'Panda', a_nutria: 'Otter' }, - zh: { p_diadema: '发箍', p_mono: '大蝴蝶结', p_perlas: '珍珠项链', p_vestido: '碎花裙', p_gala: '礼服裙', p_falda: '短裙', p_zapatillas: '运动鞋', p_botas: '靴子', p_sandalias: '凉鞋', e_pies: '脚', c_sandia: '西瓜', c_galleta: '饼干', c_helado: '冰淇淋', c_te: '花草茶', deseo_sandia: '我想尝尝西瓜!', deseo_galleta: '来块饼干就太好了!', deseo_helado: '我好想吃冰淇淋!', deseo_te: '我们来喝杯花草茶吧?', verDeseo: '看看它的心愿', acariciar: '抚摸', cepillar: '梳毛', jugar: '玩耍', c_fruta: '水果', c_zanahoria: '胡萝卜', c_pastel: '蛋糕', c_jabon: '泡泡皂', cantidad: '×{n}', vinculo: '亲密度:第 {n} 级,共 {m} 级', deseoCumplido: '谢谢!我好喜欢。', deseo_fruta: '我想尝尝水果!', deseo_zanahoria: '我想尝尝胡萝卜!', deseo_pastel: '来块蛋糕就太好了!', deseo_banio: '我想泡个泡泡浴!', deseo_ropa: '穿点新衣服一定很好看!', deseo_jugar: '我们来玩一会儿吧!', deseo_caricias: '可以摸摸我吗?', + zh: { gana_comer: '看你玩得这么开心,我也想吃点东西啦!', gana_jugar: '看你玩,我也想玩啦!', gana_banio: '好想泡个泡泡浴呀!', ganaNivel_comer: '想吃东西:{n}/3', ganaNivel_jugar: '想玩耍:{n}/3', ganaNivel_banio: '想洗澡:{n}/3', p_diadema: '发箍', p_mono: '大蝴蝶结', p_perlas: '珍珠项链', p_vestido: '碎花裙', p_gala: '礼服裙', p_falda: '短裙', p_zapatillas: '运动鞋', p_botas: '靴子', p_sandalias: '凉鞋', e_pies: '脚', c_sandia: '西瓜', c_galleta: '饼干', c_helado: '冰淇淋', c_te: '花草茶', deseo_sandia: '我想尝尝西瓜!', deseo_galleta: '来块饼干就太好了!', deseo_helado: '我好想吃冰淇淋!', deseo_te: '我们来喝杯花草茶吧?', verDeseo: '看看它的心愿', acariciar: '抚摸', cepillar: '梳毛', jugar: '玩耍', c_fruta: '水果', c_zanahoria: '胡萝卜', c_pastel: '蛋糕', c_jabon: '泡泡皂', cantidad: '×{n}', vinculo: '亲密度:第 {n} 级,共 {m} 级', deseoCumplido: '谢谢!我好喜欢。', deseo_fruta: '我想尝尝水果!', deseo_zanahoria: '我想尝尝胡萝卜!', deseo_pastel: '来块蛋糕就太好了!', deseo_banio: '我想泡个泡泡浴!', deseo_ropa: '穿点新衣服一定很好看!', deseo_jugar: '我们来玩一会儿吧!', deseo_caricias: '可以摸摸我吗?', p_gorra: '鸭舌帽', p_corona: '王冠', p_lentes: '眼镜', p_lazo: '蝴蝶结', p_panuelo: '领巾', p_sueter: '毛衣', p_capa: '斗篷', e_cabeza: '头部', e_cuello: '颈部', e_cuerpo: '身体', ninguna: '不穿', ariaPrecio: '{nombre},{precio}', ariaTuyo: '{nombre},已拥有', ariaEnUso: '{nombre},使用中', ariaPerfil: '{nombre},在个人资料中解锁', d_natural: '自然', d_medianoche: '午夜', d_arcoiris: '柔和彩虹', d_manchitas: '斑点', d_rayas: '条纹', d_dorado: '金色', desbloquear: '花 {n} 金币解锁?', desbloquearBtn: '解锁', faltan: '还差 {n} 金币', jugarParaGanar: '玩一局就能赚到。', enPerfil: '在个人资料中解锁', precio: '{n} 金币', tuyo: '已拥有', monedas: '金币', animales: '动物', colores: '颜色', ropa: '服装', cuidado: '照顾', cargando: '正在准备你的伙伴……', vacio: '这里还没有东西。', sinMonedas: '你还没有金币。玩一局就能赚到。', equipado: '使用中', a_fox: '狐狸', a_owl: '猫头鹰', a_cat: '猫', a_bear: '熊', a_capybara: '水豚', a_conejo: '兔子', a_panda: '熊猫', a_nutria: '水獭' }, - de: { p_diadema: 'Haarreif', p_mono: 'Große Schleife', p_perlas: 'Perlen', p_vestido: 'Blumen\u00ADkleid', p_gala: 'Fest\u00ADkleid', p_falda: 'Rock', p_zapatillas: 'Turn\u00ADschuhe', p_botas: 'Stiefel', p_sandalias: 'Sandalen', e_pies: 'Füße', c_sandia: 'Wasser\u00ADmelone', c_galleta: 'Keks', c_helado: 'Eis', c_te: 'Kräuter\u00ADtee', deseo_sandia: 'Ich würde gern Wassermelone probieren!', deseo_galleta: 'Ein Keks wäre toll!', deseo_helado: 'Ein Eis wäre herrlich!', deseo_te: 'Trinken wir einen Kräutertee?', verDeseo: 'Seinen Wunsch zeigen', acariciar: 'Streicheln', cepillar: 'Bürsten', jugar: 'Spielen', c_fruta: 'Obst', c_zanahoria: 'Karotte', c_pastel: 'Kuchen', c_jabon: 'Schaumseife', cantidad: '×{n}', vinculo: 'Bindung: Stufe {n} von {m}', deseoCumplido: 'Danke! Das hat mir gefallen.', deseo_fruta: 'Ich würde gern ein Stück Obst probieren!', deseo_zanahoria: 'Ich würde gern die Karotte probieren!', deseo_pastel: 'Ein Kuchen wäre toll!', deseo_banio: 'Ein Schaumbad wäre herrlich!', deseo_ropa: 'Etwas Neues zum Anziehen stünde mir gut!', deseo_jugar: 'Lass uns ein bisschen spielen!', deseo_caricias: 'Streichelst du mich ein wenig?', + de: { gana_comer: 'Vom Zuschauen hab ich Appetit bekommen!', gana_jugar: 'Beim Zuschauen will ich auch spielen!', gana_banio: 'Ein Schaumbad wäre jetzt schön!', ganaNivel_comer: 'Lust auf Essen: {n} von 3', ganaNivel_jugar: 'Lust zu spielen: {n} von 3', ganaNivel_banio: 'Lust auf ein Bad: {n} von 3', p_diadema: 'Haarreif', p_mono: 'Große Schleife', p_perlas: 'Perlen', p_vestido: 'Blumen\u00ADkleid', p_gala: 'Fest\u00ADkleid', p_falda: 'Rock', p_zapatillas: 'Turn\u00ADschuhe', p_botas: 'Stiefel', p_sandalias: 'Sandalen', e_pies: 'Füße', c_sandia: 'Wasser\u00ADmelone', c_galleta: 'Keks', c_helado: 'Eis', c_te: 'Kräuter\u00ADtee', deseo_sandia: 'Ich würde gern Wassermelone probieren!', deseo_galleta: 'Ein Keks wäre toll!', deseo_helado: 'Ein Eis wäre herrlich!', deseo_te: 'Trinken wir einen Kräutertee?', verDeseo: 'Seinen Wunsch zeigen', acariciar: 'Streicheln', cepillar: 'Bürsten', jugar: 'Spielen', c_fruta: 'Obst', c_zanahoria: 'Karotte', c_pastel: 'Kuchen', c_jabon: 'Schaumseife', cantidad: '×{n}', vinculo: 'Bindung: Stufe {n} von {m}', deseoCumplido: 'Danke! Das hat mir gefallen.', deseo_fruta: 'Ich würde gern ein Stück Obst probieren!', deseo_zanahoria: 'Ich würde gern die Karotte probieren!', deseo_pastel: 'Ein Kuchen wäre toll!', deseo_banio: 'Ein Schaumbad wäre herrlich!', deseo_ropa: 'Etwas Neues zum Anziehen stünde mir gut!', deseo_jugar: 'Lass uns ein bisschen spielen!', deseo_caricias: 'Streichelst du mich ein wenig?', p_gorra: 'Kappe', p_corona: 'Krone', p_lentes: 'Brille', p_lazo: 'Schleife', p_panuelo: 'Halstuch', p_sueter: 'Pullover', p_capa: 'Umhang', e_cabeza: 'Kopf', e_cuello: 'Hals', e_cuerpo: 'Körper', ninguna: 'Nichts', ariaPrecio: '{nombre}, {precio}', ariaTuyo: '{nombre}, gehört dir', ariaEnUso: '{nombre}, in Gebrauch', ariaPerfil: '{nombre}, im Profil freischaltbar', d_natural: 'Natur', d_medianoche: 'Mitternacht', d_arcoiris: 'Sanfter Regenbogen', d_manchitas: 'Tupfen', d_rayas: 'Streifen', d_dorado: 'Golden', desbloquear: 'Für {n} Münzen freischalten?', desbloquearBtn: 'Freischalten', faltan: 'Dir fehlen {n} Münzen', jugarParaGanar: 'Spiel eine Runde, um welche zu verdienen.', enPerfil: 'Im Profil freischaltbar', precio: '{n} Münzen', tuyo: 'Deins', monedas: 'Münzen', animales: 'Tiere', colores: 'Farben', ropa: 'Kleidung', cuidado: 'Pflege', cargando: 'Dein Begleiter wird vorbereitet …', vacio: 'Hier ist noch nichts.', sinMonedas: 'Du hast noch keine Münzen. Spiel eine Runde, um welche zu verdienen.', equipado: 'In Gebrauch', a_fox: 'Fuchs', a_owl: 'Eule', a_cat: 'Katze', a_bear: 'Bär', a_capybara: 'Wasser\u00ADschwein', a_conejo: 'Hase', a_panda: 'Panda', a_nutria: 'Otter' }, - es: { p_diadema: 'Diadema', p_mono: 'Moño', p_perlas: 'Perlas', p_vestido: 'Vestido de flores', p_gala: 'Vestido de fiesta', p_falda: 'Falda', p_zapatillas: 'Zapatillas', p_botas: 'Botas', p_sandalias: 'Sandalias', e_pies: 'Pies', c_sandia: 'Sandía', c_galleta: 'Galleta', c_helado: 'Helado', c_te: 'Té de hierbas', deseo_sandia: '¡Me gustaría probar la sandía!', deseo_galleta: '¡Qué rica sería una galleta!', deseo_helado: '¡Me encantaría un helado!', deseo_te: '¿Tomamos un té de hierbas?', verDeseo: 'Ver su deseo', acariciar: 'Acariciar', cepillar: 'Cepillar', jugar: 'Jugar', c_fruta: 'Fruta', c_zanahoria: 'Zanahoria', c_pastel: 'Pastel', c_jabon: 'Jabón de burbujas', cantidad: '×{n}', vinculo: 'Vínculo: nivel {n} de {m}', deseoCumplido: '¡Gracias! Me encantó.', deseo_fruta: '¡Me gustaría probar una fruta!', deseo_zanahoria: '¡Me gustaría probar la zanahoria!', deseo_pastel: '¡Qué rico sería un pastel!', deseo_banio: '¡Me encantaría un baño de burbujas!', deseo_ropa: '¡Me vería bien con algo nuevo!', deseo_jugar: '¡Juguemos un rato!', deseo_caricias: '¿Me das unas caricias?', + es: { gana_comer: '¡Uf, tanto mirarte jugar me abrió el apetito!', gana_jugar: '¡Mirarte jugar me dio ganas de jugar a mí!', gana_banio: '¡Qué bien me vendría un baño de burbujas!', ganaNivel_comer: 'Ganas de comer: {n} de 3', ganaNivel_jugar: 'Ganas de jugar: {n} de 3', ganaNivel_banio: 'Ganas de un baño: {n} de 3', p_diadema: 'Diadema', p_mono: 'Moño', p_perlas: 'Perlas', p_vestido: 'Vestido de flores', p_gala: 'Vestido de fiesta', p_falda: 'Falda', p_zapatillas: 'Zapatillas', p_botas: 'Botas', p_sandalias: 'Sandalias', e_pies: 'Pies', c_sandia: 'Sandía', c_galleta: 'Galleta', c_helado: 'Helado', c_te: 'Té de hierbas', deseo_sandia: '¡Me gustaría probar la sandía!', deseo_galleta: '¡Qué rica sería una galleta!', deseo_helado: '¡Me encantaría un helado!', deseo_te: '¿Tomamos un té de hierbas?', verDeseo: 'Ver su deseo', acariciar: 'Acariciar', cepillar: 'Cepillar', jugar: 'Jugar', c_fruta: 'Fruta', c_zanahoria: 'Zanahoria', c_pastel: 'Pastel', c_jabon: 'Jabón de burbujas', cantidad: '×{n}', vinculo: 'Vínculo: nivel {n} de {m}', deseoCumplido: '¡Gracias! Me encantó.', deseo_fruta: '¡Me gustaría probar una fruta!', deseo_zanahoria: '¡Me gustaría probar la zanahoria!', deseo_pastel: '¡Qué rico sería un pastel!', deseo_banio: '¡Me encantaría un baño de burbujas!', deseo_ropa: '¡Me vería bien con algo nuevo!', deseo_jugar: '¡Juguemos un rato!', deseo_caricias: '¿Me das unas caricias?', p_gorra: 'Gorra', p_corona: 'Corona', p_lentes: 'Lentes', p_lazo: 'Lazo', p_panuelo: 'Pañuelo', p_sueter: 'Suéter', p_capa: 'Capa', e_cabeza: 'Cabeza', e_cuello: 'Cuello', e_cuerpo: 'Cuerpo', ninguna: 'Ninguna', ariaPrecio: '{nombre}, {precio}', ariaTuyo: '{nombre}, tuyo', ariaEnUso: '{nombre}, en uso', ariaPerfil: '{nombre}, se desbloquea en tu perfil', d_natural: 'Natural', d_medianoche: 'Medianoche', d_arcoiris: 'Arcoíris suave', d_manchitas: 'Manchitas', d_rayas: 'Rayas', d_dorado: 'Dorado', desbloquear: '¿Desbloquear por {n} monedas?', desbloquearBtn: 'Desbloquear', faltan: 'Te faltan {n} monedas', jugarParaGanar: 'Juega una partida para ganarlas.', enPerfil: 'Se desbloquea en tu perfil', precio: '{n} monedas', tuyo: 'Tuyo', @@ -147,7 +153,9 @@ nucleo: function () { return { unlocks: C.state.unlocks, mascot: C.state.settings.mascot }; } }); var ARTE = window.CompaneroArte; - var K = window.CompaneroCuidado.crear({ store: ctx.store, economia: E }); + // the games finished in the whole app, read from the core and never written (#39) + var K = window.CompaneroCuidado.crear({ store: ctx.store, economia: E, + partidas: function () { var st = C.stats && C.stats.get ? C.stats.get() : null; return st && st.plays; } }); K.visitar(); // each opening is a visit: one kind wish, never the last visit's var CONSUMIBLES = window.CompaneroEconomia.CATALOGO.filter(function (a) { return a.tipo === 'consumible'; }).map(function (a) { return a.id; }); // the face of the moment: the opening one, or a short-lived one after something happens @@ -204,6 +212,13 @@ compa.appendChild(globo); compa.appendChild(lienzo); compa.appendChild(vinculo); + var ganasEl = el('div', { class: 'cp-ganas' }); + compa.appendChild(ganasEl); + var ICONO_GANA = { + comer: '', + jugar: '', + banio: '' + }; var raiz = el('div', { class: 'cp' }, [ el('div', { class: 'cp-escenario' }, [estado, compa]), sinMonedas, @@ -224,12 +239,22 @@ lienzo.innerHTML = dibujo; compa.dataset.expresion = cara; - var deseo = K.deseo(); + var deseo = K.deseo(), fuerte = K.ganaFuerte(); if (deseo) { globo.dataset.deseo = deseo.id; globo.dataset.cumplido = deseo.cumplido ? '1' : '0'; globo.textContent = deseo.cumplido ? C.t('deseoCumplido') : C.t('deseo_' + deseo.id); } + // a gana at its top speaks instead of the visit's wish, which stays as it is + globo.dataset.gana = fuerte || ''; + if (fuerte && !(deseo && deseo.cumplido)) globo.textContent = C.t('gana_' + fuerte); + var gs = K.ganas(); + ganasEl.textContent = ''; + Object.keys(gs).forEach(function (g) { + var x = el('span', { class: 'cp-gana', 'data-gana': g, 'data-nivel': String(gs[g]), role: 'img', 'aria-label': C.t('ganaNivel_' + g, { n: C.num(gs[g]) }), html: ICONO_GANA[g] }); + for (var i = 0; i < 3; i++) x.appendChild(el('span', { class: 'cp-punto' + (i < gs[g] ? ' cp-punto--on' : '') })); + ganasEl.appendChild(x); + }); var v = K.vinculo(); vinculo.dataset.vinculo = String(v.puntos); vinculo.textContent = '';