Characters - Builder: pick-then-Next on every step with live preview, Back button, plain-language help throughout, and "Suggest for me" for ability scores - Motivation is picked from a list; new steps for special abilities, background and appearance, all composed from fixed lists and DM-only to edit afterwards - Copy a character to another campaign - Characters can give gold, weapons, items and spells to each other - Money tracker: gold only changes through a logged ledger - Fixes: level/XP/location are DM-only, HP stays within 0..max, physical dice ability rolls, every die is rolled once Gear - DM-managed item pool; weapons, items and spells are handed out by the DM - Starting-option flag per pool entry - Gear levels 1-9 per held copy with a damage ladder, growth rate and starting level; spells gated by spell level with a fixed damage value - Spell library carries default damage; damage is picked from a list Encounters - Battle and NPC quest modes, turn tracking, scene description, DM notes - NPCs, richer combatant cards, conditions from a list - Rewards (gold, weapons, items) lined up and awarded, with gold splitting - Character HP is shared between the sheet and the encounter - Monster stat blocks and NPC secrets are never sent to players Other - Dice roller on every page - Docs for characters, weapons, spells, items, encounters, monsters and NPCs, with full lists generated from the data (npm run docs:lists) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
230 lines
11 KiB
TypeScript
230 lines
11 KiB
TypeScript
import type { Ability, CharacterClass, Species } from '../api/types';
|
||
|
||
// Plain-language explanations for someone building their first character.
|
||
// Everything here describes what a choice does to the *numbers* — plus a line
|
||
// of flavour. The special abilities a species or class grants are listed
|
||
// separately, from the server's own lists (useBuildOptions).
|
||
|
||
export const SPECIES_SPEED: Record<Species, number> = {
|
||
HUMAN: 30,
|
||
HALF_ELF: 30,
|
||
ELF: 30,
|
||
GNOME: 25,
|
||
DWARF: 25,
|
||
HALFLING: 25,
|
||
DRAGONBORN: 30,
|
||
TIEFLING: 30,
|
||
HALF_ORC: 30,
|
||
ORC: 30,
|
||
};
|
||
|
||
export const SPECIES_GUIDE: Record<Species, { blurb: string; goodFor: string }> = {
|
||
HUMAN: {
|
||
blurb: 'Adaptable and ambitious, found in every corner of the world.',
|
||
goodFor: 'A little better at everything (+1 to all six abilities). A safe pick for any class.',
|
||
},
|
||
HALF_ELF: {
|
||
blurb: 'Walks between two worlds, with human drive and elven grace.',
|
||
goodFor: 'Very charming (+2 Charisma), and you choose two more abilities to raise by 1. Great for talkers and Charisma spellcasters.',
|
||
},
|
||
ELF: {
|
||
blurb: 'Graceful, long-lived and quick on their feet.',
|
||
goodFor: 'Nimble (+2 Dexterity): harder to hit, acts sooner in a fight, better with bows and light blades.',
|
||
},
|
||
GNOME: {
|
||
blurb: 'Small, curious tinkerers with sharp minds.',
|
||
goodFor: 'Clever (+2 Intelligence): ideal for Wizards and knowledge skills. A little slower on foot.',
|
||
},
|
||
DWARF: {
|
||
blurb: 'Stout, stubborn mountain folk who are hard to put down.',
|
||
goodFor: 'Tough (+2 Constitution): more hit points at every level. A little slower on foot.',
|
||
},
|
||
HALFLING: {
|
||
blurb: 'Small, cheerful and surprisingly brave.',
|
||
goodFor: 'Nimble (+2 Dexterity): harder to hit and good at sneaking. A little slower on foot.',
|
||
},
|
||
DRAGONBORN: {
|
||
blurb: 'Proud, scaled warriors with dragon blood.',
|
||
goodFor: 'Strong (+2 Strength) with presence (+1 Charisma): hits hard up close. Great for Paladins and Fighters.',
|
||
},
|
||
TIEFLING: {
|
||
blurb: 'Marked by an infernal bloodline, and used to being stared at.',
|
||
goodFor: 'Magnetic (+2 Charisma) and bright (+1 Intelligence): great for Warlocks, Sorcerers and Bards.',
|
||
},
|
||
HALF_ORC: {
|
||
blurb: 'Fierce and enduring, with something to prove.',
|
||
goodFor: 'Strong (+2 Strength) and hardy (+1 Constitution): hits hard and takes hits. Great for Barbarians and Fighters.',
|
||
},
|
||
ORC: {
|
||
blurb: 'Powerful and relentless, raised to survive.',
|
||
goodFor: 'Strong (+2 Strength) and hardy (+1 Constitution): built for the front line.',
|
||
},
|
||
};
|
||
|
||
export type Toughness = 'Fragile' | 'Average' | 'Sturdy' | 'Very tough';
|
||
|
||
export const CLASS_GUIDE: Record<
|
||
CharacterClass,
|
||
{ blurb: string; playsLike: string; keyAbilities: Ability[]; skillCount: number; difficulty: 'Easy' | 'Medium' | 'Tricky' }
|
||
> = {
|
||
FIGHTER: {
|
||
blurb: 'A trained warrior, at home with any weapon.',
|
||
playsLike: 'Stand at the front, hit things, take hits. The simplest class to play well.',
|
||
keyAbilities: ['STR', 'DEX', 'CON'],
|
||
skillCount: 2,
|
||
difficulty: 'Easy',
|
||
},
|
||
BARBARIAN: {
|
||
blurb: 'A fierce warrior driven by raw fury.',
|
||
playsLike: 'Charge in and soak up damage — you have the most hit points of any class.',
|
||
keyAbilities: ['STR', 'CON'],
|
||
skillCount: 2,
|
||
difficulty: 'Easy',
|
||
},
|
||
ROGUE: {
|
||
blurb: 'A sneak, scout and scoundrel who relies on cunning.',
|
||
playsLike: 'Strike from the shadows and solve problems with skills — you get more skills than anyone.',
|
||
keyAbilities: ['DEX'],
|
||
skillCount: 4,
|
||
difficulty: 'Medium',
|
||
},
|
||
RANGER: {
|
||
blurb: 'A hunter and tracker of the wild places.',
|
||
playsLike: 'Fight from range or up close, with a little nature magic and plenty of outdoor skills.',
|
||
keyAbilities: ['DEX', 'WIS'],
|
||
skillCount: 3,
|
||
difficulty: 'Medium',
|
||
},
|
||
WIZARD: {
|
||
blurb: 'A scholar who has studied magic from books.',
|
||
playsLike: 'Stay back and cast spells. Powerful but fragile — the fewest hit points of any class.',
|
||
keyAbilities: ['INT'],
|
||
skillCount: 2,
|
||
difficulty: 'Tricky',
|
||
},
|
||
SORCERER: {
|
||
blurb: 'Born with magic in the blood.',
|
||
playsLike: 'Stay back and cast spells through sheer force of personality. Powerful but fragile.',
|
||
keyAbilities: ['CHA'],
|
||
skillCount: 2,
|
||
difficulty: 'Tricky',
|
||
},
|
||
CLERIC: {
|
||
blurb: 'A priest who channels the power of a god.',
|
||
playsLike: 'Heal and protect the party, and hold your own in a fight. Every group is glad to have one.',
|
||
keyAbilities: ['WIS'],
|
||
skillCount: 2,
|
||
difficulty: 'Medium',
|
||
},
|
||
PALADIN: {
|
||
blurb: 'A holy knight sworn to an oath.',
|
||
playsLike: 'A front-line fighter with a little divine magic to back up the sword.',
|
||
keyAbilities: ['STR', 'CHA'],
|
||
skillCount: 2,
|
||
difficulty: 'Medium',
|
||
},
|
||
BARD: {
|
||
blurb: 'A performer whose words and music carry magic.',
|
||
playsLike: 'The party’s talker and helper: spells, skills, and a way out of trouble.',
|
||
keyAbilities: ['CHA'],
|
||
skillCount: 3,
|
||
difficulty: 'Medium',
|
||
},
|
||
WARLOCK: {
|
||
blurb: 'Made a pact with a powerful being in exchange for magic.',
|
||
playsLike: 'A spellcaster with a few strong tricks used often, rather than many used rarely.',
|
||
keyAbilities: ['CHA'],
|
||
skillCount: 2,
|
||
difficulty: 'Medium',
|
||
},
|
||
DRUID: {
|
||
blurb: 'A guardian of nature who draws magic from the wild.',
|
||
playsLike: 'A flexible spellcaster: healing, nature magic and control of the battlefield.',
|
||
keyAbilities: ['WIS'],
|
||
skillCount: 2,
|
||
difficulty: 'Tricky',
|
||
},
|
||
MONK: {
|
||
blurb: 'A martial artist who has trained body and mind.',
|
||
playsLike: 'Fast and precise: dart around the fight striking quickly, with little need for gear.',
|
||
keyAbilities: ['DEX', 'WIS'],
|
||
skillCount: 2,
|
||
difficulty: 'Medium',
|
||
},
|
||
};
|
||
|
||
export function hitDieToughness(hitDie: number): Toughness {
|
||
if (hitDie <= 6) return 'Fragile';
|
||
if (hitDie <= 8) return 'Average';
|
||
if (hitDie <= 10) return 'Sturdy';
|
||
return 'Very tough';
|
||
}
|
||
|
||
// What each ability changes on the sheet this app builds.
|
||
export const ABILITY_GUIDE: Record<Ability, { short: string; affects: string }> = {
|
||
STR: {
|
||
short: 'Physical power',
|
||
affects: 'Strength — hitting and damage with heavy melee weapons, the Athletics skill (climbing, jumping, grappling), and Strength saves.',
|
||
},
|
||
DEX: {
|
||
short: 'Agility and reflexes',
|
||
affects:
|
||
'Dexterity — your Armor Class (how hard you are to hit), your Initiative (how early you act), attacks with bows and light blades, and Acrobatics, Sleight of Hand and Stealth.',
|
||
},
|
||
CON: {
|
||
short: 'Health and stamina',
|
||
affects: 'Constitution — your hit points, at level 1 and again every level after. Also Constitution saves. Useful for everyone.',
|
||
},
|
||
INT: {
|
||
short: 'Memory and reasoning',
|
||
affects: 'Intelligence — the Arcana, History, Investigation, Nature and Religion skills, and a Wizard’s spells.',
|
||
},
|
||
WIS: {
|
||
short: 'Awareness and intuition',
|
||
affects:
|
||
'Wisdom — noticing things (Perception and Passive Perception), plus Insight, Medicine, Survival and Animal Handling, and the spells of Clerics, Druids and Rangers.',
|
||
},
|
||
CHA: {
|
||
short: 'Force of personality',
|
||
affects: 'Charisma — Persuasion, Deception, Intimidation and Performance, and the spells of Bards, Paladins, Sorcerers and Warlocks.',
|
||
},
|
||
};
|
||
|
||
export const SKILL_GUIDE: Record<string, { ability: Ability; what: string }> = {
|
||
ACROBATICS: { ability: 'DEX', what: 'Staying on your feet: balancing, tumbling, flips.' },
|
||
ANIMAL_HANDLING: { ability: 'WIS', what: 'Calming, controlling or reading the mood of an animal.' },
|
||
ARCANA: { ability: 'INT', what: 'Knowing about magic, spells and magical creatures.' },
|
||
ATHLETICS: { ability: 'STR', what: 'Climbing, jumping, swimming and wrestling.' },
|
||
DECEPTION: { ability: 'CHA', what: 'Lying convincingly and hiding the truth.' },
|
||
HISTORY: { ability: 'INT', what: 'Recalling past events, kingdoms, wars and legends.' },
|
||
INSIGHT: { ability: 'WIS', what: 'Reading people: spotting lies and sensing intentions.' },
|
||
INTIMIDATION: { ability: 'CHA', what: 'Getting your way through threats and menace.' },
|
||
INVESTIGATION: { ability: 'INT', what: 'Searching for clues and working out what they mean.' },
|
||
MEDICINE: { ability: 'WIS', what: 'Steadying the dying and diagnosing illness.' },
|
||
NATURE: { ability: 'INT', what: 'Knowing about plants, animals, weather and terrain.' },
|
||
PERCEPTION: { ability: 'WIS', what: 'Noticing things: hidden enemies, traps, faint sounds. Rolled more than any other skill.' },
|
||
PERFORMANCE: { ability: 'CHA', what: 'Entertaining a crowd with music, acting or storytelling.' },
|
||
PERSUASION: { ability: 'CHA', what: 'Winning people over with honest charm and good arguments.' },
|
||
RELIGION: { ability: 'INT', what: 'Knowing about gods, rites, holy symbols and cults.' },
|
||
SLEIGHT_OF_HAND: { ability: 'DEX', what: 'Picking pockets, palming objects, planting things unseen.' },
|
||
STEALTH: { ability: 'DEX', what: 'Moving quietly and staying hidden.' },
|
||
SURVIVAL: { ability: 'WIS', what: 'Tracking, finding food and shelter, not getting lost.' },
|
||
};
|
||
|
||
// One line under each step heading: what this step is and why it matters.
|
||
export const STEP_GUIDE = {
|
||
SPECIES: 'Your species is who your character was born as. It nudges a few ability scores up and sets how fast you move.',
|
||
CLASS: 'Your class is what your character does — warrior, spellcaster, sneak. It sets your hit points, which saves you are good at, your skills, and whether you cast spells.',
|
||
MOTIVATION: 'Why is your character out adventuring? This is pure story — it changes no numbers, but it gives you something to play.',
|
||
SCORES:
|
||
'Six numbers that describe your character. Higher is better: 10 is average, and every 2 points above 10 adds +1 to related rolls. You roll six scores, then decide which ability gets which.',
|
||
SKILLS: 'Skills are things your character is trained in. When you roll for one of these, you add your proficiency bonus on top.',
|
||
ABILITIES:
|
||
'Special abilities are the tricks that make your character more than numbers. Your species and class each grant some automatically, and you pick a couple of talents of your own. Your DM applies them at the table.',
|
||
BACKGROUND: 'Who was your character before the adventure began? Pick one answer to each question. It changes no numbers — it tells you and your DM who you are playing.',
|
||
APPEARANCE: 'What do people see when your character walks in? Pick from each list to build the description.',
|
||
EQUIPMENT: 'What you set out with. Your DM decides what is on offer here, and can hand out more as you play.',
|
||
} as const;
|
||
|
||
export const STEP_NAMES = ['Species', 'Class', 'Motivation', 'Scores', 'Skills', 'Abilities', 'Background', 'Appearance', 'Equipment'] as const;
|