d1f180ee0a
Offline Electron app seeds 10 system presets, supports create-from-scratch designer with palette/drag/resize/bricks, and exports .ttrpg-system.json for the player. Co-authored-by: Cursor <cursoragent@cursor.com>
224 lines
4.7 KiB
TypeScript
224 lines
4.7 KiB
TypeScript
/** 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<string, unknown>;
|
|
};
|
|
|
|
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<WidgetFrame> & { hide?: boolean; stack?: boolean } };
|
|
props?: Record<string, unknown>;
|
|
};
|
|
|
|
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<string, unknown>[];
|
|
};
|
|
|
|
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<string, string> };
|
|
};
|
|
|
|
export type CharacterValues = {
|
|
meta?: Record<string, unknown>;
|
|
attributes?: Record<string, number | string | boolean>;
|
|
skills?: Record<string, number | string | boolean>;
|
|
resources?: Record<string, number>;
|
|
traits?: string[];
|
|
ownedTraitIds?: string[];
|
|
spells?: Array<{ id: string; prepared?: boolean; notes?: string }>;
|
|
inventory?: Array<Record<string, unknown>>;
|
|
derived?: Record<string, number>;
|
|
[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[];
|
|
};
|