Muse owns muse-shaped files now
This commit is contained in:
parent
3e2ab6ec73
commit
bf444ccb1a
23 changed files with 1288 additions and 201 deletions
361
src/define/index.ts
Normal file
361
src/define/index.ts
Normal file
|
@ -0,0 +1,361 @@
|
|||
import {
|
||||
type Ability,
|
||||
type Blueprint,
|
||||
type Item,
|
||||
type Method,
|
||||
type Resource,
|
||||
type Rule,
|
||||
type Species,
|
||||
parseAbility,
|
||||
parseBlueprint,
|
||||
parseGlossary,
|
||||
parseItem,
|
||||
parseMethod,
|
||||
parseResource,
|
||||
parseRule,
|
||||
parseSpecies,
|
||||
} from "@proscenium/playbill";
|
||||
import { z } from "zod";
|
||||
|
||||
const Base = z.object({
|
||||
$define: z.string(),
|
||||
});
|
||||
|
||||
const Info = Base.extend({
|
||||
id: z.string(), // TODO: Validate ID shapes
|
||||
name: z.string().default("Unnamed Component"),
|
||||
description: z.string().default("No description provided"),
|
||||
categories: z.array(z.string()).default([]),
|
||||
});
|
||||
|
||||
const AbilitySchema = Info.extend({
|
||||
$define: z.literal("ability"),
|
||||
type: z.string().default("action"),
|
||||
ap: z.number().int().default(0),
|
||||
hp: z.number().int().default(0),
|
||||
ep: z.number().int().default(0),
|
||||
xp: z.number().int().default(0),
|
||||
damage: z.number().int().default(0),
|
||||
damageType: z.string().default("phy"),
|
||||
roll: z.string().default("none"),
|
||||
});
|
||||
|
||||
const BlueprintSchema = Info.extend({
|
||||
$define: z.literal("blueprint"),
|
||||
species: z.string(),
|
||||
items: z.array(z.string()).default([]),
|
||||
abilities: z.array(z.string()).default([]),
|
||||
ap: z.number().int().default(0),
|
||||
hp: z.number().int().default(0),
|
||||
ep: z.number().int().default(0),
|
||||
xp: z.number().int().default(0),
|
||||
muscle: z.string().default("novice"),
|
||||
focus: z.string().default("novice"),
|
||||
knowledge: z.string().default("novice"),
|
||||
charm: z.string().default("novice"),
|
||||
cunning: z.string().default("novice"),
|
||||
spark: z.string().default("novice"),
|
||||
});
|
||||
|
||||
const GlossarySchema = Base.extend({
|
||||
$define: z.literal("glossary"),
|
||||
terms: z.record(z.string(), z.string()),
|
||||
});
|
||||
|
||||
const BaseItem = Info.extend({
|
||||
$define: z.literal("item"),
|
||||
rarity: z.string().default("common"),
|
||||
affinity: z.string().default("none"),
|
||||
damage: z.number().int().default(0),
|
||||
damageType: z.string().default("phy"),
|
||||
});
|
||||
|
||||
const ItemSchema = z.discriminatedUnion("type", [
|
||||
BaseItem.extend({
|
||||
type: z.literal("wielded"),
|
||||
hands: z.number().int().default(1),
|
||||
}),
|
||||
BaseItem.extend({
|
||||
type: z.literal("worn"),
|
||||
hands: z.number().int().default(1),
|
||||
}),
|
||||
BaseItem.extend({
|
||||
type: z.literal("trinket"),
|
||||
}),
|
||||
]);
|
||||
|
||||
const MethodSchema = Info.extend({
|
||||
$define: z.literal("method"),
|
||||
curator: z.string().default("Someone"),
|
||||
abilities: z.array(z.array(z.string())).default([]),
|
||||
});
|
||||
|
||||
const ResourceSchema = z.discriminatedUnion("type", [
|
||||
Info.extend({
|
||||
$define: z.literal("resource"),
|
||||
type: z.literal("text"),
|
||||
}),
|
||||
Info.extend({
|
||||
$define: z.literal("resource"),
|
||||
type: z.literal("image"),
|
||||
url: z.string(),
|
||||
}),
|
||||
Info.extend({
|
||||
$define: z.literal("resource"),
|
||||
type: z.literal("table"),
|
||||
data: z.array(z.array(z.string())),
|
||||
}),
|
||||
]);
|
||||
|
||||
const RuleSchema = Info.extend({
|
||||
$define: z.literal("rule"),
|
||||
overrule: z.nullable(z.string()).default(null),
|
||||
order: z.number().int().default(Number.POSITIVE_INFINITY),
|
||||
});
|
||||
|
||||
const SpeciesSchema = Info.extend({
|
||||
$define: z.literal("species"),
|
||||
hands: z.number().int().default(2),
|
||||
abilities: z.array(z.string()).default([]),
|
||||
ap: z.number().int().default(0),
|
||||
hp: z.number().int().default(0),
|
||||
ep: z.number().int().default(0),
|
||||
xp: z.number().int().default(0),
|
||||
muscle: z.string().default("novice"),
|
||||
focus: z.string().default("novice"),
|
||||
knowledge: z.string().default("novice"),
|
||||
charm: z.string().default("novice"),
|
||||
cunning: z.string().default("novice"),
|
||||
spark: z.string().default("novice"),
|
||||
});
|
||||
|
||||
const SchemaMapping = {
|
||||
ability: AbilitySchema,
|
||||
blueprint: BlueprintSchema,
|
||||
glossary: GlossarySchema,
|
||||
item: ItemSchema,
|
||||
method: MethodSchema,
|
||||
resource: ResourceSchema,
|
||||
rule: RuleSchema,
|
||||
species: SpeciesSchema,
|
||||
} as const;
|
||||
|
||||
type ComponentType = keyof typeof SchemaMapping;
|
||||
|
||||
const parseComponentType = (val: unknown): ComponentType => {
|
||||
if (typeof val !== "string") {
|
||||
throw new Error("Component type must be a string");
|
||||
}
|
||||
|
||||
if (!(val in SchemaMapping)) {
|
||||
throw new Error(`Unknown component type: ${val}`);
|
||||
}
|
||||
|
||||
return val as ComponentType;
|
||||
};
|
||||
|
||||
export type ParsedComponent =
|
||||
| {
|
||||
type: "ability";
|
||||
component: Ability;
|
||||
}
|
||||
| {
|
||||
type: "blueprint";
|
||||
component: Blueprint;
|
||||
}
|
||||
| {
|
||||
type: "glossary";
|
||||
component: Record<string, string[]>;
|
||||
}
|
||||
| {
|
||||
type: "item";
|
||||
component: Item;
|
||||
}
|
||||
| {
|
||||
type: "method";
|
||||
component: Method;
|
||||
}
|
||||
| {
|
||||
type: "resource";
|
||||
component: Resource;
|
||||
}
|
||||
| {
|
||||
type: "rule";
|
||||
component: Rule;
|
||||
}
|
||||
| {
|
||||
type: "species";
|
||||
component: Species;
|
||||
};
|
||||
|
||||
function parseAbilityDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = AbilitySchema.parse(obj);
|
||||
|
||||
const ability = parseAbility({
|
||||
id: parsed.id,
|
||||
name: parsed.name,
|
||||
description: parsed.description,
|
||||
categories: parsed.categories,
|
||||
type: parsed.type,
|
||||
ap: parsed.ap,
|
||||
ep: parsed.ep,
|
||||
hp: parsed.hp,
|
||||
xp: parsed.xp,
|
||||
damage:
|
||||
parsed.damage > 0
|
||||
? {
|
||||
amount: parsed.damage,
|
||||
type: parsed.damageType,
|
||||
}
|
||||
: null,
|
||||
roll: parsed.roll,
|
||||
boons: [],
|
||||
banes: [],
|
||||
});
|
||||
|
||||
return {
|
||||
type: "ability",
|
||||
component: ability,
|
||||
};
|
||||
}
|
||||
|
||||
function parseBlueprintDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = BlueprintSchema.parse(obj);
|
||||
|
||||
const blueprint = parseBlueprint({
|
||||
id: parsed.id,
|
||||
name: parsed.name,
|
||||
description: parsed.description,
|
||||
categories: parsed.categories,
|
||||
species: parsed.species,
|
||||
items: parsed.items,
|
||||
abilities: parsed.abilities,
|
||||
baggage: [],
|
||||
ap: parsed.ap,
|
||||
hp: parsed.hp,
|
||||
ep: parsed.ep,
|
||||
xp: parsed.xp,
|
||||
muscle: parsed.muscle,
|
||||
focus: parsed.focus,
|
||||
knowledge: parsed.knowledge,
|
||||
charm: parsed.charm,
|
||||
cunning: parsed.cunning,
|
||||
spark: parsed.spark,
|
||||
});
|
||||
|
||||
return {
|
||||
type: "blueprint",
|
||||
component: blueprint,
|
||||
};
|
||||
}
|
||||
|
||||
function parseGlossaryDefinition(obj: unknown): ParsedComponent {
|
||||
const { terms } = GlossarySchema.parse(obj);
|
||||
|
||||
// Create a new object with each definition as an array
|
||||
const curatedTerms: Record<string, string[]> = {};
|
||||
for (const [term, definition] of Object.entries(terms)) {
|
||||
curatedTerms[term] = [definition];
|
||||
}
|
||||
|
||||
const glossary = parseGlossary(curatedTerms);
|
||||
|
||||
return {
|
||||
type: "glossary",
|
||||
component: glossary,
|
||||
};
|
||||
}
|
||||
|
||||
function parseItemDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = ItemSchema.parse(obj);
|
||||
|
||||
return {
|
||||
type: "item",
|
||||
component: parseItem({
|
||||
...parsed,
|
||||
damage:
|
||||
parsed.damage > 0
|
||||
? {
|
||||
amount: parsed.damage,
|
||||
type: parsed.damageType,
|
||||
}
|
||||
: null,
|
||||
tweak: "",
|
||||
temper: "",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function parseMethodDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = MethodSchema.parse(obj);
|
||||
|
||||
const method = {
|
||||
id: parsed.id,
|
||||
name: parsed.name,
|
||||
description: parsed.description,
|
||||
categories: parsed.categories,
|
||||
curator: parsed.curator,
|
||||
abilities: parsed.abilities,
|
||||
};
|
||||
|
||||
return {
|
||||
type: "method",
|
||||
component: parseMethod(method),
|
||||
};
|
||||
}
|
||||
|
||||
function parseResourceDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = ResourceSchema.parse(obj);
|
||||
|
||||
return {
|
||||
type: "resource",
|
||||
component: parseResource(parsed),
|
||||
};
|
||||
}
|
||||
|
||||
function parseRuleDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = RuleSchema.parse(obj);
|
||||
|
||||
return {
|
||||
type: "rule",
|
||||
component: parseRule(parsed),
|
||||
};
|
||||
}
|
||||
|
||||
function parseSpeciesDefinition(obj: unknown): ParsedComponent {
|
||||
const parsed = SpeciesSchema.parse(obj);
|
||||
|
||||
return {
|
||||
type: "species",
|
||||
component: parseSpecies(parsed),
|
||||
};
|
||||
}
|
||||
|
||||
export function parsePlaybillComponent(obj: unknown): ParsedComponent {
|
||||
const baseParse = Base.parse(obj);
|
||||
|
||||
const type = parseComponentType(baseParse.$define);
|
||||
const schema = SchemaMapping[type];
|
||||
const component = schema.parse(obj);
|
||||
|
||||
switch (type) {
|
||||
case "ability":
|
||||
return parseAbilityDefinition(component);
|
||||
case "blueprint":
|
||||
return parseBlueprintDefinition(component);
|
||||
case "glossary":
|
||||
return parseGlossaryDefinition(component);
|
||||
case "item":
|
||||
return parseItemDefinition(component);
|
||||
case "method":
|
||||
return parseMethodDefinition(component);
|
||||
case "resource":
|
||||
return parseResourceDefinition(component);
|
||||
case "rule":
|
||||
return parseRuleDefinition(component);
|
||||
case "species":
|
||||
return parseSpeciesDefinition(component);
|
||||
default:
|
||||
throw new Error(`Unknown component type: ${type}`);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue