all-specs: one place that knows where the actors are
check-creatures collects every spec in the repo, including the rule that a scenario cast is any exported array whose entries carry characteristics — so a new scenario is covered the day it is written. That rule is a definition, and two more tools are about to need it. Lifted into tools/all-specs.mjs and imported back. check-creatures reports the same 76 specs across the same 8 sources; this is a move, not a change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
255b146753
commit
24b183642f
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Every actor spec in the repository, collected once.
|
||||
*
|
||||
* Three things now need the same list — check-creatures validates it, make-portraits
|
||||
* draws it, and mj-queue writes prompts for it — and this repository's one standing law
|
||||
* is that a definition may not exist twice. The collection rule is a definition: a
|
||||
* scenario cast is any exported array whose entries carry characteristics, so a new
|
||||
* scenario is covered the day it is written rather than the day somebody remembers to
|
||||
* add it to a list. That rule belongs in one place.
|
||||
*
|
||||
* `kind` is what buildActor will make of the spec: "agent" becomes a character actor,
|
||||
* "npc" becomes an npc actor. It decides which portrait family a creature is drawn in
|
||||
* and which prompt template it gets, so it is carried here rather than re-derived.
|
||||
*/
|
||||
import { NPCS, PREGENS } from "./content.mjs";
|
||||
import { ROSTER } from "./roster.mjs";
|
||||
|
||||
/** Scenario modules whose casts join the list. */
|
||||
export const SCENARIOS = ["throughtrain", "starter", "lastadmission", "openday"];
|
||||
|
||||
/**
|
||||
* @returns {Promise<Array<{label: string, kind: "agent"|"npc", spec: object}>>}
|
||||
* every spec, in a stable order, each tagged with where it came from.
|
||||
*/
|
||||
export async function collectSpecs() {
|
||||
const sources = [
|
||||
{ label: "content.mjs PREGENS", kind: "agent", specs: PREGENS },
|
||||
{ label: "content.mjs NPCS", kind: "npc", specs: NPCS },
|
||||
{ label: "roster.mjs ROSTER", kind: "agent", specs: ROSTER }
|
||||
];
|
||||
|
||||
for (const s of SCENARIOS) {
|
||||
const mod = await import(`./scenario-${s}.mjs`);
|
||||
for (const [name, value] of Object.entries(mod)) {
|
||||
if (Array.isArray(value) && value.some(x => x && typeof x === "object" && x.ch)) {
|
||||
sources.push({ label: `scenario-${s}.mjs ${name}`, kind: "npc", specs: value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const out = [];
|
||||
for (const { label, kind, specs } of sources) {
|
||||
for (const spec of specs) out.push({ label, kind, spec });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** The same thing grouped, for callers that report per source. */
|
||||
export async function collectSources() {
|
||||
const flat = await collectSpecs();
|
||||
const byLabel = new Map();
|
||||
for (const row of flat) {
|
||||
if (!byLabel.has(row.label)) byLabel.set(row.label, { label: row.label, kind: row.kind, specs: [] });
|
||||
byLabel.get(row.label).specs.push(row.spec);
|
||||
}
|
||||
return [...byLabel.values()];
|
||||
}
|
||||
@@ -14,30 +14,13 @@
|
||||
*
|
||||
* node tools/check-creatures.mjs
|
||||
*/
|
||||
import { NPCS, PREGENS } from "./content.mjs";
|
||||
import { ROSTER } from "./roster.mjs";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { validate, KNOWN_FIELDS } from "./creature-schema.mjs";
|
||||
import { collectSources } from "./all-specs.mjs";
|
||||
|
||||
/* Scenario casts are collected by shape rather than by name: a cast is any exported
|
||||
array whose entries carry characteristics. A new scenario is then covered the day it
|
||||
is written instead of the day somebody remembers to add it here. */
|
||||
const SCENARIOS = ["throughtrain", "starter", "lastadmission", "openday"];
|
||||
|
||||
const sources = [
|
||||
{ label: "content.mjs PREGENS", kind: "agent", specs: PREGENS },
|
||||
{ label: "content.mjs NPCS", kind: "npc", specs: NPCS },
|
||||
{ label: "roster.mjs ROSTER", kind: "agent", specs: ROSTER }
|
||||
];
|
||||
|
||||
for (const s of SCENARIOS) {
|
||||
const mod = await import(`./scenario-${s}.mjs`);
|
||||
for (const [name, value] of Object.entries(mod)) {
|
||||
if (Array.isArray(value) && value.some(x => x && typeof x === "object" && x.ch)) {
|
||||
sources.push({ label: `scenario-${s}.mjs ${name}`, kind: "npc", specs: value });
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Scenario casts are collected by shape rather than by name — see all-specs.mjs, which
|
||||
owns that rule now that make-portraits and mj-queue need the same list. */
|
||||
const sources = await collectSources();
|
||||
|
||||
const problems = [];
|
||||
const seenKeys = new Map();
|
||||
|
||||
Reference in New Issue
Block a user