Move playbill to its own repo
This commit is contained in:
parent
1c64a941cd
commit
879e508d95
8 changed files with 20 additions and 490 deletions
|
@ -7,10 +7,7 @@
|
|||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": [
|
||||
"*.d.ts",
|
||||
"*.json"
|
||||
]
|
||||
"ignore": ["*.d.ts", "*.json"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
|
|
13
bun.lock
13
bun.lock
|
@ -2,8 +2,9 @@
|
|||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "lang",
|
||||
"name": "@proscenium/muse",
|
||||
"dependencies": {
|
||||
"@proscenium/playbill": "0.0.1",
|
||||
"chalk": "^5.4.1",
|
||||
"yaml": "^2.7.0",
|
||||
"zod": "^3.24.1",
|
||||
|
@ -36,13 +37,15 @@
|
|||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="],
|
||||
"@proscenium/playbill": ["@proscenium/playbill@0.0.1", "https://git.astral.camp/api/packages/proscenium/npm/%40proscenium%2Fplaybill/-/0.0.1/playbill-0.0.1.tgz", { "peerDependencies": { "typescript": "^5" } }, "sha512-uvv2Bt1+rjzuXl5y2t2C4KGgVda3orTZMVzb6gNAzStzKutiR3xJZkw2ULukYWWGyZ9BzzkRClSKVYka2Z3Png=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
|
||||
"@types/bun": ["@types/bun@1.2.3", "", { "dependencies": { "bun-types": "1.2.3" } }, "sha512-054h79ipETRfjtsCW9qJK8Ipof67Pw9bodFWmkfkaUaRiIQ1dIV2VTlheshlBx3mpKr0KeK8VqnMMCtgN9rQtw=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
|
||||
"bun-types": ["bun-types@1.2.3", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P7AeyTseLKAvgaZqQrvp3RqFM3yN9PlcLuSTe7SoJOfZkER73mLdT2vEQi8U64S1YvM/ldcNiQjn0Sn7H9lGgg=="],
|
||||
|
||||
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||
|
||||
|
@ -52,6 +55,6 @@
|
|||
|
||||
"yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="],
|
||||
|
||||
"zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
|
||||
"zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
"dependencies": {
|
||||
"chalk": "^5.4.1",
|
||||
"yaml": "^2.7.0",
|
||||
"zod": "^3.24.1"
|
||||
"zod": "^3.24.1",
|
||||
"@proscenium/playbill": "0.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"fmt": "bunx --bun biome check --fix",
|
||||
|
|
11
src/index.ts
11
src/index.ts
|
@ -1,12 +1,13 @@
|
|||
import { parseArgs } from "node:util";
|
||||
import chalk from "chalk";
|
||||
import { loadBindingFile } from "./binding";
|
||||
import { DirectiveError, processLang } from "./lang";
|
||||
import {
|
||||
type AnyResource,
|
||||
DirectiveError,
|
||||
ValidatedPlaybillSchema,
|
||||
compileScript,
|
||||
getEmptyPlaybill,
|
||||
} from "./playbill-schema";
|
||||
} from "@proscenium/playbill";
|
||||
import chalk from "chalk";
|
||||
import { loadBindingFile } from "./binding";
|
||||
import { loadResourceFile } from "./resource";
|
||||
import { usage } from "./usage";
|
||||
|
||||
|
@ -148,7 +149,7 @@ async function main(): Promise<boolean> {
|
|||
verboseLog("Processing descriptions");
|
||||
for (const resource of loadedResources) {
|
||||
try {
|
||||
resource.description = await processLang(resource.description, playbill);
|
||||
resource.description = compileScript(resource.description, playbill);
|
||||
} catch (error) {
|
||||
if (error instanceof DirectiveError) {
|
||||
console.error(
|
||||
|
|
107
src/lang.ts
107
src/lang.ts
|
@ -1,107 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { StatsSchema, type Playbill } from "./playbill-schema";
|
||||
|
||||
const DirectiveSchema = z.enum(["dmg", "ability", "stat", "rule"] as const);
|
||||
type DirectiveName = z.infer<typeof DirectiveSchema>;
|
||||
type DirectiveHandler = (playbill: Playbill, ...args: string[]) => string;
|
||||
|
||||
export class DirectiveError extends Error { }
|
||||
|
||||
const ProsceniumLangDirectives: Record<DirectiveName, DirectiveHandler> = {
|
||||
dmg: (_, amount = "1", type = "phy") => {
|
||||
const DAMAGE_TYPES = ["phy", "arc", "physical", "arcane"];
|
||||
if (!DAMAGE_TYPES.includes(type)) {
|
||||
throw new DirectiveError(`Unknown damage type: ${type}`);
|
||||
}
|
||||
|
||||
const finalizedType = type.slice(0, 3);
|
||||
return `<em class='dmg ${finalizedType}'>${amount}</em>`;
|
||||
},
|
||||
|
||||
ability: (playbill: Playbill, ref: string) => {
|
||||
if (typeof ref !== "string") {
|
||||
throw new DirectiveError("Invalid ability reference");
|
||||
}
|
||||
|
||||
const foundAbility = playbill.abilities.find((a) => a.id === ref);
|
||||
|
||||
if (foundAbility === undefined) {
|
||||
throw new DirectiveError(`Unknown ability reference: ${ref}`);
|
||||
}
|
||||
|
||||
return `<em class="ref-ability">${foundAbility.name}</em>`;
|
||||
},
|
||||
|
||||
rule: (playbill: Playbill, ref: string) => {
|
||||
if (typeof ref !== "string") {
|
||||
throw new DirectiveError("Invalid rule reference");
|
||||
}
|
||||
|
||||
const foundRule = playbill.rules.find((r) => r.id === ref);
|
||||
|
||||
if (foundRule === undefined) {
|
||||
throw new DirectiveError(`Unknown ability reference: ${ref}`);
|
||||
}
|
||||
|
||||
return `<em class="ref-rule">${foundRule.name}</em>`;
|
||||
},
|
||||
stat: (_, stat: string, amount = "") => {
|
||||
const lower = stat.toLowerCase();
|
||||
const parsed = StatsSchema.safeParse(lower);
|
||||
|
||||
if (!parsed.success) {
|
||||
throw new DirectiveError(`Unknown stat: ${stat}`);
|
||||
}
|
||||
|
||||
if (amount !== "") {
|
||||
const amountAsNum = Number.parseInt(amount, 10);
|
||||
if (Number.isNaN(amountAsNum)) {
|
||||
throw new DirectiveError(`Invalid amount: ${amount}`);
|
||||
}
|
||||
return `<em class="stat">${amount} ${stat.toUpperCase()}</em>`;
|
||||
}
|
||||
|
||||
return `<em class="stat">${stat.toUpperCase()}</em>`;
|
||||
},
|
||||
};
|
||||
|
||||
function processCommands(text: string, playbill: Playbill): string {
|
||||
const commandPattern = /\{(\w+):([^}]+)\}/g;
|
||||
|
||||
return text.replace(commandPattern, (_match, command, args) => {
|
||||
const directive = DirectiveSchema.parse(command);
|
||||
const handler = ProsceniumLangDirectives[directive];
|
||||
const parsedArgs = [];
|
||||
let currentArg = "";
|
||||
let inQuotes = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const char = args[i];
|
||||
if (char === '"' && (i === 0 || args[i - 1] !== "\\")) {
|
||||
inQuotes = !inQuotes;
|
||||
} else if (char === "," && !inQuotes) {
|
||||
parsedArgs.push(currentArg.trim().replace(/^"|"$/g, ""));
|
||||
currentArg = "";
|
||||
} else {
|
||||
currentArg += char;
|
||||
}
|
||||
}
|
||||
parsedArgs.push(currentArg.trim().replace(/^"|"$/g, ""));
|
||||
|
||||
return handler(playbill, ...parsedArgs);
|
||||
});
|
||||
}
|
||||
|
||||
export async function processLang(
|
||||
input: string,
|
||||
playbill: Playbill,
|
||||
): Promise<string> {
|
||||
const lines = input.split("\n");
|
||||
const processedLines: string[] = [];
|
||||
for (const line of lines) {
|
||||
const processed = processCommands(line, playbill);
|
||||
processedLines.push(processed);
|
||||
}
|
||||
|
||||
return processedLines.join("\n");
|
||||
}
|
|
@ -1,365 +0,0 @@
|
|||
import { z } from "zod";
|
||||
|
||||
/*
|
||||
* TYPE REFERENCES
|
||||
* ------------------------
|
||||
* Ability N/A
|
||||
* Tag N/A
|
||||
* Lore N/A
|
||||
* Rule N/A
|
||||
* Method Ability
|
||||
* Item Tag
|
||||
* Species Ability
|
||||
* Entity Ability, Species
|
||||
*/
|
||||
|
||||
// String shapes
|
||||
export const IDSchema = z.string().regex(/[a-z\-]+/);
|
||||
export const NameSchema = z.string().max(40).default("Unnamed");
|
||||
export const DescriptionSchema = z.string().default("No description");
|
||||
|
||||
// Constants
|
||||
export const STAT_ABBREVIATIONS = ["ap", "ep", "hp", "xp"] as const;
|
||||
export const ITEM_TYPES = ["trinket", "worn", "wielded"] as const;
|
||||
export const ROLL_TYPES = ["none", "attack", "fate"] as const;
|
||||
export const TALENTS = [
|
||||
"muscle",
|
||||
"knowledge",
|
||||
"focus",
|
||||
"charm",
|
||||
"cunning",
|
||||
"spark",
|
||||
] as const;
|
||||
export const ABILITY_TYPES = ["action", "cue", "trait"] as const;
|
||||
export const TALENT_PROWESS = ["novice", "adept", "master"] as const;
|
||||
|
||||
// Enums
|
||||
export const StatsSchema = z.enum(STAT_ABBREVIATIONS);
|
||||
export const ItemTypeSchema = z.enum(ITEM_TYPES);
|
||||
export const RollTypeSchema = z.enum(ROLL_TYPES);
|
||||
export const AbilityTypeSchema = z.enum(ABILITY_TYPES);
|
||||
export const TalentSchema = z.enum(TALENTS);
|
||||
export const TalentProwessSchema = z.enum(TALENT_PROWESS);
|
||||
|
||||
const ResourceTypes = [
|
||||
"ability",
|
||||
"species",
|
||||
"entity",
|
||||
"item",
|
||||
"tag",
|
||||
"lore",
|
||||
"method",
|
||||
"rule",
|
||||
"playbill",
|
||||
] as const;
|
||||
const ResourceTypeSchema = z.enum(ResourceTypes);
|
||||
type ResourceType = z.infer<typeof ResourceTypeSchema>;
|
||||
|
||||
// Inner utility shapes
|
||||
export const StatSpreadSchema = z.object({
|
||||
ap: z.number().default(0),
|
||||
ep: z.number().default(0),
|
||||
hp: z.number().default(0),
|
||||
xp: z.number().default(0),
|
||||
});
|
||||
|
||||
export const TalentSpreadSchema = z.object({
|
||||
muscle: TalentProwessSchema.default("novice"),
|
||||
knowledge: TalentProwessSchema.default("novice"),
|
||||
focus: TalentProwessSchema.default("novice"),
|
||||
charm: TalentProwessSchema.default("novice"),
|
||||
cunning: TalentProwessSchema.default("novice"),
|
||||
spark: TalentProwessSchema.default("novice"),
|
||||
});
|
||||
|
||||
export const PlaybillResourceSchema = z.object({
|
||||
$define: ResourceTypeSchema,
|
||||
id: IDSchema,
|
||||
name: NameSchema,
|
||||
description: DescriptionSchema,
|
||||
});
|
||||
|
||||
// An object with optional properties to define passive effects
|
||||
// offered from an ability. These are aggregated by the game
|
||||
// engine at runtime to determine the final stats of a character.
|
||||
export const AbilityEffectSchema = z.object({
|
||||
bonusMaxHp: z.number().optional(),
|
||||
bonusMaxAp: z.number().optional(),
|
||||
bonusMaxEp: z.number().optional(),
|
||||
muscleProwess: TalentProwessSchema.optional(),
|
||||
knowledgeProwess: TalentProwessSchema.optional(),
|
||||
focusProwess: TalentProwessSchema.optional(),
|
||||
charmProwess: TalentProwessSchema.optional(),
|
||||
cunningProwess: TalentProwessSchema.optional(),
|
||||
sparkProwess: TalentProwessSchema.optional(),
|
||||
});
|
||||
|
||||
export const BaggageSchema = z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
});
|
||||
|
||||
// Playbill component shapes
|
||||
export const AbilitySchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("ability"),
|
||||
name: NameSchema.default("Unnamed Ability"),
|
||||
type: AbilityTypeSchema,
|
||||
costs: StatSpreadSchema.default({}),
|
||||
roll: RollTypeSchema.or(TalentSchema).default("none"),
|
||||
banes: z.array(z.string()).default([]),
|
||||
boons: z.array(z.string()).default([]),
|
||||
effects: AbilityEffectSchema.default({}),
|
||||
});
|
||||
|
||||
export type PlaybillAbility = z.infer<typeof AbilitySchema>;
|
||||
|
||||
export const EntitySchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("entity"),
|
||||
name: NameSchema.default("Unnamed Species"),
|
||||
species: z.string(IDSchema),
|
||||
abilities: z.array(IDSchema),
|
||||
stats: StatSpreadSchema,
|
||||
talents: TalentSpreadSchema,
|
||||
damage: z.number().default(0),
|
||||
baggage: z.array(BaggageSchema).default([]),
|
||||
});
|
||||
|
||||
export const ItemTagSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("tag"),
|
||||
name: NameSchema.default("Unnamed Item Tag"),
|
||||
icon: z.string().url(),
|
||||
});
|
||||
|
||||
export const ItemSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("item"),
|
||||
name: NameSchema.default("Unnamed Item"),
|
||||
type: ItemTypeSchema,
|
||||
slots: z.number().min(0).max(5).default(0),
|
||||
tags: z.array(IDSchema).default([]),
|
||||
});
|
||||
|
||||
export const LoreSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("lore"),
|
||||
name: NameSchema.default("Unnamed Lore Entry"),
|
||||
categories: z.array(z.string()).default([]),
|
||||
});
|
||||
|
||||
export const MethodSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("method"),
|
||||
name: NameSchema.default("Unnamed Method"),
|
||||
curator: NameSchema,
|
||||
abilities: z.array(z.array(IDSchema)).default([]),
|
||||
});
|
||||
|
||||
export const SpeciesSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("species"),
|
||||
name: NameSchema.default("Unnamed Species"),
|
||||
abilities: z.array(IDSchema).default([]),
|
||||
bonuses: AbilityEffectSchema.default({}),
|
||||
});
|
||||
|
||||
export const RuleSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("rule"),
|
||||
overrule: z.string().optional(),
|
||||
});
|
||||
|
||||
// Full Playbill Schema
|
||||
export const PlaybillSchema = PlaybillResourceSchema.extend({
|
||||
$define: z.literal("playbill"),
|
||||
version: z.string().default("0"),
|
||||
name: NameSchema.default("Unnamed Playbill"),
|
||||
author: z.string().default("Anonymous"),
|
||||
|
||||
abilities: z.array(AbilitySchema).default([]),
|
||||
entities: z.array(EntitySchema).default([]),
|
||||
items: z.array(ItemSchema).default([]),
|
||||
tags: z.array(ItemTagSchema).default([]),
|
||||
lore: z.array(LoreSchema).default([]),
|
||||
methods: z.array(MethodSchema).default([]),
|
||||
species: z.array(SpeciesSchema).default([]),
|
||||
rules: z.array(RuleSchema).default([]),
|
||||
});
|
||||
|
||||
export const ValidatedPlaybillSchema = PlaybillSchema.superRefine(
|
||||
(val, ctx) => {
|
||||
// For each ability, ensure unique IDs
|
||||
const abilityIds = new Set<string>();
|
||||
for (const ability of val.abilities) {
|
||||
if (abilityIds.has(ability.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Ability ${ability.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
abilityIds.add(ability.id);
|
||||
}
|
||||
|
||||
// For each method, ensure its abilities exist
|
||||
// Also ensure unique IDs
|
||||
const methodIds = new Set<string>();
|
||||
for (const method of val.methods) {
|
||||
if (methodIds.has(method.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Method ${method.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
|
||||
methodIds.add(method.id);
|
||||
const methodAbilityIds = method.abilities.flat();
|
||||
|
||||
for (const abilityId of methodAbilityIds) {
|
||||
if (!val.abilities.some((a) => a.id === abilityId)) {
|
||||
ctx.addIssue({
|
||||
message: `Method ${method.id} references undefined ability ${abilityId}`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each species, ensure its abilities and IDs
|
||||
const speciesIds = new Set<string>();
|
||||
for (const species of val.species) {
|
||||
if (speciesIds.has(species.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Species ${species.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
|
||||
speciesIds.add(species.id);
|
||||
|
||||
const speciesAbilityIds = species.abilities.flat();
|
||||
|
||||
for (const abilityId of speciesAbilityIds) {
|
||||
if (!val.abilities.some((a) => a.id === abilityId)) {
|
||||
ctx.addIssue({
|
||||
message: `Species ${species.id} references undefined ability ${abilityId}`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each entity, ensure its species and known abilities exist
|
||||
const entityIds = new Set<string>();
|
||||
for (const entity of val.entities) {
|
||||
if (entityIds.has(entity.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Entity ${entity.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
entityIds.add(entity.id);
|
||||
|
||||
for (const abilityId of entity.abilities) {
|
||||
if (!val.abilities.some((a) => a.id === abilityId)) {
|
||||
ctx.addIssue({
|
||||
message: `Entity ${entity.id} references undefined ability ${abilityId}`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!val.species.some((s) => s.id === entity.species)) {
|
||||
ctx.addIssue({
|
||||
message: `Entity ${entity.id} references undefined species ${entity.species}`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// For each item, ensure its tags exist
|
||||
const itemIds = new Set<string>();
|
||||
for (const item of val.items) {
|
||||
if (itemIds.has(item.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Item ${item.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
itemIds.add(item.id);
|
||||
for (const tag of item.tags) {
|
||||
if (!val.tags.some((t) => t.id === tag)) {
|
||||
ctx.addIssue({
|
||||
message: `Item ${item.id} references undefined tag ${tag}`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each tag, ensure unique IDs
|
||||
const tagIds = new Set<string>();
|
||||
for (const tag of val.tags) {
|
||||
if (tagIds.has(tag.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Tag ${tag.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
tagIds.add(tag.id);
|
||||
}
|
||||
|
||||
// For each rule, ensure unique IDs
|
||||
const ruleIds = new Set<string>();
|
||||
for (const rule of val.rules) {
|
||||
if (ruleIds.has(rule.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Rule ${rule.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
ruleIds.add(rule.id);
|
||||
}
|
||||
|
||||
// For each lore, ensure unique IDs
|
||||
const loreIds = new Set<string>();
|
||||
for (const lore of val.lore) {
|
||||
if (loreIds.has(lore.id)) {
|
||||
ctx.addIssue({
|
||||
message: `Lore ${lore.id} has a duplicate ID`,
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
loreIds.add(lore.id);
|
||||
}
|
||||
},
|
||||
).brand("ValidatedPlaybill");
|
||||
|
||||
export type Playbill = z.infer<typeof PlaybillSchema>;
|
||||
export type ValidatedPlaybill = z.infer<typeof ValidatedPlaybillSchema>;
|
||||
|
||||
export const ResourceMap = {
|
||||
ability: AbilitySchema,
|
||||
species: SpeciesSchema,
|
||||
entity: EntitySchema,
|
||||
item: ItemSchema,
|
||||
tag: ItemTagSchema,
|
||||
method: MethodSchema,
|
||||
lore: LoreSchema,
|
||||
rule: RuleSchema,
|
||||
playbill: PlaybillSchema,
|
||||
} as const;
|
||||
|
||||
export const AnyResourceSchema = z.discriminatedUnion("$define", [
|
||||
AbilitySchema,
|
||||
SpeciesSchema,
|
||||
EntitySchema,
|
||||
ItemSchema,
|
||||
ItemTagSchema,
|
||||
MethodSchema,
|
||||
LoreSchema,
|
||||
RuleSchema,
|
||||
PlaybillSchema,
|
||||
]);
|
||||
|
||||
export type AnyResource = z.infer<typeof AnyResourceSchema>;
|
||||
|
||||
export function getEmptyPlaybill(): Playbill {
|
||||
return PlaybillSchema.parse({
|
||||
$define: "playbill",
|
||||
id: "empty",
|
||||
});
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import YAML from "yaml";
|
||||
import {
|
||||
type AnyResource,
|
||||
AnyResourceSchema,
|
||||
ResourceMap,
|
||||
} from "./playbill-schema";
|
||||
} from "@proscenium/playbill";
|
||||
import YAML from "yaml";
|
||||
import { extractFrontmatter, extractMarkdown } from "./markdown";
|
||||
|
||||
type FileFormat = "yaml" | "markdown";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { version } from "../package.json" with { type: "json" };
|
||||
import chalk from "chalk";
|
||||
import { version } from "../package.json" with { type: "json" };
|
||||
|
||||
export const usage = `
|
||||
${chalk.bold("muse")} - Compile and validate Playbills for Proscenium
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue