You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.6 KiB
136 lines
3.6 KiB
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
import { readFile, writeFile, readdir } from 'fs/promises';
|
|
import { join } from 'path';
|
|
|
|
export interface GameCharacter {
|
|
health: number;
|
|
armor?: number;
|
|
unstable?: boolean;
|
|
luck: number;
|
|
luckSpecial?: string;
|
|
experience: number;
|
|
|
|
Charm?: number;
|
|
Cool?: number;
|
|
Sharp?: number;
|
|
Tough?: number;
|
|
Weird?: number;
|
|
|
|
color: number;
|
|
defaultFacePath: string;
|
|
additionalFaces?: GameCharacterFace[];
|
|
activeFaceSets?: string[];
|
|
moves?: GameCharacterMove[];
|
|
improvementsTaken?: string[];
|
|
improvementsAvailable?: string[];
|
|
improvementsAdvanced?: string[];
|
|
}
|
|
|
|
export enum GameAttribute {
|
|
Charm = 'Charm',
|
|
Cool = 'Cool',
|
|
Sharp = 'Sharp',
|
|
Tough = 'Tough',
|
|
Weird = 'Weird'
|
|
}
|
|
|
|
export const GameAttributes = [
|
|
GameAttribute.Charm,
|
|
GameAttribute.Cool,
|
|
GameAttribute.Sharp,
|
|
GameAttribute.Tough,
|
|
GameAttribute.Weird
|
|
] as const;
|
|
|
|
export interface GameCharacterMoveBase {
|
|
type?: string;
|
|
name: string;
|
|
summary?: string;
|
|
description?: string;
|
|
}
|
|
|
|
export interface GameCharacterPassiveMove extends GameCharacterMoveBase {
|
|
type?: 'passive';
|
|
}
|
|
|
|
export interface GameCharacterRollableMove extends GameCharacterMoveBase {
|
|
type: 'rollable';
|
|
attribute?: GameAttribute;
|
|
bonus?: number;
|
|
advanced?: boolean;
|
|
onAdvanced?: string;
|
|
onSuccess?: string;
|
|
onMixed?: string;
|
|
onMiss?: string;
|
|
}
|
|
|
|
export type GameCharacterMove = GameCharacterPassiveMove | GameCharacterRollableMove;
|
|
|
|
export interface FaceConditionBase {
|
|
type: string;
|
|
negated?: boolean;
|
|
}
|
|
|
|
export interface FaceConditionStability extends FaceConditionBase {
|
|
type: 'stable' | 'unstable' | 'dead';
|
|
}
|
|
|
|
export interface FaceConditionHealth extends FaceConditionBase {
|
|
type: 'hpEq' | 'hpGt' | 'hpLt' | 'hpGtEq' | 'hpLtEq';
|
|
threshold: number;
|
|
}
|
|
|
|
export interface FaceConditionHealthDelta extends FaceConditionBase {
|
|
type: 'beingHealed' | 'beingDamaged' | 'healthSteady';
|
|
}
|
|
|
|
export interface FaceConditionSet extends FaceConditionBase {
|
|
type: 'faceSetActive';
|
|
set: string;
|
|
}
|
|
|
|
export type FaceCondition = FaceConditionStability | FaceConditionHealth | FaceConditionHealthDelta | FaceConditionSet;
|
|
|
|
export interface GameCharacterFace {
|
|
path: string;
|
|
conditions: FaceCondition[];
|
|
}
|
|
|
|
export const FaceSetIdentifier = /^[a-z0-9_]+$/;
|
|
|
|
export async function listCharacters(dataDir: string): Promise<string[]> {
|
|
const list = await readdir(join(dataDir, 'characters'));
|
|
return list.filter((s) => s.endsWith('.yaml')).map((s) => s.substring(0, s.length - 5));
|
|
}
|
|
|
|
export async function loadCharacter(dataDir: string, name: string): Promise<GameCharacter> {
|
|
const contents = await readFile(join(dataDir, 'characters', name + '.yaml'), { encoding: 'utf-8' });
|
|
return parseYaml(contents);
|
|
}
|
|
|
|
export async function saveCharacter(dataDir: string, name: string, character: GameCharacter): Promise<void> {
|
|
const contents = stringifyYaml(character);
|
|
return writeFile(join(dataDir, 'characters', name + '.yaml'), contents, { encoding: 'utf-8' });
|
|
}
|
|
|
|
export interface GameParty {
|
|
defaultCharacters: Record<string, string>;
|
|
activeParty: string[];
|
|
keeper: string;
|
|
}
|
|
|
|
export interface ReadonlyGameParty {
|
|
readonly defaultCharacters: Readonly<Record<string, string>>;
|
|
readonly activeParty: readonly string[];
|
|
readonly keeper: string;
|
|
}
|
|
|
|
export async function loadParty(dataDir: string): Promise<GameParty> {
|
|
const contents = await readFile(join(dataDir, 'party.yaml'), { encoding: 'utf-8' });
|
|
return parseYaml(contents);
|
|
}
|
|
|
|
export async function saveParty(dataDir: string, party: GameParty): Promise<void> {
|
|
const contents = stringifyYaml(party);
|
|
return writeFile(join(dataDir, 'party.yaml'), contents, { encoding: 'utf-8' });
|
|
}
|
|
|