/** Portable TTRPG system / character contract (builder + player + future mobile). */ export const SYSTEM_FORMAT = 'ttrpg-system' as const; export const CHARACTER_FORMAT = 'ttrpg-character' as const; export const FORMAT_VERSION = 1 as const; export type ScaleKind = 'numeric' | 'dice' | 'percent' | 'rank' | 'pool' | 'boolean'; export type AttributeDefinition = { key: string; label: string; scale: ScaleKind; min?: number; max?: number; default?: number | string | boolean; category?: 'physical' | 'mental' | 'social' | 'other'; costPerPoint?: number; }; export type SkillDefinition = { key: string; label: string; linkedAttribute?: string; scale: ScaleKind; min?: number; max?: number; default?: number | string | boolean; }; export type ResourceDefinition = { key: string; label: string; min?: number; max?: number | string; default?: number; track?: boolean; }; export type TraitDefinition = { key: string; label: string; description?: string; tags?: string[]; prerequisites?: string[]; }; export type DerivedDefinition = { key: string; label: string; formula: string; dependsOn: string[]; round?: 'floor' | 'ceil' | 'round' | 'none'; }; export type ProgressionNode = { id: string; label: string; kind: 'career' | 'feat' | 'talent' | 'other'; traitKey?: string; position?: { x: number; y: number }; }; export type ProgressionEdge = { id: string; source: string; target: string; kind?: 'prerequisite' | 'exit' | 'unlock'; }; export type ProgressionDefinition = { label?: string; graph: { nodes: ProgressionNode[]; edges: ProgressionEdge[]; }; }; export type CreationStrategy = | 'point_buy' | 'roll' | 'array' | 'priority' | 'life_path' | 'funnel'; export type CreationRule = { id: string; type: CreationStrategy; appliesTo: 'attribute' | 'skill' | 'any'; params: Record; }; export type WidgetType = | 'text' | 'number' | 'modBadge' | 'skillRow' | 'skillList' | 'resourceBar' | 'checkbox' | 'tagList' | 'section' | 'portrait' | 'notes' | 'table' | 'repeatingTable' | 'statBlock' | 'dots' | 'percent' | 'rankEnum' | 'track' | 'clock' | 'slotMatrix' | 'diceStep' | 'locationBar' | 'spellCards' | 'unknownWidget'; export type WidgetFrame = { x: number; y: number; w: number; h: number }; export type SheetWidget = { id: string; type: WidgetType; bind?: string; frame: WidgetFrame; responsive?: { sm?: Partial & { hide?: boolean; stack?: boolean } }; props?: Record; }; export type SheetPage = { id: string; label: string; widgets: SheetWidget[]; }; export type SheetLayout = { pageSize: 'letter' | 'a4' | 'fluid' | { w: number; h: number }; grid: { size: number; columns?: number }; pages: SheetPage[]; }; export type CompendiumSpell = { id: string; name: string; level: number; school?: string; source?: string; classes?: string[]; castingTime?: string; range?: string; components?: string; duration?: string; description?: string; tags?: string[]; }; export type SystemBricks = { attributes?: AttributeDefinition[]; skills?: SkillDefinition[]; resources?: ResourceDefinition[]; traits?: TraitDefinition[]; derived?: DerivedDefinition[]; progression?: ProgressionDefinition; constraints?: Record[]; }; export type SystemDefinition = { format: typeof SYSTEM_FORMAT; formatVersion: typeof FORMAT_VERSION; id: string; name: string; description?: string; edition?: string; schemaVersion: number; revision: number; bricks: SystemBricks; creationRules: CreationRule[]; sheetLayout: SheetLayout; compendium?: { spells?: CompendiumSpell[]; }; theme?: { tokenOverrides?: Record }; }; export type CharacterValues = { meta?: Record; attributes?: Record; skills?: Record; resources?: Record; traits?: string[]; ownedTraitIds?: string[]; spells?: Array<{ id: string; prepared?: boolean; notes?: string }>; inventory?: Array>; derived?: Record; [key: string]: unknown; }; export type CharacterSheet = { format: typeof CHARACTER_FORMAT; formatVersion: typeof FORMAT_VERSION; id: string; name: string; systemId: string; schemaVersion: number; revisionAtCreate: number; values: CharacterValues; creation?: { strategy?: CreationStrategy; seed?: number; rolls?: unknown; }; }; export type ValidationIssue = { path: string; message: string; code: string; }; export type ValidationResult = { ok: boolean; errors: ValidationIssue[]; };