Something went wrong. Try again.
a tiny pet with a GPT-2 brain, living in your browser
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106import { L, type Effect } from "../core.ts";import { FLOOR, W as LW } from "../scene.ts";import { WANT_ICON } from "./config.ts";import { store, toast, view } from "./store.ts";import { clamp, rand } from "./utils.ts";
// Animations queue, so "[emote:happy] [emote:dance]" plays both in order.export function animate(name: string, ms = 1800, now = false) { if (now || !view.anim) view.anim = { name, t0: performance.now(), until: performance.now() + ms }; else if (view.queue.length < 4) view.queue.push({ name, ms });}export function nextAnim(t: number) { // A sleeping pet does not act out anything left over from before it dozed off. if (store.state.asleep) { view.queue = []; if (!["sleepy", "stretch"].includes(view.anim?.name || "")) view.anim = null; } if (view.anim && t > view.anim.until) view.anim = null; if (!view.anim && view.queue.length) { const a = view.queue.shift()!; view.anim = { name: a.name, t0: t, until: t + a.ms }; }}export function float(item: string, n = 3) { for (let i = 0; i < n; i++) view.effects.push({ kind: "float", item, x: view.x - 6 + Math.random() * 12, y: FLOOR - 18, t0: performance.now() + i * 220, until: performance.now() + 2600 + i * 220, });}export function bubbles(n = 10) { for (let i = 0; i < n; i++) view.effects.push({ kind: "bubble", x: view.x - 10 + Math.random() * 20, y: FLOOR - Math.random() * 6, t0: performance.now() + i * 120, until: performance.now() + 2400 + i * 120, });}export function want(kind: string, ms = 25e3) { view.want = WANT_ICON[kind]; view.wantUntil = performance.now() + ms;}export function walk(dir: string) { const d = dir === "left" ? -1 : 1; view.target = clamp(view.x + d * rand(14, 28), 14, LW - 14); view.facing = d;}
// Effects come from the shared engine, locally or in a server snapshot. The// save has already changed; these only animate the LCD and tell the owner.export function playEffect(e: Effect) { const pet = store.state.pet?.name || "Your pet", asleep = store.state.asleep; if (e.kind === "emote" && L.EMOTES.includes(e.arg)) { animate(e.arg, e.arg === "spin" ? 1300 : 2000); if (e.arg === "love" || e.arg === "blush") float("smallheart", 3); if (e.arg === "dance") float("note", 3); if (e.arg === "wow") float("bang", 1); } else if (e.kind === "walk" && L.WALKS.includes(e.arg) && !asleep) walk(e.arg); else if (e.kind === "want" && L.WANTS.includes(e.arg)) want(e.arg); else if (e.kind === "dream" && L.DREAMS.includes(e.arg) && asleep) { view.dream = e.arg; view.dreamUntil = performance.now() + 25e3; } else if (e.kind === "eat" && L.FOODS.includes(e.arg)) { animate("eat", 1700, true); view.effects.push({ kind: "food", item: e.arg, t0: performance.now(), until: performance.now() + 1700, }); } else if (e.kind === "bath") bubbles(14); // The squish is physics; the feelings come from the brain. else if (e.kind === "pat") animate(e.arg === "asleep" ? "stretch" : "pat", 600, true); else if (e.kind === "sparkle") float("sparkle", 3); else if (e.kind === "gift" && L.GIFTS.includes(e.arg)) { float(e.arg, 1); toast(`${pet} gave you a ${e.arg}!`, e.arg); } else if (e.kind === "remember") toast(`${pet} will remember: ${e.arg}`); else if (e.kind === "grew") toast(`${pet} grew into a ${e.arg}!`); else if (e.kind === "sick") toast(`${pet} is feeling sick. Medicine helps.`); else if (e.kind === "diary") toast( e.arg === store.state.today.date ? `${pet} wrote in the diary` : `${pet} wrote the page for ${e.arg}`, "book", ); else if (e.kind === "sleep") { view.target = view.x; view.want = null; view.dream = null; view.queue = []; animate("sleepy", 1500, true); } else if (e.kind === "woke") { view.dream = null; view.anim = null; }}