248 lines
8.6 KiB
TypeScript
248 lines
8.6 KiB
TypeScript
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 | null;
|
|
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;
|
|
}; |