From 86cc3191ca83a335f648a42aec4a825af38e839c Mon Sep 17 00:00:00 2001 From: Nils Briggen Date: Tue, 9 Jun 2026 22:34:49 +0200 Subject: [PATCH] 5e builder: add curated official subclasses (start with Rogue Mastermind) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit classes.json ships the SRD subclass + third-party homebrew but is missing most official PHB/XGE archetypes. New DND5E_SUBCLASSES supplement is merged (deduped by name) ahead of the on-disk list in the builder, surviving any classes.json regeneration. First entry: Rogue → Mastermind. (Subclass feature mechanics remain a separate task; this adds the selectable options.) Co-Authored-By: Claude Opus 4.8 --- src/features/characters/builder/CreationWizard.tsx | 10 +++++++++- src/lib/rules/dnd5e/progression.ts | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/features/characters/builder/CreationWizard.tsx b/src/features/characters/builder/CreationWizard.tsx index 1804de9..ea729db 100644 --- a/src/features/characters/builder/CreationWizard.tsx +++ b/src/features/characters/builder/CreationWizard.tsx @@ -18,6 +18,7 @@ import { getClassTip, raceSynergyNote } from '@/lib/assistant/builder'; import { briefOverview, dedupeByName } from './overview'; import { parseAsiBonuses, makeSkillResolver, parseBackgroundSkills, parseTraitSkills } from './origin'; import { PF2E_SUBCLASSES } from '@/lib/rules/pf2e/progression'; +import { DND5E_SUBCLASSES } from '@/lib/rules/dnd5e/progression'; import { Modal } from '@/components/ui/Modal'; import { Button } from '@/components/ui/Button'; import { Badge } from '@/components/ui/Codex'; @@ -94,7 +95,14 @@ export function CreationWizard({ campaign, onClose }: { campaign: Campaign; onCl const caster = def && def.caster !== 'none' && def.spellAbility ? def.spellAbility : rc.caster; return { ...rc, caster, subclasses }; }) - : c; + : c.map((rc) => { + // Merge curated official subclasses (classes.json is missing most PHB/XGE + // archetypes) ahead of the on-disk list, deduped by name. + const extra = DND5E_SUBCLASSES[rc.name] ?? []; + if (!extra.length) return rc; + const have = new Set(rc.subclasses.map((s) => s.name.toLowerCase())); + return { ...rc, subclasses: [...extra.filter((s) => !have.has(s.name.toLowerCase())), ...rc.subclasses] }; + }); setClasses([...enriched].sort((a, b) => a.name.localeCompare(b.name))); }); return () => { on = false; }; diff --git a/src/lib/rules/dnd5e/progression.ts b/src/lib/rules/dnd5e/progression.ts index 5c28cdc..f52bf78 100644 --- a/src/lib/rules/dnd5e/progression.ts +++ b/src/lib/rules/dnd5e/progression.ts @@ -99,3 +99,16 @@ export function dnd5eAsiLevels(className: string): number[] { if (c === 'rogue') return [...base, 10].sort((a, b) => a - b); return base; } + +/** + * Curated official 5e subclasses, merged into the builder's class list on top of + * whatever the on-disk classes.json carries (which is SRD + third-party homebrew and + * is missing most PHB/XGE archetypes). Keyed by class name; deduped by name at merge + * time. Descriptions are short "what it plays like" blurbs — subclass *features* are + * not yet auto-applied (a separate task), so these add the missing options to pick. + */ +export const DND5E_SUBCLASSES: Record = { + Rogue: [ + { name: 'Mastermind', desc: 'A master of intrigue and tactics: extra tool/language proficiencies, Help as a bonus action at range, and uncanny insight into a creature’s drives.' }, + ], +};