Repository navigation
Conversation
…Town Records the decisions from the design session, the milestone plan with task checkboxes, and a handoff file. A SessionStart hook injects the handoff and open tasks; a Stop hook asks for a handoff update when code commits are newer than the last one.
- scripts/assets/fetch.mjs finds the private art (sibling checkout, ASSETS_DIR, or token clone) and falls back to placeholders, refusing placeholders in production builds; build runs it first - phaser 4, inkjs, pngjs, vitest, Playwright; @types/node 24 - pnpm lint uses the ESLint CLI (next lint is gone in Next 16) and forbids Next.js/React imports under game/ - vitest unit tests and a Playwright smoke test of / on desktop and phone
…t 16.3.6 - / is a static, server-rendered title screen (SVG fjord at dusk) that boots Phaser behind it; Start enters the world. The old page moved to /legacy, /journal is a stub. - game/: framework-free boot, low-res canvas scaled by whole device pixels (viewport.ts, unit tested), Boot/Preload/World scenes. - pnpm game:dev: standalone esbuild harness with live reload. - Next 16.2 canary -> 16.3.6, React 19.3, React Compiler 1.0. Turbopack's disk cache is off on WSL-mounted drives (fsync fails there, which caused the dev panic); removed the dropped viewTransition option. - lib/github.ts: pinned-repo scrape falls back to [] on network errors like the other fetchers instead of failing the page.
The repository previously had no licence. Code is now GPL-3.0-or-later; the README lists what is not covered (Pangram Pangram fonts, images, LimeZu art, Geist fonts under OFL).
- scripts/assets/build.mjs writes public/game/ (gitignored): characters composited from LimeZu generator layers with colour swaps (datagutt matches the avatar), or placeholder figures without the art repo; a generated greybox tileset; Tiled .tmj maps; Geist Pixel as a 1-bit bitmap font. - world/: a small map-drawing API and the greybox town and house, written as code (the seed of the M3 generator). - Lockfile for sharp, opentype.js and wawoff2 (the previous commit's package.json change landed without it).
Tiled loading with tile-property collision, tile-locked movement with turn-in-place and chained steps, sign and NPC interaction with a simple dialogue box, doors between maps, tap/click-to-move with A* (straight-run preference) and walk-to-talk, unified keyboard/gamepad/pointer input, a versioned localStorage save that survives hostile storage, and ?at= deep links that skip the title. Pure logic is unit tested.
Plays the real page: deep link, doors both ways, talking to datagutt, continuing from a save. CI runs lint, typecheck, unit tests, a placeholder-art build and the e2e suite.
data/ moves to content/ (with slug project ids) and gains profile, socials and places. places.ts is the whole town content map; a test checks every project and job has exactly one place. The legacy components and lib/github read from it, and the game uses it for ?at= deep links. Discord stays out of socials, as on the old site.
It routes server fetch() calls to a Playwright test proxy that is not running, so every GitHub fetch failed with 'other side closed' or an HTTP parser error (seen in dev on Next 16.3).
lib/world-state.ts combines pinned repos, stats and contributions; a cached server component embeds it in / as JSON and game/live reads it with a safe fallback. lib/github.ts moves from unstable_cache to 'use cache', logs failures, and caches failed fetches for minutes instead of an hour. / stays prerendered and revalidates hourly.
Dialogue is written in Ink under game/dialogue/ink. The asset build
compiles it, declares every external from one registry
(game/dialogue/externals.ts) and fails on unknown literal ids such as
project_desc("irlsever") or NPC knots that don't exist. At runtime a
DialogueRunner plays knots beat by beat, facts come from content/ and
live data, and visit counts persist in the save. The dialogue box gains
a choice list (keyboard, gamepad, tap). Ferryman and datagutt get
drafted conversations.
Picking one choice used to hide the others for good: the next visit went straight to the repeat greeting. NPCs now loop back to a topics hub with once-only questions and a sticky goodbye, so every fact stays reachable across visits. main.ink documents the pattern.
The dialogue box is now a nine-slice of LimeZu's wood-rimmed parchment frame (a drawn stand-in with the same palette in placeholder mode), with a matching name tab, dark ink text, and short pauses after punctuation.
Portraits are composited at build time from the portrait generator layers (with the same colour swaps as the sprite, so datagutt keeps his yellow hair and black glasses), cropped to the head area and shown at 2x. The mouth moves while text types; # nod and # shake Ink tags play a gesture first. Placeholder mode draws a stand-in face.
Undertale-style blips synthesised with Web Audio as text appears, with a voice per character (the ferryman is low and slow, datagutt higher and quicker). They play through Phaser's sound output, so the saved mute applies. The ?debug readout now refreshes every frame, including during dialogue, and counts blips for the e2e test.
Ink lines can carry # link: project <id>, social <id> or email tags, validated at build time against content. After such a line the game asks "Open github.com?" and opens it in a new tab. Because browsers block popups outside a real user event and Phaser handles input a frame later, LinkOpener opens the tab from a window listener during the actual key press or tap. Direction presses are now queued so fast presses aren't lost in menus.
Eleven NPCs with names, places, one-line personalities and voices in game/npcs.ts, and sprite recipes in the asset manifest. Portraits are now derived from the sprite layers (the generators share numbering), so each NPC is defined once. Awaiting the user's review.
Drafted Ink for Sunniva (Guac.tv), Kjell (IRLServer), Randi (Donate.chat), Ida (Nettbureau), Bjørn (Indre Østfold Data), Tor (tech stack), Solveig (live pinned repos), Ola (live stats) and Liv (contact and socials), all using the topics pattern and content functions. Every NPC now waits in the greybox town near their future building. Tests check that dialogue mentions every project, job, skill category, social link and live stat, and walk every conversation to the end with and without live data. Coffee jokes became energy drink jokes, as is only accurate.
Finishing a conversation with a place's main NPC stamps that place in the passport: a banner drops in, a synthesised rubber-stamp thunk plays and the camera shakes a little (not with reduced motion). The passport page lists all ten places. Progress now lives in one Progress object shared through the registry and written to the save, instead of re-reading the save on every step.
Enter, gamepad Start or the on-screen Menu button opens a menu with the passport, the plain-text Journal, settings (sound, reduced motion, both saved) and credits, including the LimeZu attribution the art licence requires. It works with keys, gamepad and taps, with soft menu ticks. Completes M2.
…layers and packed tileset (M3.1) pnpm world:gen writes world/maps/*.tmj from world/gen/maps, keeping manual_* layers. Tile ids live in the append-only world/tile-ids.json; the build packs them into one tileset from the art, or from coarse committed colour sketches in placeholder mode. The game reads any tileset by name, hidden collision layers and manual object layers. ?debug&map=<id> opens a map in progress.
…rest (M3.3, M3.7 wip) Prefabs for villas (one recoloured falu red for datagutt's house), civic buildings, farm, smithy, office, post office, kiosk and trees; landscape helpers for plateaus with stairs, piers, forests and buildings with doors. Renders can carry a labelled grid. The tileset now holds 16k tiles and world:gen --prune drops dead registry entries.
…for M3 (M3.4, M3.7 draft)
…tart) The validator runs on every world:gen and world:check. It caught the office and smithy blocking their front steps, which are now walkable.
Approved by the user. The greybox town is retired (the greybox house stays until its interior); spawns and e2e follow the new layout. Doors are wired only to interiors that exist, and the asset build fails on doors to unknown maps or spawns. Pier fixes from review: the camping sheet's baked-in water is cleared (campingDry) so the sea shows under the pier ends, the life ring is drawn whole, and the rowboat crop no longer catches half a neighbouring boat.
The engine ships its English UI copy by key; a game overrides any key in content/strings.json, and an unknown key fails the content build. The town's own one-off lines move into Ink. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
…neric world The world scene gets a plugin API: object types, usables, prompts, talk and stamp hooks, menu items, takeovers and debug fields. The cat, the cabinets, the live GitHub objects, the ferry intro, the finale, the live Thomas NPC and the Journal link become plugins; summit, the edge lines and the passport achievement become declarative triggers. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
createGame() takes the game's config, compiled content, live data, link resolver, dialogue functions and plugins. Content reaches scenes and ui through a GameData object on the services instead of module imports of the app. The weather types move into the runtime so that kai-live can depend on it without a cycle. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
…le plugins presenceNpc takes its places, routes, rules, words, story-night spot and mock presences from content/presence.json, so any game can have a live developer walking its town. githubObjects draws the contribution field and the repo shelf. The debug hook becomes window.__kai, and an e2e test checks that saves from before the split still load. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
… world socket useKaiGame and useMenuKeys carry the title screen's lifecycle and keyboard; the site keeps its look. LiveDataScript embeds the live payload, worldSocketHandler serves the ghost rooms, and cached wrappers sit over kai-live's GitHub and weather fetchers, which no longer know about Next. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
The runtime decides that a conversation reached an NPC's own knot by its visit count, but Ink only counts knots the story refers to. Fjord Town's knots happened to be referenced; a new game's were not, so talking to its NPCs never gave a stamp. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
…o.json An app with its own turbo.json was taken for the repository root, so the local art checkout next to the repository was not found. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
One map, one NPC, one conversation, two triggers and placeholder art without the token, played by a smoke test in the dev harness. It proves the engine needs nothing from datagutt, and game 2 starts as a copy. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
packages never import apps, runtime code never imports the build-time packages or schemas (except as types), and the runtime stays free of next and react. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
…fjord town also lint that the map toolkit and the asset build never import an art adapter. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
matches the art repository, where art for one game now lives under games/<id>/. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
the finale only started at the moment of the last stamp, so anything that missed that moment lost it for good. it now resumes on the next map while the passport is full and the finale unfinished, and waits until the world is free instead of a fixed timer. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
…game or plugin the engine hardcoded fjord town's cat, arcade, crops and books objects, so a new object type meant an engine change. a descriptor (defineMapObject) now gives an object's properties, size and placement; the map checks follow the placement. the build reads a game's types from MAP_OBJECTS in its maps module, the runtime from the plugins that place them. the generated maps are byte-identical. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
a sprite object plays a kai.json strip at a tile plus a pixel offset, drawn among the characters by its bottom edge or on the ground or roof layer, and limited to seasons or daylight when asked. MapCanvas.reserve keeps the collision of the art a sprite replaces, renders draw each sprite's first frame, and the build rejects unknown sprite names, layers, seasons and times of day. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
the windmill's blades turn, the boats bob, the fountain runs, and the fireplace, treadmill and escalator move, from limezu's own strips. pigeons, gulls, buoys, butterflies in the warm half of the year and a campfire at the lookout fill places the maps already have, keeping to the daylight where it suits them. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
kai.json critters names which sprites are animals and how each species gets away when the player comes close: a bird flies off out of sight, a gull waddles a few walkable tiles, a butterfly drifts. a built-in plugin runs them and brings them back once the player has gone; with reduced motion they stay put. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
the square's pigeons are hooded crows now, since limezu's crows have take-off and flying frames. gulls and butterflies get away too, and the site reads the validated config from .kai/ like the game. Claude-Session: https://claude.ai/code/session_014PUagkkCzJQPfsvmUWTdU5
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
its a game