The rules page, the offline page, the guide, two spell descriptions and the share description no longer assume a duel has exactly two wizards: every wizard chooses, attacks go at an opponent, the last wizard standing wins, and blindness and invisibility speak of other wizards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
297 lines
9.5 KiB
Svelte
297 lines
9.5 KiB
Svelte
<script lang="ts">
|
|
import raw from '../../../docs/waving-hands-rules.txt?raw';
|
|
import { MONSTER_STATS, SPELLS, type SpellCategory } from '$lib/game/spells';
|
|
|
|
const groups: { category: SpellCategory; title: string; blurb: string }[] = [
|
|
{ category: 'protection', title: 'Protection', blurb: 'Usually cast on yourself. A shield is a single P, so a palm is never wasted.' },
|
|
{ category: 'summons', title: 'Summons', blurb: 'Creatures fight for whoever the spell is cast on, attack the same turn they appear, and keep attacking each turn until destroyed.' },
|
|
{ category: 'damage', title: 'Damage', blurb: 'A wizard can take 14 points; the fifteenth kills. Cures apply in the same turn as the damage.' },
|
|
{ category: 'enchantment', title: 'Enchantments', blurb: 'Most bind the subject on the following turn. Two of the mind-affecting kind landing at once cancel each other.' }
|
|
];
|
|
|
|
const HEADINGS = [
|
|
'Introduction',
|
|
'The Turn',
|
|
'Gestures',
|
|
'Winning',
|
|
'Spells',
|
|
'Protection spells',
|
|
'Summons spells',
|
|
'Damaging Spells',
|
|
'Enchantments',
|
|
'Non-spells',
|
|
'Reference Sheet',
|
|
'Sample Game',
|
|
'Annotation'
|
|
];
|
|
|
|
/** Split the transcription at its headings so each part can be linked. */
|
|
const original = (() => {
|
|
const lines = raw.split('\n');
|
|
const sections: { title: string; id: string; body: string }[] = [];
|
|
let current: { title: string; id: string; lines: string[] } | null = null;
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
if (HEADINGS.includes(trimmed)) {
|
|
if (current) sections.push({ title: current.title, id: current.id, body: current.lines.join('\n').trim() });
|
|
current = { title: trimmed, id: 'original-' + trimmed.toLowerCase().replace(/\s+/g, '-'), lines: [] };
|
|
continue;
|
|
}
|
|
if (/^\s*(Spellcaster|Rules for the Spellcaster Game|Back to Main List)\s*$/.test(line)) continue;
|
|
if (current) current.lines.push(line);
|
|
}
|
|
if (current) sections.push({ title: current.title, id: current.id, body: current.lines.join('\n').trim() });
|
|
return sections;
|
|
})();
|
|
|
|
const monsters = (['goblin', 'ogre', 'troll', 'giant', 'fire_elemental', 'ice_elemental'] as const).map((k) => MONSTER_STATS[k]);
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Waving Hands: the rules</title>
|
|
</svelte:head>
|
|
|
|
<main class="rules">
|
|
<header>
|
|
<a class="back" href="/">Back to the hall</a> · <a class="back" href="/guide">How to play</a>
|
|
<h1>How to duel</h1>
|
|
<p class="lede">Waving Hands is Richard Bartle's 1977 game of duelling wizards, who trade spells made of hand gestures. Two can play, or a whole table. This page is a working reference, and the original transcription is at the end.</p>
|
|
<nav aria-label="Sections">
|
|
<a href="#turn">A turn</a>
|
|
<a href="#gestures">Gestures</a>
|
|
<a href="#overlap">Overlapping spells</a>
|
|
<a href="#targets">Targets and timing</a>
|
|
<a href="#monsters">Monsters</a>
|
|
<a href="#winning">Winning</a>
|
|
<a href="#spells">Every spell</a>
|
|
<a href="#original">Original text</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<section id="turn">
|
|
<h2>A turn</h2>
|
|
<ol>
|
|
<li>Every wizard secretly chooses a gesture for each hand. In this game you pick them, then choose which finished spells to cast and at whom.</li>
|
|
<li>Both reveal at once. Every spell, stab and monster attack of the turn takes effect together, so a shield you make this turn stops a missile made this turn.</li>
|
|
<li>Damage and healing are added up, monsters that lost all their hit points are removed, and enchantments cast this turn bind next turn.</li>
|
|
</ol>
|
|
</section>
|
|
|
|
<section id="gestures">
|
|
<h2>Gestures</h2>
|
|
<p>There are six magical gestures. <strong>F</strong> is wriggled fingers, <strong>P</strong> a proffered palm, <strong>S</strong> a snap, <strong>W</strong> a wave, <strong>D</strong> a pointed digit and <strong>C</strong> a clap. A clap needs both hands, and a clap with one hand counts as nothing. A hand may instead <strong>stab</strong> with the knife (one point of damage, one stab a turn) or do <strong>nothing</strong>. Neither belongs to any spell, so a hand that stabs or rests starts its next spell from scratch.</p>
|
|
<p>A spell is a run of gestures made on consecutive turns by one hand. In the notation, a bracketed lower-case letter such as <strong>(w</strong> means that gesture made with both hands at once.</p>
|
|
<p>Both palms at once is <strong>surrender</strong>. It is not a spell and it ends the duel.</p>
|
|
</section>
|
|
|
|
<section id="overlap">
|
|
<h2>Overlapping spells</h2>
|
|
<p>A gesture can belong to more than one spell as long as each spell's run is unbroken. F-F-F is Paralysis. Follow it with S-S-D-D, and the last five gestures, F-S-S-D-D, are a Fireball. Another F after F-F-F is a second Paralysis. Each gesture can finish only one spell, so if two spells would end on the same gesture you choose one. Any finished spell may be let go harmlessly, except the storms, which rage wherever they are released.</p>
|
|
</section>
|
|
|
|
<section id="targets">
|
|
<h2>Targets and timing</h2>
|
|
<p>Every spell has a subject. Protection and summons are usually cast on yourself, and attacks and enchantments on an opponent, but any spell may be aimed at any wizard or monster, and a spell aimed at something that no longer exists is lost.</p>
|
|
<p>Because everything is simultaneous, a monster killed the turn it appears still attacks that turn, a Resist heat cast in the same turn as a Fireball saves you, and a Remove enchantment cast in the same turn as a Fireball leaves you burned. <strong>Counter-spell</strong> stops every other spell cast at its subject that turn, except Finger of death and Dispel magic. <strong>Dispel magic</strong> stops every spell on the field, ends every enchantment and unmakes every monster after it attacks.</p>
|
|
</section>
|
|
|
|
<section id="monsters">
|
|
<h2>Monsters</h2>
|
|
<p>A summoned creature obeys whoever it was summoned for, attacks any wizard or monster its master names, and is stopped by a shield on its target. Elementals attack everyone not resistant to their element, are destroyed by the opposite element, and merge if two of a kind meet.</p>
|
|
<table>
|
|
<thead><tr><th>Creature</th><th>Hit points</th><th>Damage a turn</th></tr></thead>
|
|
<tbody>
|
|
{#each monsters as m (m.name)}
|
|
<tr><td>{m.name}</td><td>{m.hp}</td><td>{m.attack}</td></tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section id="winning">
|
|
<h2>Winning</h2>
|
|
<p>Each wizard has fifteen hit points and dies on losing them all. The last wizard standing wins. If the last wizards standing all die in the same turn, the duel is a draw. Surrender loses, unless the surrendering wizard's last gestures also killed the last opponent.</p>
|
|
</section>
|
|
|
|
<section id="spells">
|
|
<h2>Every spell</h2>
|
|
{#each groups as g (g.category)}
|
|
<h3 id={'spells-' + g.category}>{g.title}</h3>
|
|
<p class="blurb">{g.blurb}</p>
|
|
<dl>
|
|
{#each SPELLS.filter((s) => s.category === g.category) as spell (spell.id)}
|
|
<div>
|
|
<dt><span class="name">{spell.name}</span> <span class="seq">{spell.sequences.join(' or ')}</span></dt>
|
|
<dd>{spell.summary}</dd>
|
|
</div>
|
|
{/each}
|
|
</dl>
|
|
{/each}
|
|
</section>
|
|
|
|
<section id="original">
|
|
<h2>The original text</h2>
|
|
<p class="blurb">Bartle's rules as printed in the <em>Duel Purpose</em> fanzine, transcribed by Andrew Buchanan. Kept as the source for anything this page simplifies.</p>
|
|
<nav aria-label="Original sections" class="subnav">
|
|
{#each original as sec (sec.id)}
|
|
<a href={'#' + sec.id}>{sec.title}</a>
|
|
{/each}
|
|
</nav>
|
|
{#each original as sec (sec.id)}
|
|
<details id={sec.id}>
|
|
<summary>{sec.title}</summary>
|
|
<pre>{sec.body}</pre>
|
|
</details>
|
|
{/each}
|
|
</section>
|
|
<p class="word">If this page reads a rule differently from the text above, that is a bug: <a href="mailto:eric@ericwagoner.com">send word</a>.</p>
|
|
</main>
|
|
|
|
<style>
|
|
.rules {
|
|
max-width: 44rem;
|
|
margin: 0 auto;
|
|
padding: 1.5rem var(--gutter) 4rem;
|
|
}
|
|
|
|
a {
|
|
color: var(--frost);
|
|
}
|
|
|
|
.back {
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2.4rem;
|
|
font-weight: 300;
|
|
font-variation-settings: 'opsz' 144;
|
|
margin: 0.5rem 0 0.3rem;
|
|
}
|
|
|
|
.lede {
|
|
color: var(--bone-dim);
|
|
max-width: 38em;
|
|
}
|
|
|
|
nav {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.3rem 1rem;
|
|
margin: 1rem 0 0.5rem;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
section {
|
|
padding: 1.25rem 0 0.5rem;
|
|
border-top: 1px solid var(--rule);
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.5rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.15rem;
|
|
font-style: italic;
|
|
margin-top: 1.25rem;
|
|
}
|
|
|
|
p + p,
|
|
ol {
|
|
margin-top: 0.6rem;
|
|
}
|
|
|
|
ol {
|
|
padding-left: 1.4rem;
|
|
}
|
|
|
|
li + li {
|
|
margin-top: 0.3rem;
|
|
}
|
|
|
|
.blurb {
|
|
color: var(--bone-dim);
|
|
font-size: 0.95rem;
|
|
margin: 0.2rem 0 0.6rem;
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
margin-top: 0.75rem;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
th,
|
|
td {
|
|
text-align: left;
|
|
padding: 0.25rem 1.5rem 0.25rem 0;
|
|
border-bottom: 1px solid var(--rule);
|
|
}
|
|
|
|
th {
|
|
font-weight: 500;
|
|
color: var(--bone-dim);
|
|
}
|
|
|
|
dl {
|
|
margin: 0;
|
|
}
|
|
|
|
dl > div {
|
|
padding: 0.45rem 0;
|
|
border-bottom: 1px solid var(--rule);
|
|
}
|
|
|
|
dt .name {
|
|
font-weight: 500;
|
|
}
|
|
|
|
dt .seq {
|
|
margin-left: 0.6rem;
|
|
letter-spacing: 0.05em;
|
|
color: var(--ember);
|
|
}
|
|
|
|
dd {
|
|
margin: 0.15rem 0 0;
|
|
color: var(--bone-dim);
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.subnav {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
details {
|
|
border-bottom: 1px solid var(--rule);
|
|
padding: 0.4rem 0;
|
|
}
|
|
|
|
summary {
|
|
cursor: pointer;
|
|
font-style: italic;
|
|
}
|
|
|
|
pre {
|
|
font-family: var(--font);
|
|
font-size: 0.95rem;
|
|
line-height: 1.55;
|
|
white-space: pre-wrap;
|
|
margin: 0.5rem 0 0.5rem;
|
|
color: var(--bone-dim);
|
|
}
|
|
|
|
.word {
|
|
margin-top: 2rem;
|
|
color: var(--bone-faint);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.word a {
|
|
color: var(--frost);
|
|
}
|
|
</style>
|