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.
 
 
 
motw-tracker/src/character.ts

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' });
}