diff --git a/README.md b/README.md index a7fa285..8127720 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,12 @@ The contract is simultaneous rounds: every seat that needs input submits, then the round resolves; turn-at-a-time games fit as the case where one seat needs input at a time. A game of single commands with out-of-turn interruptions, Wiz-War's shape, does not fit and keeps its own server. + +## The family + +`family.json` lists every game of the Kestrel's Nest: slug, name, address, +one-line pitch, player count and origin. Each game ships a copy as +`static/family.json` (the deploy script takes the kit's latest when the kit +is beside the game) and its about panel lists the others under *More games +at the Kestrel's Nest*. Add a game here, then deploy the games; nothing +else needs to know. diff --git a/family.json b/family.json new file mode 100644 index 0000000..10d4a2e --- /dev/null +++ b/family.json @@ -0,0 +1,21 @@ +{ + "family": "the Kestrel's Nest", + "games": [ + { + "slug": "wizwar", + "name": "Wiz-War", + "url": "https://wizwar.kestrelsnest.social", + "pitch": "Steal treasure. Blast through walls. Outwit your friends.", + "players": "2 to 6", + "origin": "Tom Jolly's board game, sixth edition, 1993" + }, + { + "slug": "waving-hands", + "name": "Waving Hands", + "url": "https://hands.kestrelsnest.social", + "pitch": "Duelling wizards. Two hands each. The right run of gestures is a spell.", + "players": "2 to 12", + "origin": "Richard Bartle's pencil-and-paper duel, 1977" + } + ] +} diff --git a/template/deploy/deploy.sh b/template/deploy/deploy.sh index e962efb..d7cb857 100755 --- a/template/deploy/deploy.sh +++ b/template/deploy/deploy.sh @@ -4,6 +4,10 @@ set -euo pipefail HOST="${1:?usage: deploy.sh }" +# The family list is the kit's; a game deploys with the kit's latest copy when the kit is beside it. +KIT_FAMILY="$(dirname "$0")/../../game-kit/family.json" +[ -f "$KIT_FAMILY" ] && cp "$KIT_FAMILY" static/family.json + npm run check npm run check:server npm test diff --git a/template/src/lib/components/Hall.svelte b/template/src/lib/components/Hall.svelte index 9bcc463..afe4165 100644 --- a/template/src/lib/components/Hall.svelte +++ b/template/src/lib/components/Hall.svelte @@ -3,6 +3,7 @@ // bot, open a table, join by code, the ledger of your tables, and the // about panel with the rules, the guide and a way to reach the keeper. import { goto } from '$app/navigation'; + import { otherGames, type FamilyGame } from '$lib/family'; import { allSeats, doubtSeat, forgetSeat, rememberSeat, ServerError, trustSeat, type Report } from '$lib/net/client'; import { api, NAME_MAX, playerName, rememberName, Room } from '$lib/net/room.svelte'; import { unreadTalk } from '$lib/net/talk'; @@ -17,6 +18,11 @@ let seats = $state(allSeats()); let games = $state | null>>({}); let aboutOpen = $state(false); + /** The other games of the Kestrel's Nest, for the about panel. */ + let family = $state([]); + $effect(() => { + void otherGames('__SLUG__').then((g) => (family = g)); + }); /** Reports this browser's seats have filed, with the keeper's replies. */ let reports = $state([]); let answers = $state>({}); @@ -228,6 +234,14 @@

It is free and keeps no accounts. A game between people lives on a small server as an append-only ledger of moves, so it can be replayed from its first move, and each player is shown only what the rules let them see.

Send word

A rule read wrong, a bug, a game you would like to tell of: write to eric@ericwagoner.com, @kestrelsnest.social on Bluesky, or @eric@toots.kestrelsnest.social on Mastodon. The keeper of this hall roosts at kestrelsnest.social.

+ {#if family.length} +

More games at the Kestrel's Nest

+
    + {#each family as g (g.slug)} +
  • {g.name}: {g.pitch} {g.players} players; {g.origin}.
  • + {/each} +
+ {/if}

__NAME__ is its designer's. This page was made by Eric and a very enthusiastic AI, 2026.

@@ -529,4 +543,12 @@ font-size: 0.85rem; color: var(--bone-faint); } + .family { + margin: 0.4rem 0 0.8rem 1.2rem; + padding: 0; + } + + .family li + li { + margin-top: 0.3rem; + } diff --git a/template/src/lib/family.ts b/template/src/lib/family.ts new file mode 100644 index 0000000..719a5e9 --- /dev/null +++ b/template/src/lib/family.ts @@ -0,0 +1,23 @@ +// The other games of the Kestrel's Nest, from the family list the kit keeps +// (family.json, one copy shipped with every game), so a player who liked one +// table can find the next. This game is left out of its own list. + +export interface FamilyGame { + slug: string; + name: string; + url: string; + pitch: string; + players: string; + origin: string; +} + +export async function otherGames(self: string): Promise { + try { + const res = await fetch('/family.json', { cache: 'no-cache' }); + if (!res.ok) return []; + const data = (await res.json()) as { games?: FamilyGame[] }; + return (data.games ?? []).filter((g) => g.slug !== self); + } catch { + return []; + } +} diff --git a/template/static/family.json b/template/static/family.json new file mode 100644 index 0000000..10d4a2e --- /dev/null +++ b/template/static/family.json @@ -0,0 +1,21 @@ +{ + "family": "the Kestrel's Nest", + "games": [ + { + "slug": "wizwar", + "name": "Wiz-War", + "url": "https://wizwar.kestrelsnest.social", + "pitch": "Steal treasure. Blast through walls. Outwit your friends.", + "players": "2 to 6", + "origin": "Tom Jolly's board game, sixth edition, 1993" + }, + { + "slug": "waving-hands", + "name": "Waving Hands", + "url": "https://hands.kestrelsnest.social", + "pitch": "Duelling wizards. Two hands each. The right run of gestures is a spell.", + "players": "2 to 12", + "origin": "Richard Bartle's pencil-and-paper duel, 1977" + } + ] +}