The family list knows the hall: about panels link to gamehall.kestrelsnest.social, and a new game gets a card there
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
2d78ab6b3f
commit
bce01a013a
@@ -63,6 +63,11 @@ README.
|
|||||||
- **Send word** is the contact paragraph: mail, Bluesky and Mastodon, and
|
- **Send word** is the contact paragraph: mail, Bluesky and Mastodon, and
|
||||||
where the keeper roosts. It lives in the about panel; the colophons, the
|
where the keeper roosts. It lives in the about panel; the colophons, the
|
||||||
rules page and the guide each carry a one-line mail link.
|
rules page and the guide each carry a one-line mail link.
|
||||||
|
- **The family list** in the about panel, *More games in Kestrel's Hall*,
|
||||||
|
comes from `family.json` and its heading links to the family's landing
|
||||||
|
page, gamehall.kestrelsnest.social. A new game is added to the kit's
|
||||||
|
`family.json` and given a card on that page in the blog repo; both are
|
||||||
|
hand-kept.
|
||||||
- **The hosting line** follows the family list in the about panel: the
|
- **The hosting line** follows the family list in the about panel: the
|
||||||
games are free and will stay free, the servers cost a few dollars a
|
games are free and will stay free, the servers cost a few dollars a
|
||||||
month, and ko-fi.com/kestrelsnest is where anyone may chip in. It says
|
month, and ko-fi.com/kestrelsnest is where anyone may chip in. It says
|
||||||
|
|||||||
@@ -71,8 +71,14 @@ interruptions, Wiz-War's shape, does not fit and keeps its own server.
|
|||||||
## The family
|
## The family
|
||||||
|
|
||||||
`family.json` lists every game of Kestrel's Hall: slug, name, address,
|
`family.json` lists every game of Kestrel's Hall: slug, name, address,
|
||||||
one-line pitch, player count and origin. Each game ships a copy as
|
one-line pitch, player count and origin, plus `hall`, the address of the
|
||||||
`static/family.json` (the deploy script takes the kit's latest when the kit
|
family's landing page. Each game ships a copy as `static/family.json` (the
|
||||||
is beside the game) and its about panel lists the others under *More games
|
deploy script takes the kit's latest when the kit is beside the game) and
|
||||||
in Kestrel's Hall*. Add a game here, then deploy the games; nothing
|
its about panel lists the others under *More games in Kestrel's Hall*, the
|
||||||
else needs to know.
|
heading linking to the family's landing page. Add a game here, then
|
||||||
|
deploy the games.
|
||||||
|
|
||||||
|
That landing page, https://gamehall.kestrelsnest.social, is one static page
|
||||||
|
in the blog repo (`~/Sites/blog/static/gamehall/index.html`) with a card
|
||||||
|
per game, written by hand, and it deploys with the blog. A new game is not
|
||||||
|
in the family until it has its card there too.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"family": "Kestrel's Hall",
|
"family": "Kestrel's Hall",
|
||||||
|
"hall": "https://gamehall.kestrelsnest.social",
|
||||||
"games": [
|
"games": [
|
||||||
{
|
{
|
||||||
"slug": "wizwar",
|
"slug": "wizwar",
|
||||||
|
|||||||
+6
-1
@@ -45,5 +45,10 @@ Next:
|
|||||||
4. Create a Sentry project (the Sentry MCP can) and put its DSN and project id
|
4. Create a Sentry project (the Sentry MCP can) and put its DSN and project id
|
||||||
in deploy/$SLUG.service and deploy/sentry-slack-alert.sh.
|
in deploy/$SLUG.service and deploy/sentry-slack-alert.sh.
|
||||||
5. deploy/deploy.sh <ip>
|
5. deploy/deploy.sh <ip>
|
||||||
docs/conventions.md is the house style every game follows.
|
6. Put the game in the family: add it to the kit's family.json (every game's
|
||||||
|
about panel lists the others from that file at its next deploy), then give
|
||||||
|
it a card on the family's landing page, Kestrel's Hall, hand-written HTML in
|
||||||
|
the blog repo at ~/Sites/blog/static/gamehall/index.html, and deploy the blog.
|
||||||
|
Card art is the game's own drawing; never a publisher's logo or scan.
|
||||||
|
CONVENTIONS.md in the kit is the house style every game follows.
|
||||||
MSG
|
MSG
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// bot, open a table, join by code, the ledger of your tables, and the
|
// 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.
|
// about panel with the rules, the guide and a way to reach the keeper.
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { otherGames, type FamilyGame } from '$lib/family';
|
import { loadFamily, type FamilyGame } from '$lib/family';
|
||||||
import Preferences from './Preferences.svelte';
|
import Preferences from './Preferences.svelte';
|
||||||
import { prefs, setPref } from '$lib/prefs.svelte';
|
import { prefs, setPref } from '$lib/prefs.svelte';
|
||||||
import { allSeats, doubtSeat, forgetSeat, rememberSeat, ServerError, trustSeat } from '$lib/net/client';
|
import { allSeats, doubtSeat, forgetSeat, rememberSeat, ServerError, trustSeat } from '$lib/net/client';
|
||||||
@@ -33,8 +33,12 @@
|
|||||||
const hours = (minutes: number) => (minutes < 90 ? `${minutes} minutes` : `${Math.round(minutes / 60)} hours`);
|
const hours = (minutes: number) => (minutes < 90 ? `${minutes} minutes` : `${Math.round(minutes / 60)} hours`);
|
||||||
/** The other games of Kestrel's Hall, for the about panel. */
|
/** The other games of Kestrel's Hall, for the about panel. */
|
||||||
let family = $state<FamilyGame[]>([]);
|
let family = $state<FamilyGame[]>([]);
|
||||||
|
let hall = $state('');
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
void otherGames('__SLUG__').then((g) => (family = g));
|
void loadFamily('__SLUG__').then((f) => {
|
||||||
|
family = f.games;
|
||||||
|
hall = f.hall;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
/** Reports this browser's seats have filed, with the keeper's replies. */
|
/** Reports this browser's seats have filed, with the keeper's replies. */
|
||||||
let reports = $state<Report[]>([]);
|
let reports = $state<Report[]>([]);
|
||||||
@@ -282,7 +286,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if family.length}
|
{#if family.length}
|
||||||
<h3>More games in Kestrel's Hall</h3>
|
<h3>More games in {#if hall}<a href={hall}>Kestrel's Hall</a>{:else}Kestrel's Hall{/if}</h3>
|
||||||
<ul class="family">
|
<ul class="family">
|
||||||
{#each family as g (g.slug)}
|
{#each family as g (g.slug)}
|
||||||
<li><a href={g.url}>{g.name}</a>: {g.pitch} <span class="muted">{g.players} players; {g.origin}.</span></li>
|
<li><a href={g.url}>{g.name}</a>: {g.pitch} <span class="muted">{g.players} players; {g.origin}.</span></li>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// The other games of Kestrel's Hall, from the family list the kit keeps
|
// The other games of Kestrel's Hall, from the family list the kit keeps
|
||||||
// (family.json, one copy shipped with every game), so a player who liked one
|
// (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.
|
// table can find the next, and the hall's front door. This game is left out
|
||||||
|
// of its own list.
|
||||||
|
|
||||||
export interface FamilyGame {
|
export interface FamilyGame {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -11,13 +12,19 @@ export interface FamilyGame {
|
|||||||
origin: string;
|
origin: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function otherGames(self: string): Promise<FamilyGame[]> {
|
/** The family list: the hall's front door and every game but this one. */
|
||||||
|
export interface Family {
|
||||||
|
hall: string;
|
||||||
|
games: FamilyGame[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadFamily(self: string): Promise<Family> {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/family.json', { cache: 'no-cache' });
|
const res = await fetch('/family.json', { cache: 'no-cache' });
|
||||||
if (!res.ok) return [];
|
if (!res.ok) return { hall: '', games: [] };
|
||||||
const data = (await res.json()) as { games?: FamilyGame[] };
|
const data = (await res.json()) as { hall?: string; games?: FamilyGame[] };
|
||||||
return (data.games ?? []).filter((g) => g.slug !== self);
|
return { hall: data.hall ?? '', games: (data.games ?? []).filter((g) => g.slug !== self) };
|
||||||
} catch {
|
} catch {
|
||||||
return [];
|
return { hall: '', games: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"family": "Kestrel's Hall",
|
"family": "Kestrel's Hall",
|
||||||
|
"hall": "https://gamehall.kestrelsnest.social",
|
||||||
"games": [
|
"games": [
|
||||||
{
|
{
|
||||||
"slug": "wizwar",
|
"slug": "wizwar",
|
||||||
@@ -16,6 +17,14 @@
|
|||||||
"pitch": "Duelling wizards. Two hands each. The right run of gestures is a spell.",
|
"pitch": "Duelling wizards. Two hands each. The right run of gestures is a spell.",
|
||||||
"players": "2 to 12",
|
"players": "2 to 12",
|
||||||
"origin": "Richard Bartle's pencil-and-paper duel, 1977"
|
"origin": "Richard Bartle's pencil-and-paper duel, 1977"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "hnefatafl",
|
||||||
|
"name": "Hnefatafl",
|
||||||
|
"url": "https://tafl.kestrelsnest.social",
|
||||||
|
"pitch": "The Viking board game. One king, one throne, four corners, and a ring of attackers closing in.",
|
||||||
|
"players": "2",
|
||||||
|
"origin": "the Norse tafl game, reconstructed from Linnaeus's 1732 notes; Copenhagen rules or Tablut"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user