Deepen feature engine: full 5e subclass depth + PF2e parity
No-shortcuts pass on the class feature/choice engine, and PF2e brought level with 5e. 5e subclass depth (subclass.5e.ts): - SUBCLASS_PROGRESSION_5E: per-level features for ~22 subclasses (SRD cores + popular), and subclass-specific CHOICES — Battle Master maneuvers (3/5/7/9 by level), Arcane Archer shots, Rune Knight runes, Way of Four Elements disciplines, Hunter's selectable features (Hunter's Prey/Defensive Tactics/Multiattack/Superior Defense), Totem Spirit, Dragon Ancestor. collectFeatures5e now expands the chosen subclass into its real features. PF2e feature/choice engine (data.pf2e.ts) — full parity: - CLASS_PROGRESSION_PF2E: signature features by level for all 24 classes. - Universal choices generated for every class: Class feats (per-class levels incl. the 1st-level martial feat), Skill feats (even levels), General feats (3/7/11/15/19), Ancestry feats (1/5/9/13/17), plus the 1st-level subclass (Instinct/Doctrine/Bloodline/ Racket/…) from PF2E_SUBCLASSES with options. Engine generalized: collectChoices(system)/collectFeatures(system) dispatch 5e vs pf2e; ClassFeaturesSection now renders for BOTH systems (pf2e subclass resolves via choices[]). Verified in-app: PF2e Barbarian 5 → Instinct (6 options) + Class(3)/Skill(2)/General(1)/ Ancestry(2) feats = '9 to choose' + Rage/Deny Advantage; selecting Dragon Instinct resolves and shows as a feature. 5e Fighter+Battle Master → Maneuvers (choose 3) + Combat Superiority. 352 tests green (+ pf2e & subclass suites), build OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,19 +1,23 @@
|
||||
import { collectChoices5e, collectFeatures5e } from '@/lib/rules';
|
||||
import { collectChoices, collectFeatures } from '@/lib/rules';
|
||||
import type { ClassEntry } from '@/lib/schemas';
|
||||
import { Input, Select } from '@/components/ui/Input';
|
||||
import { Badge } from '@/components/ui/Codex';
|
||||
import { SheetSection, type SectionProps } from './common';
|
||||
|
||||
/**
|
||||
* Class features + every choice the character must make (Fighting Style, Pact Boon,
|
||||
* Eldritch Invocations, Metamagic, Expertise, …) so no unlock-able option is missed.
|
||||
* 5e only — driven by CLASS_PROGRESSION_5E.
|
||||
* Class features + every choice the character must make — 5e (Fighting Style, Pact Boon,
|
||||
* Invocations, Metamagic, Expertise, subclass maneuvers…) and PF2e (class/skill/general/
|
||||
* ancestry feats, instinct/doctrine/bloodline…) — so no unlock-able option is missed.
|
||||
*/
|
||||
export function ClassFeaturesSection({ c, update }: SectionProps) {
|
||||
if (c.system !== '5e') return null;
|
||||
const classes: ClassEntry[] = c.classes.length ? c.classes : c.className ? [{ className: c.className, level: c.level }] : [];
|
||||
const features = collectFeatures5e(classes);
|
||||
const choices = collectChoices5e(classes);
|
||||
// 5e multiclasses via classes[]; pf2e is single-class with its subclass resolved in choices[].
|
||||
const classes: ClassEntry[] = c.classes.length
|
||||
? c.classes
|
||||
: c.className
|
||||
? [{ className: c.className, level: c.level, ...(c.system === 'pf2e' ? { subclass: c.choices.find((ch) => ch.key.endsWith(':subclass'))?.values[0] } : {}) }]
|
||||
: [];
|
||||
const features = collectFeatures(c.system, classes);
|
||||
const choices = collectChoices(c.system, classes);
|
||||
if (features.length === 0 && choices.length === 0) return null;
|
||||
|
||||
const getVals = (key: string) => c.choices.find((ch) => ch.key === key)?.values ?? [];
|
||||
|
||||
Reference in New Issue
Block a user