Something went wrong. Try again.
a tiny pet with a GPT-2 brain, living in your browser
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100import { FLOOR, W as LW } from "../scene.ts";import type { Disturbance } from "../types.ts";import * as Wd from "../world.ts";import { MIN, WANT_ICON } from "./config.ts";import { pushLine } from "./conversation.ts";import { logToday, rollDay } from "./simulation.ts";import { store, 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;}// Disturbances are body rolls. Offers wake only when the brain accepts.// This short-lived counter resets on waking, a new sleep, reload, or 5 min quiet.export const disturbances = { count: 0, last: 0 };let wakeRoll = Math.random;export function resetDisturbances() { disturbances.count = 0; disturbances.last = 0;}export function disturbanceCount(now = Date.now()) { return now - disturbances.last >= 5 * MIN ? 0 : disturbances.count;}export function disturb(kind: Disturbance) { if (!store.state.pet || !store.state.asleep) return false; disturbances.count = disturbanceCount() + 1; disturbances.last = Date.now(); const wakes = wakeRoll() < Wd.wakeChance(store.state.stats.energy, kind, disturbances.count); if (wakes) wake(); return wakes;}export function wake() { if (!store.state.pet || !store.state.asleep) return; store.state.asleep = false; store.state.wokeAt = Date.now(); resetDisturbances(); view.dream = null; view.anim = null; pushLine(Wd.events.woke(store.state.pet!.name));}export function dream(item: string) { view.dream = item; view.dreamUntil = performance.now() + 25e3; rollDay(); const entry = `dreamed of ${Wd.dreamThing(item)}`; if (store.state.today.log.includes(entry)) return; // Keep at most two dreams, so frequent dreams cannot displace the whole day. const old = store.state.today.log.filter((x) => x.startsWith("dreamed of ")); if (old.length >= 2) store.state.today.log.splice(store.state.today.log.indexOf(old[0]), 1); logToday(entry);}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;}
export function setWakeRoll(fn: (() => number) | null) { wakeRoll = typeof fn === "function" ? fn : Math.random;}