The chronicle's enchantment lines no longer all run image, colon, consequence; each keeps its picture and its exact fact in its own sentence shape, and the about panel's tally heading and two of its closing flourishes are said plainly, since a page of one sentence pattern reads as written by a machine
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
This commit is contained in:
1 parent
f21b2c3b16
commit
fa5e9cfc08
3 files changed
+26
-26
No files matched your search
@@ -46,7 +46,7 @@
|
||||
...Object.entries(t.byGame).filter(([label]) => !told.has(label)).map(([label, n]) => ({ n, label }))
|
||||
];
|
||||
const first = t.firstGameAt ? `The first duel began ${new Date(t.firstGameAt).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}. ` : '';
|
||||
return { headline, details, note: `${first}Names are counted, not people: one player under two names is two here. Table time counts only the minutes between reveals, so a duel left open overnight adds nothing for the night.` };
|
||||
return { headline, details, note: `${first}The count is of names, so one player under two names is two here. Table time counts only the minutes between reveals, so a duel left open overnight adds nothing for the night.` };
|
||||
});
|
||||
/** The other games of Kestrel's Hall, for the about panel. */
|
||||
let family = $state<FamilyGame[]>([]);
|
||||
@@ -342,7 +342,7 @@
|
||||
{/snippet}
|
||||
{#snippet aboutTally()}
|
||||
{#if told}
|
||||
<TallyBoard title="How much love the duel is getting" headline={told.headline} details={told.details} note={told.note} />
|
||||
<TallyBoard title="What the ledgers add up to" headline={told.headline} details={told.details} note={told.note} />
|
||||
{:else}
|
||||
<p class="muted">Counting…</p>
|
||||
{/if}
|
||||
@@ -358,7 +358,7 @@
|
||||
{/if}
|
||||
<p class="hosting">
|
||||
The games of Kestrel's Hall are free and will stay free. They run on small servers that cost a few dollars a month, and anyone who wants to chip in toward the hosting can do so at
|
||||
<a href="https://ko-fi.com/kestrelsnest" target="_blank" rel="noreferrer">ko-fi.com/kestrelsnest</a>. Chipping in unlocks nothing, and nothing is held back without it.
|
||||
<a href="https://ko-fi.com/kestrelsnest" target="_blank" rel="noreferrer">ko-fi.com/kestrelsnest</a>. Chipping in unlocks nothing, and the games are the same for everyone.
|
||||
</p>
|
||||
<p class="made">Waving Hands is Richard Bartle's, 1977.</p>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// What the ledgers add up to: a few counts that are the game's own story,
|
||||
// large, on cards; then the rest in a column of figures; then the fine
|
||||
// print. Each game chooses its counts and words.
|
||||
let { headline, details, note, title = 'How much love the table is getting' }: { headline: TallyLine[]; details: TallyLine[]; note?: string; title?: string } =
|
||||
let { headline, details, note, title = 'What the ledgers add up to' }: { headline: TallyLine[]; details: TallyLine[]; note?: string; title?: string } =
|
||||
$props();
|
||||
|
||||
const shown = (n: number | string) => (typeof n === 'number' ? n.toLocaleString() : n);
|
||||
|
||||
+22
-22
@@ -312,7 +312,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
record(w, l2, r2, 'haste');
|
||||
entries[id].push(w.history.length - 1);
|
||||
}
|
||||
if (w.surrendered) log(`${w.name} lowers both hands and shows empty palms: a surrender.`, 'death');
|
||||
if (w.surrendered) log(`${w.name} lowers both hands and shows empty palms in surrender.`, 'death');
|
||||
}
|
||||
|
||||
// ---- 2. Which completed spells are actually loosed ----
|
||||
@@ -336,7 +336,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
options.find((c) => choice.seqIndex === undefined || c.seqIndex === choice.seqIndex) ?? options[0];
|
||||
if (!completion) continue;
|
||||
if (completion.spell.id === 'lightning_bolt_quick' && w.usedQuickLightning) {
|
||||
log(`${w.name}'s quick lightning bolt sputters: it has already been used this duel.`, 'fizzle');
|
||||
log(`${w.name}'s quick lightning bolt sputters and dies, spent earlier in this duel.`, 'fizzle');
|
||||
continue;
|
||||
}
|
||||
const cells = usedCells(completion, choice.hand, index + 1);
|
||||
@@ -404,7 +404,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
const countered = new Set<TargetId>();
|
||||
const dispels = live().filter((c) => c.spell.id === 'dispel_magic');
|
||||
if (dispels.length > 0) {
|
||||
log('A wave of stillness rolls across the field. Dispel magic: every other spell fails, every enchantment ends, and every creature will fade after this turn.', 'spell');
|
||||
log('A wave of stillness rolls across the field as the magic is dispelled. Every other spell fails, every enchantment ends, and every creature will fade after this turn.', 'spell');
|
||||
for (const c of casts) if (!c.nullified && c.spell.id !== 'dispel_magic') c.nullified = 'dispelled';
|
||||
for (const d of dispels) shielded.set(d.target, 'dispel magic');
|
||||
for (const id of seats) clearEnchantments(W[id]);
|
||||
@@ -476,7 +476,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
if (chosen) {
|
||||
chosen.permanent = true;
|
||||
extendedNow.add(id);
|
||||
log(`${w.name}'s ${chosen.spell.name} sets hard, like wax cooling: it will not fade.`, 'spell');
|
||||
log(`${w.name}'s ${chosen.spell.name} sets hard, like wax cooling, and will not fade.`, 'spell');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,7 +545,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
};
|
||||
if (fireStorm && iceStorm) {
|
||||
fireStorm = iceStorm = false;
|
||||
log('Fire storm meets ice storm: a great hiss of steam, and the two storms cancel. Nothing is harmed.', 'fizzle');
|
||||
log('Fire storm meets ice storm in a great hiss of steam, and the two cancel. Nothing is harmed.', 'fizzle');
|
||||
}
|
||||
if (fireStorm && elementals('ice_elemental').length > 0) {
|
||||
fireStorm = false;
|
||||
@@ -621,14 +621,14 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
if (wiz) {
|
||||
wiz.pending.amnesia = true;
|
||||
if (c.permanent) wiz.permanent.amnesia = true;
|
||||
log(`A grey haze drifts behind ${wiz.name}'s eyes: next turn the hands will repeat what they just did${c.permanent ? ', and every turn after' : ''}.`);
|
||||
log(`A grey haze drifts behind ${wiz.name}'s eyes. Next turn the hands will repeat what they just did${c.permanent ? ', and every turn after' : ''}.`);
|
||||
} else if (mon) mon.pending.forcedTarget = orders.get(mon.id);
|
||||
break;
|
||||
case 'confusion':
|
||||
if (wiz) {
|
||||
wiz.pending.confusion = true;
|
||||
if (c.permanent) wiz.permanent.confusion = true;
|
||||
log(`The field tilts and swims around ${wiz.name}: next turn one hand will go astray${c.permanent ? ', and every turn after' : ''}.`);
|
||||
log(`The field tilts and swims around ${wiz.name}. One hand will go astray next turn${c.permanent ? ', and every turn after' : ''}.`);
|
||||
} else if (mon) {
|
||||
const candidates = [...seats.filter((id) => W[id].alive), ...state.monsters.filter((o) => o.id !== mon.id).map((o) => o.id)];
|
||||
const t = pick(candidates as TargetId[]);
|
||||
@@ -641,7 +641,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
const hand = c.choice.chosenHand ?? pick(HANDS);
|
||||
wiz.pending.charmed = { hand, by: c.caster };
|
||||
if (c.permanent) wiz.permanent.charmed = { hand, by: c.caster };
|
||||
log(`A warm, easy trust settles on ${wiz.name}: the ${hand} hand will obey ${W[c.caster].name}${c.permanent ? ' from now on' : ' next turn'}.`);
|
||||
log(`A warm, easy trust comes over ${wiz.name}, whose ${hand} hand will obey ${W[c.caster].name}${c.permanent ? ' from now on' : ' next turn'}.`);
|
||||
} else {
|
||||
c.nullified = 'monsters cannot be charmed as people';
|
||||
log(`The charm slides off ${target}, finding no mind to hold.`, 'fizzle');
|
||||
@@ -651,7 +651,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
if (mon && !isElemental(mon.kind)) {
|
||||
mon.owner = c.caster;
|
||||
orders.set(mon.id, input(c.caster).monsterOrders?.[mon.id] ?? enemyOf(state, c.caster));
|
||||
log(`${mon.name} turns, blinks, and pads over to ${W[c.caster].name}'s side: it serves a new master.`);
|
||||
log(`${mon.name} turns, blinks, and pads over to ${W[c.caster].name}'s side to serve a new master.`);
|
||||
} else {
|
||||
c.nullified = 'not a charmable monster';
|
||||
log(`The charm slides off ${target} and takes no hold.`, 'fizzle');
|
||||
@@ -662,10 +662,10 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
const hand = wiz.constraints.paralysedHand ?? c.choice.chosenHand ?? 'left';
|
||||
wiz.pending.paralysedHand = hand;
|
||||
if (c.permanent) wiz.permanent.paralysedHand = hand;
|
||||
log(`A cold stiffness creeps into ${wiz.name}'s ${hand} hand: it will be paralysed${c.permanent ? ' from now on' : ' next turn'}.`);
|
||||
log(`A cold stiffness creeps into ${wiz.name}'s ${hand} hand, which will be paralysed${c.permanent ? ' from now on' : ' next turn'}.`);
|
||||
} else if (mon && !isElemental(mon.kind)) {
|
||||
mon.pending.skipAttack = true;
|
||||
log(`${mon.name}'s limbs stiffen: it will stand paralysed next turn.`);
|
||||
log(`${mon.name}'s limbs stiffen, and it will stand paralysed next turn.`);
|
||||
} else {
|
||||
c.nullified = 'elementals cannot be paralysed';
|
||||
}
|
||||
@@ -686,7 +686,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
case 'protection_from_evil':
|
||||
if (wiz) wiz.protectionTurns = duration(4);
|
||||
else if (mon) mon.protectionTurns = duration(4);
|
||||
log(`A pale circle of light rises around ${target}: protected from evil${c.permanent ? forever : ' for this turn and the next three'}.`);
|
||||
log(`A pale circle of light rises around ${target}, protected from evil${c.permanent ? forever : ' for this turn and the next three'}.`);
|
||||
break;
|
||||
case 'resist_heat':
|
||||
if (wiz) wiz.resistHeat = true;
|
||||
@@ -696,7 +696,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
case 'resist_cold':
|
||||
if (wiz) wiz.resistCold = true;
|
||||
else if (mon) mon.resistCold = true;
|
||||
log(`A warm rosy aura settles over ${target}, who now resists cold.`);
|
||||
log(`A warm rosy aura wraps ${target}, who now resists cold.`);
|
||||
break;
|
||||
case 'disease':
|
||||
if (wiz) {
|
||||
@@ -707,13 +707,13 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
case 'poison':
|
||||
if (wiz) {
|
||||
wiz.poisonTurns = 6;
|
||||
log(`${wiz.name} goes grey about the lips: poison is in the blood. Unless cured, ${wiz.name} will die at the end of turn ${state.turn + 6}.`);
|
||||
log(`${wiz.name} goes grey about the lips as poison takes the blood. Unless cured, ${wiz.name} will die at the end of turn ${state.turn + 6}.`);
|
||||
} else c.nullified = 'monsters do not sicken';
|
||||
break;
|
||||
case 'blindness':
|
||||
if (wiz) {
|
||||
wiz.blindTurns = duration(4);
|
||||
log(`A milky film clouds ${wiz.name}'s eyes: blind${c.permanent ? forever : ' for three turns'}.`);
|
||||
log(`A milky film clouds ${wiz.name}'s eyes and leaves them blind${c.permanent ? forever : ' for three turns'}.`);
|
||||
} else if (mon) destroyBefore(mon, 'blinded and unmade');
|
||||
break;
|
||||
case 'invisibility':
|
||||
@@ -725,25 +725,25 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
case 'haste':
|
||||
if (wiz) {
|
||||
wiz.hasteTurns = duration(4);
|
||||
log(`The air about ${wiz.name} quickens; everyone else seems to move through honey. Hastened${c.permanent ? forever : ' for the next three turns'}: two pairs of gestures a turn.`);
|
||||
log(`The air about ${wiz.name} quickens; everyone else seems to move through honey. Hastened${c.permanent ? forever : ' for the next three turns'}, ${wiz.name} makes two pairs of gestures a turn.`);
|
||||
} else c.nullified = 'monsters cannot be hastened';
|
||||
break;
|
||||
case 'time_stop':
|
||||
if (wiz) {
|
||||
state.timeStops.push(wiz.id);
|
||||
log(`The moment catches on ${wiz.name} like a snagged thread: when this turn ends, time will stop, and ${wiz.name} alone will move.`);
|
||||
log(`The moment catches on ${wiz.name} like a snagged thread. When this turn ends, time will stop, and ${wiz.name} alone will move.`);
|
||||
} else c.nullified = 'only wizards can stop time';
|
||||
break;
|
||||
case 'delayed_effect':
|
||||
if (wiz) {
|
||||
wiz.delayedTurns = bankedNow.has(wiz.id) ? 0 : duration(4);
|
||||
if (!bankedNow.has(wiz.id)) log(`A hollow of waiting opens around ${wiz.name}'s hands: the next spell will be caught and held.`);
|
||||
if (!bankedNow.has(wiz.id)) log(`A hollow of waiting opens around ${wiz.name}'s hands, ready to catch and hold the next spell.`);
|
||||
} else c.nullified = 'only wizards can delay a spell';
|
||||
break;
|
||||
case 'permanency':
|
||||
if (wiz) {
|
||||
wiz.permanencyTurns = extendedNow.has(wiz.id) ? 0 : 4;
|
||||
if (!extendedNow.has(wiz.id)) log(`Something steady and unhurried settles on ${wiz.name}: the next enchantment will be permanent.`);
|
||||
if (!extendedNow.has(wiz.id)) log(`Something steady and unhurried takes hold of ${wiz.name}, and the next enchantment will be permanent.`);
|
||||
} else c.nullified = 'only a wizard can hold a permanency';
|
||||
break;
|
||||
}
|
||||
@@ -756,10 +756,10 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
const mon = monsterOf(c.target);
|
||||
if (wiz) {
|
||||
clearEnchantments(wiz);
|
||||
log(`Threads of old magic lift from ${wiz.name} and drift away: every enchantment is gone.`);
|
||||
log(`Threads of old magic lift from ${wiz.name} and drift away. Every enchantment is gone.`);
|
||||
} else if (mon && c.spell.id === 'remove_enchantment') {
|
||||
destroyedAfterAttack.add(mon.id);
|
||||
log(`${mon.name} begins to fray at the edges: it will be unmade once it has attacked.`);
|
||||
log(`${mon.name} begins to fray at the edges, and will be unmade once it has attacked.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,7 +820,7 @@ export function resolveTurn(previous: GameState, inputs: Record<WizardId, TurnIn
|
||||
heal(t, 2, 'Cure heavy wounds');
|
||||
if (isWizard(t) && W[t].diseaseTurns !== null) {
|
||||
W[t].diseaseTurns = null;
|
||||
log(`${W[t].name}'s fever breaks, and the flush fades from the skin: the disease is cured.`);
|
||||
log(`${W[t].name}'s fever breaks, and the flush fades from the skin. The disease is cured.`);
|
||||
}
|
||||
break;
|
||||
case 'raise_dead':
|
||||
|
||||
Reference in new issue
Block a user