You've already forked obsidian-visualiser
Add CharacterBuilder class to unify and compile the features
This commit is contained in:
175
types/character.d.ts
vendored
Normal file
175
types/character.d.ts
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
import type { MAIN_STATS, ABILITIES, LEVELS, TRAINING_LEVELS, SPELL_TYPES, CATEGORIES, SPELL_ELEMENTS } from "~/shared/character";
|
||||
|
||||
export type MainStat = typeof MAIN_STATS[number];
|
||||
export type Ability = typeof ABILITIES[number];
|
||||
export type Level = typeof LEVELS[number];
|
||||
export type TrainingLevel = typeof TRAINING_LEVELS[number];
|
||||
export type SpellType = typeof SPELL_TYPES[number];
|
||||
export type Category = typeof CATEGORIES[number];
|
||||
export type SpellElement = typeof SPELL_ELEMENTS[number];
|
||||
|
||||
export type DoubleIndex<T extends number | string> = [T, number];
|
||||
|
||||
export type Character = {
|
||||
id: number;
|
||||
|
||||
name: string;
|
||||
people?: number;
|
||||
level: number;
|
||||
aspect?: number;
|
||||
notes?: string | null;
|
||||
health: number;
|
||||
mana: number;
|
||||
|
||||
training: Record<MainStat, DoubleIndex<TrainingLevel>[]>;
|
||||
leveling: DoubleIndex<Level>[];
|
||||
abilities: Partial<Record<Ability, [number, number]>>; //First is the ability, second is the max increment
|
||||
spells: string[]; //Spell ID
|
||||
modifiers: Partial<Record<MainStat, number>>;
|
||||
|
||||
choices: Record<string, number[]>;
|
||||
|
||||
owner: number;
|
||||
username?: string;
|
||||
visibility: "private" | "public";
|
||||
};
|
||||
export type CharacterValues = {
|
||||
health: number;
|
||||
mana: number;
|
||||
};
|
||||
export type CharacterConfig = {
|
||||
peoples: Race[],
|
||||
training: Record<MainStat, Record<TrainingLevel, TrainingOption[]>>;
|
||||
abilities: Record<Ability, AbilityConfig>;
|
||||
spells: SpellConfig[];
|
||||
};
|
||||
export type SpellConfig = {
|
||||
id: string;
|
||||
name: string;
|
||||
rank: 1 | 2 | 3 | 4;
|
||||
type: SpellType;
|
||||
cost: number;
|
||||
speed: "action" | "reaction" | number;
|
||||
elements: Array<SpellElement>;
|
||||
effect: string;
|
||||
tags?: string[];
|
||||
};
|
||||
export type AbilityConfig = {
|
||||
max: [MainStat, MainStat];
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
export type Race = {
|
||||
name: string;
|
||||
description: string;
|
||||
options: Record<Level, Feature[]>;
|
||||
};
|
||||
|
||||
export type FeatureEffect = {
|
||||
category: "value";
|
||||
operation: "add" | "set";
|
||||
property: string;
|
||||
value: number | `modifier/${MainStat}`;
|
||||
} | {
|
||||
category: "feature";
|
||||
kind: "action" | "reaction" | "freeaction" | "passive";
|
||||
text: string;
|
||||
} | {
|
||||
category: "list";
|
||||
list: "spells";
|
||||
action: "add" | "remove";
|
||||
item: string;
|
||||
};
|
||||
export type FeatureItem = FeatureEffect | {
|
||||
category: "choice";
|
||||
id: string;
|
||||
settings?: { //If undefined, amount is 1 by default
|
||||
amount: number;
|
||||
exclusive: boolean; //Disallow to pick the same option twice
|
||||
};
|
||||
options: FeatureEffect[];
|
||||
}
|
||||
export type Feature = {
|
||||
name?: string;
|
||||
description: string;
|
||||
effect: FeatureItem[];
|
||||
};
|
||||
|
||||
export type TrainingOption = {
|
||||
description: Array<{
|
||||
text: string;
|
||||
disposable?: boolean;
|
||||
replaced?: boolean;
|
||||
category?: Category;
|
||||
}>;
|
||||
|
||||
//Automatically calculated by compiler
|
||||
mana?: number;
|
||||
health?: number;
|
||||
speed?: false | number;
|
||||
initiative?: number;
|
||||
mastery?: keyof CompiledCharacter["mastery"];
|
||||
spellrank?: SpellType;
|
||||
defense?: Array<keyof CompiledCharacter["defense"]>;
|
||||
resistance?: Record<MainStat, number>;
|
||||
bonus?: Record<string, number>;
|
||||
spell?: string;
|
||||
|
||||
//Used during character creation, not used by compiler
|
||||
modifier?: number;
|
||||
ability?: number;
|
||||
spec?: number;
|
||||
spellslot?: number | MainStat;
|
||||
arts?: number | MainStat;
|
||||
|
||||
features?: FeatureItem[]; //TODO
|
||||
};
|
||||
export type CompiledCharacter = {
|
||||
id: number;
|
||||
owner?: number;
|
||||
username?: string;
|
||||
name: string;
|
||||
health: number;
|
||||
mana: number;
|
||||
race: number;
|
||||
spellslots: number;
|
||||
artslots: number;
|
||||
spellranks: Record<SpellType, 0 | 1 | 2 | 3>;
|
||||
aspect: string;
|
||||
speed: number | false;
|
||||
initiative: number;
|
||||
spells: string[];
|
||||
|
||||
values: CharacterValues,
|
||||
|
||||
defense: {
|
||||
hardcap: number;
|
||||
static: number;
|
||||
activeparry: number;
|
||||
activedodge: number;
|
||||
passiveparry: number;
|
||||
passivedodge: number;
|
||||
};
|
||||
|
||||
mastery: {
|
||||
strength: number;
|
||||
dexterity: number;
|
||||
shield: number;
|
||||
armor: number;
|
||||
multiattack: number;
|
||||
magicpower: number;
|
||||
magicspeed: number;
|
||||
magicelement: number;
|
||||
magicinstinct: number;
|
||||
};
|
||||
|
||||
bonus: Record<string, number>; //Any special bonus goes here
|
||||
resistance: Record<string, number>;
|
||||
|
||||
modifier: Record<MainStat, number>;
|
||||
abilities: Partial<Record<Ability, number>>;
|
||||
level: number;
|
||||
features: { [K in Extract<FeatureEffect, { category: "feature" }>["kind"]]: string[] };
|
||||
|
||||
notes: string;
|
||||
};
|
||||
@@ -1,248 +0,0 @@
|
||||
import { z, type ZodRawShape } from "zod/v4";
|
||||
|
||||
export const MAIN_STATS = ["strength","dexterity","constitution","intelligence","curiosity","charisma","psyche"] as const; export type MainStat = typeof MAIN_STATS[number];
|
||||
export const ABILITIES = ["athletics","acrobatics","intimidation","sleightofhand","stealth","survival","investigation","history","religion","arcana","understanding","perception","performance","medecine","persuasion","animalhandling","deception"] as const; export type Ability = typeof ABILITIES[number];
|
||||
export const LEVELS = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] as const; export type Level = typeof LEVELS[number];
|
||||
export const TRAINING_LEVELS = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] as const; export type TrainingLevel = typeof TRAINING_LEVELS[number];
|
||||
export const SPELL_TYPES = ["precision","knowledge","instinct","arts"] as const; export type SpellType = typeof SPELL_TYPES[number];
|
||||
export const CATEGORIES = ["action","reaction","freeaction","misc"] as const; export type Category = typeof CATEGORIES[number];
|
||||
export const SPELL_ELEMENTS = ["fire","ice","thunder","earth","arcana","air","nature","light","psyche"] as const; export type SpellElement = typeof SPELL_ELEMENTS[number];
|
||||
|
||||
export type DoubleIndex<T extends number | string> = [T, number];
|
||||
|
||||
export const defaultCharacter: Character = {
|
||||
id: -1,
|
||||
|
||||
name: "",
|
||||
people: undefined,
|
||||
level: 1,
|
||||
health: 0,
|
||||
mana: 0,
|
||||
|
||||
training: MAIN_STATS.reduce((p, v) => { p[v] = [[0, 0]]; return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
||||
leveling: [[1, 0]],
|
||||
abilities: {},
|
||||
spells: [],
|
||||
modifiers: {},
|
||||
|
||||
owner: -1,
|
||||
visibility: "private",
|
||||
};
|
||||
export const mainStatTexts: Record<MainStat, string> = {
|
||||
"strength": "Force",
|
||||
"dexterity": "Dextérité",
|
||||
"constitution": "Constitution",
|
||||
"intelligence": "Intelligence",
|
||||
"curiosity": "Curiosité",
|
||||
"charisma": "Charisme",
|
||||
"psyche": "Psyché",
|
||||
};
|
||||
export const elementTexts: Record<SpellElement, { class: string, text: string }> = {
|
||||
fire: { class: 'text-light-red dark:text-dark-red border-light-red dark:border-dark-red bg-light-red dark:bg-dark-red', text: 'Feu' },
|
||||
ice: { class: 'text-light-blue dark:text-dark-blue border-light-blue dark:border-dark-blue bg-light-blue dark:bg-dark-blue', text: 'Glace' },
|
||||
thunder: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow', text: 'Foudre' },
|
||||
earth: { class: 'text-light-orange dark:text-dark-orange border-light-orange dark:border-dark-orange bg-light-orange dark:bg-dark-orange', text: 'Terre' },
|
||||
arcana: { class: 'text-light-indigo dark:text-dark-indigo border-light-indigo dark:border-dark-indigo bg-light-indigo dark:bg-dark-indigo', text: 'Arcane' },
|
||||
air: { class: 'text-light-lime dark:text-dark-lime border-light-lime dark:border-dark-lime bg-light-lime dark:bg-dark-lime', text: 'Air' },
|
||||
nature: { class: 'text-light-green dark:text-dark-green border-light-green dark:border-dark-green bg-light-green dark:bg-dark-green', text: 'Nature' },
|
||||
light: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow', text: 'Lumière' },
|
||||
psyche: { class: 'text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-purple bg-light-purple dark:bg-dark-purple', text: 'Psy' },
|
||||
};
|
||||
export const spellTypeTexts: Record<SpellType, string> = { "instinct": "Instinct", "knowledge": "Savoir", "precision": "Précision", "arts": "Oeuvres" };
|
||||
|
||||
export const CharacterValidation = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
people: z.number().nullable(),
|
||||
level: z.number().min(1).max(20),
|
||||
aspect: z.number().nullable().optional(),
|
||||
notes: z.string().nullable().optional(),
|
||||
health: z.number().default(0),
|
||||
mana: z.number().default(0),
|
||||
training: z.object(MAIN_STATS.reduce((p, v) => {
|
||||
p[v] = z.array(z.tuple([z.number().min(0).max(15), z.number()]));
|
||||
return p;
|
||||
}, {} as Record<MainStat, z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>)),
|
||||
leveling: z.array(z.tuple([z.number().min(1).max(20), z.number()])),
|
||||
abilities: z.object(ABILITIES.reduce((p, v) => {
|
||||
p[v] = z.tuple([z.number(), z.number()]);
|
||||
return p;
|
||||
}, {} as Record<Ability, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>)).partial(),
|
||||
spells: z.string().array(),
|
||||
modifiers: z.object(MAIN_STATS.reduce((p, v) => {
|
||||
p[v] = z.number();
|
||||
return p;
|
||||
}, {} as Record<MainStat, z.ZodNumber>)).partial(),
|
||||
owner: z.number(),
|
||||
username: z.string().optional(),
|
||||
visibility: z.enum(["public", "private"]),
|
||||
thumbnail: z.any(),
|
||||
});
|
||||
export type Character = {
|
||||
id: number;
|
||||
|
||||
name: string;
|
||||
people?: number;
|
||||
level: number;
|
||||
aspect?: number;
|
||||
notes?: string | null;
|
||||
health: number;
|
||||
mana: number;
|
||||
|
||||
training: Record<MainStat, DoubleIndex<TrainingLevel>[]>;
|
||||
leveling: DoubleIndex<Level>[];
|
||||
abilities: Partial<Record<Ability, [number, number]>>; //First is the ability, second is the max increment
|
||||
spells: string[]; //Spell ID
|
||||
modifiers: Partial<Record<MainStat, number>>;
|
||||
|
||||
owner: number;
|
||||
username?: string;
|
||||
visibility: "private" | "public";
|
||||
};
|
||||
export type CharacterValues = {
|
||||
health: number;
|
||||
mana: number;
|
||||
};
|
||||
export type CharacterConfig = {
|
||||
peoples: Race[],
|
||||
training: Record<MainStat, Record<TrainingLevel, TrainingOption[]>>;
|
||||
abilities: Record<Ability, AbilityConfig>;
|
||||
spells: SpellConfig[];
|
||||
};
|
||||
export type SpellConfig = {
|
||||
id: string;
|
||||
name: string;
|
||||
rank: 1 | 2 | 3 | 4;
|
||||
type: SpellType;
|
||||
cost: number;
|
||||
speed: "action" | "reaction" | number;
|
||||
elements: Array<SpellElement>;
|
||||
effect: string;
|
||||
tags?: string[];
|
||||
};
|
||||
export type AbilityConfig = {
|
||||
max: [MainStat, MainStat];
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
export type Race = {
|
||||
name: string;
|
||||
description: string;
|
||||
utils: {
|
||||
maxOption: number;
|
||||
};
|
||||
options: Record<Level, RaceOption[]>;
|
||||
};
|
||||
export type RaceOption = {
|
||||
training?: number;
|
||||
health?: number;
|
||||
mana?: number;
|
||||
shaping?: number;
|
||||
modifier?: number;
|
||||
abilities?: number;
|
||||
spellslots?: number;
|
||||
};
|
||||
export type FeatureItem = {
|
||||
category: "misc";
|
||||
text: string;
|
||||
} | {
|
||||
category: "action";
|
||||
cost: 1 | 2 | 3;
|
||||
text: string;
|
||||
} | {
|
||||
category: "reaction";
|
||||
cost: 1 | 2;
|
||||
text: string;
|
||||
} | {
|
||||
category: "freeaction";
|
||||
text: string;
|
||||
} | {
|
||||
category: "value";
|
||||
type: "add" | "remove" | "set";
|
||||
value: number | false;
|
||||
property: string;
|
||||
} | {
|
||||
category: "asset";
|
||||
type: "add" | "remove";
|
||||
kind: "spells";
|
||||
asset: string;
|
||||
}
|
||||
type FeatureCategory = FeatureItem["category"];
|
||||
export type TrainingOption = {
|
||||
description: Array<{
|
||||
text: string;
|
||||
disposable?: boolean;
|
||||
replaced?: boolean;
|
||||
category?: Category;
|
||||
}>;
|
||||
|
||||
//Automatically calculated by compiler
|
||||
mana?: number;
|
||||
health?: number;
|
||||
speed?: false | number;
|
||||
initiative?: number;
|
||||
mastery?: keyof CompiledCharacter["mastery"];
|
||||
spellrank?: SpellType;
|
||||
defense?: Array<keyof CompiledCharacter["defense"]>;
|
||||
resistance?: Record<MainStat, number>;
|
||||
bonus?: Record<string, number>;
|
||||
spell?: string;
|
||||
|
||||
//Used during character creation, not used by compiler
|
||||
modifier?: number;
|
||||
ability?: number;
|
||||
spec?: number;
|
||||
spellslot?: number | MainStat;
|
||||
arts?: number | MainStat;
|
||||
|
||||
features?: FeatureItem[]; //TODO
|
||||
};
|
||||
export type CompiledCharacter = {
|
||||
id: number;
|
||||
owner?: number;
|
||||
username?: string;
|
||||
name: string;
|
||||
health: number;
|
||||
mana: number;
|
||||
race: number;
|
||||
spellslots: number;
|
||||
artslots: number;
|
||||
spellranks: Record<SpellType, 0 | 1 | 2 | 3>;
|
||||
aspect: string;
|
||||
speed: number | false;
|
||||
initiative: number;
|
||||
spells: string[];
|
||||
|
||||
values: CharacterValues,
|
||||
|
||||
defense: {
|
||||
hardcap: number;
|
||||
static: number;
|
||||
activeparry: number;
|
||||
activedodge: number;
|
||||
passiveparry: number;
|
||||
passivedodge: number;
|
||||
};
|
||||
|
||||
mastery: {
|
||||
strength: number;
|
||||
dexterity: number;
|
||||
shield: number;
|
||||
armor: number;
|
||||
multiattack: number;
|
||||
magicpower: number;
|
||||
magicspeed: number;
|
||||
magicelement: number;
|
||||
magicinstinct: number;
|
||||
};
|
||||
|
||||
bonus: Record<string, number>; //Any special bonus goes here
|
||||
resistance: Record<MainStat, number>;
|
||||
|
||||
modifier: Record<MainStat, number>;
|
||||
abilities: Partial<Record<Ability, number>>;
|
||||
level: number;
|
||||
features: { [K in FeatureCategory]: Array<Extract<FeatureItem, { category: K }>> };
|
||||
|
||||
notes: string;
|
||||
};
|
||||
Reference in New Issue
Block a user