A Foundry VTT game system: Basic Roleplaying d100 in a drab British agency that deals with crossings, where reality keeps a record of what belongs where and can be made to get it wrong. Contents: - rules.mjs as the single authority for every rule formula, with 40 rules and 132 spot-checked values. Nothing may restate a formula it defines. - 25 postings, each with a key characteristic, trained skills, talents and its own kit; a 175-item catalogue across four eras; 231 generated icons. - Reaction Lanes initiative, re-rolled every round including the first. - Coherence, and the Quiet Arts, which only ever arrive from a crossing gone wrong. - Graded defences: only a critical stops a critical. - Borrowed Authority — access as a record rather than a hacking roll. - A case generator, and a rules journal generated from rules.mjs so the book cannot drift from the code. Built compendia are committed so a remote Foundry needs only the files. node_modules is not; `npm install` restores it for rebuilding content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
265 lines
16 KiB
JavaScript
265 lines
16 KiB
JavaScript
/**
|
|
* The Custodians — icon set, v3.
|
|
*
|
|
* Flat, bold, saturated. Not photorealistic and not trying to be. What changed in
|
|
* v3 is DEPTH: a glyph is no longer a single silhouette but up to three layers —
|
|
*
|
|
* base the mass of the object, in near-white ink
|
|
* detail the parts that tell you WHICH object it is, in a darker tone:
|
|
* the grip and sight on a pistol, the fuller on a blade, the lens
|
|
* housing on a scanner. This is the whole point of the revision.
|
|
* mark a small accent in the rim colour — a muzzle flash, a lit indicator
|
|
*
|
|
* and ERA, because the agency crosses to other times and comes back with the
|
|
* shopping. Era is a corner tab plus a treatment of the tile, so a glance at an
|
|
* inventory tells you what century a thing is from before you read its name:
|
|
*
|
|
* antique notched corner, muted ground, no glow — flint, brass, oil
|
|
* modern plain tile — agency issue
|
|
* future cut corner and a hairline scan across it — recovered, elsewhere
|
|
* anomalous a broken rim: the tile does not close — should not exist
|
|
*
|
|
* Readable at 32px in a Foundry list, still clean at 256px on a sheet.
|
|
*/
|
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
const OUT = path.resolve("icons");
|
|
const S = 128;
|
|
|
|
/* Category hue families. Deliberately far apart so a mixed inventory reads fast. */
|
|
const FAMILY = {
|
|
weapon: { h: 8 }, melee: { h: 24 }, armour: { h: 205 },
|
|
gear: { h: 150 }, device: { h: 275 }, skill: { h: 45 },
|
|
talent: { h: 185 }, actor: { h: 330 }, threat: { h: 355 },
|
|
case: { h: 220 }, power: { h: 288 }
|
|
};
|
|
|
|
const hsl = (h, s, l) => `hsl(${h} ${s}% ${l}%)`;
|
|
|
|
/**
|
|
* Glyphs. A string is a plain silhouette (v2 behaviour, kept so nothing regresses).
|
|
* An object may carry { base, detail, mark, stroke } — `stroke` lists which of
|
|
* those layers are drawn as lines rather than filled.
|
|
*/
|
|
const G = {
|
|
/* ---------------- firearms: era told by the furniture ---------------- */
|
|
pistol: { base: `M22 62h52l10-10h16v14l-12 6v10H60l-6 12H34l6-12H22z`,
|
|
detail: `M30 70h18v8H30z M60 56h14v6H60z`, mark: `M112 60h10v8h-10z` },
|
|
revolver: { base: `M20 60h48l8-8h18v12l-10 6v10H56l-8 14H26l8-14H20z`,
|
|
detail: `M44 56a10 10 0 1 1 0 20 10 10 0 0 1 0-20z M28 70h14v8H28z`,
|
|
mark: `M110 58h10v10h-10z` },
|
|
flintlock: { base: `M18 64h44l40-16v10L70 74v8H56l-10 16H22l10-16H18z`,
|
|
detail: `M56 54l10-8 6 8-8 8z M26 72h16v8H26z` },
|
|
rifle: { base: `M12 60h84l20-8v12l-14 6v8H60l-8 14H32l8-14H12z`,
|
|
detail: `M46 66h30v6H46z M84 52h10v8H84z`, mark: `M116 58h6v10h-6z` },
|
|
musket: { base: `M10 66h96l14-6v8l-14 6v6H62l-10 14H24l10-14H10z`,
|
|
detail: `M40 70h26v6H40z M66 56l8-8 6 6-8 8z` },
|
|
smg: { base: `M18 54h60v14H18z M30 68h16v30H30z M78 56h30v10H78z`,
|
|
detail: `M52 68h12v22H52z M22 58h8v6h-8z`, mark: `M108 56h10v10h-10z` },
|
|
shotgun: { base: `M12 46h58v10H12z M12 60h58v10H12z M70 44l30 16-30 16z`,
|
|
detail: `M100 52h16v16h-16z M20 48h14v4H20z` },
|
|
gauss: { base: `M14 58h72v14H14z M86 50h26v30H86z`,
|
|
detail: `M24 62h48v6H24z M92 58h14v14H92z`,
|
|
mark: `M112 54h10v22h-10z`, stroke: ["mark"] },
|
|
|
|
/* ---------------- blades and hafted ---------------- */
|
|
blade: { base: `M78 12l12 12-44 44-12-12z M40 60l12 12-8 12-16-4z M34 84l10 10-16 16-10-10z`,
|
|
detail: `M74 22l6 6-38 38-6-6z` },
|
|
sword: { base: `M64 8l10 10v58H54V18z M40 76h48v10H40z M58 86h12v34H58z`,
|
|
detail: `M62 20h4v52h-4z M60 108h8v8h-8z` },
|
|
axe: { base: `M52 14h14v106H52z M66 20c22 0 34 14 34 26s-12 24-34 24z`,
|
|
detail: `M72 30c12 2 18 10 18 16s-6 14-18 16z` },
|
|
spear: { base: `M62 8l10 26-10 12-10-12z M58 46h12v74H58z`,
|
|
detail: `M60 20h8v12h-8z M56 92h16v6H56z` },
|
|
club: { base: `M44 100l40-72 14 8-40 72z M84 18a12 12 0 1 1 18 10 12 12 0 0 1-18-10z`,
|
|
detail: `M52 96l32-58` , stroke: ["detail"] },
|
|
bar: { base: `M52 12h24v92H52z M44 104h40v14H44z`,
|
|
detail: `M58 24h12v70H58z M52 12l-8-6h40l-8 6z` },
|
|
claw: { base: `M30 18c14 22 14 62 6 90M56 12c10 26 10 68 2 96M82 18c8 24 6 64-2 88`,
|
|
stroke: ["base"] },
|
|
bow: { base: `M40 16c30 14 30 82 0 96M36 12v104`, stroke: ["base"],
|
|
detail: `M40 16l52 48-52 48`, },
|
|
crossbow: { base: `M14 58h100v12H14z M52 40h12v46H52z`,
|
|
detail: `M20 46c18 8 18 28 0 36M108 46c-18 8-18 28 0 36`, stroke: ["detail"] },
|
|
|
|
/* ---------------- crossing tech ---------------- */
|
|
beam: { base: `M18 58h48v12H18z M66 46l30 18-30 18z`,
|
|
detail: `M26 62h30v4H26z`, mark: `M96 56h16v16H96z` },
|
|
bolt: { base: `M70 8L28 72h28l-10 48 46-68H62z`, detail: `M64 26L44 62h16z` },
|
|
lattice: { base: `M64 10l46 26v56l-46 26-46-26V36z`,
|
|
detail: `M64 34l24 14v28l-24 14-24-14V48z`, mark: `M64 58a8 8 0 1 1 0 16 8 8 0 0 1 0-16z` },
|
|
aperture: { base: `M64 14a50 50 0 1 1 0 100 50 50 0 0 1 0-100z`,
|
|
detail: `M64 38a26 26 0 1 1 0 52 26 26 0 0 1 0-52z`,
|
|
mark: `M64 54a10 10 0 1 1 0 20 10 10 0 0 1 0-20z` },
|
|
|
|
/* ---------------- armour ---------------- */
|
|
vest: { base: `M40 20h48l10 18v56a10 10 0 0 1-10 10H40a10 10 0 0 1-10-10V38z`,
|
|
detail: `M64 20v84 M38 46h20M74 46h20` , stroke: ["detail"] },
|
|
plate: { base: `M64 12l40 14v40c0 28-18 44-40 50-22-6-40-22-40-50V26z`,
|
|
detail: `M64 26l26 10v30c0 18-12 28-26 32-14-4-26-14-26-32V36z` },
|
|
jacket: { base: `M42 22h44l12 16v62H30V38z`, detail: `M64 22v78 M30 54h68`, stroke: ["detail"] },
|
|
suit: { base: `M64 8a16 16 0 1 1 0 32 16 16 0 0 1 0-32z M40 44h48l10 20v56H30V64z`,
|
|
detail: `M52 20h24v10H52z M44 72h40v12H44z` },
|
|
mail: { base: `M40 20h48l10 18v56a10 10 0 0 1-10 10H40a10 10 0 0 1-10-10V38z`,
|
|
detail: `M40 40h12v12H40zM60 40h12v12H60zM80 40h10v12H80zM50 56h12v12H50zM70 56h12v12H70zM40 72h12v12H40zM60 72h12v12H60zM80 72h10v12H80z` },
|
|
|
|
/* ---------------- gear, all eras ---------------- */
|
|
kit: { base: `M26 40h76v66H26z M52 40V26h24v14`, detail: `M64 56v34 M47 73h34`, stroke: ["detail"] },
|
|
pack: { base: `M34 38h60v70H34z M50 38V22h28v16`, detail: `M34 66h60 M50 78h28`, stroke: ["detail"] },
|
|
cell: { base: `M42 22h44v18h10v66H32V40h10z`, detail: `M52 58h24v30H52z`, mark: `M56 64h16v6H56z` },
|
|
key: { base: `M34 46a20 20 0 1 1 0 36 20 20 0 0 1 0-36z M54 60h56v10H88v14H78V70h-8v18H60V70z`,
|
|
detail: `M34 58a8 8 0 1 1 0 12 8 8 0 0 1 0-12z` },
|
|
disc: { base: `M64 20a44 44 0 1 1 0 88 44 44 0 0 1 0-88z`, detail: `M64 50a14 14 0 1 1 0 28 14 14 0 0 1 0-28z` },
|
|
scanner: { base: `M24 34h80v52H24z M38 96h52v10H38z`, detail: `M34 44h60v34H34z`,
|
|
mark: `M40 50h48M40 62h34M40 72h44`, stroke: ["mark"] },
|
|
lens: { base: `M56 20a34 34 0 1 1 0 68 34 34 0 0 1 0-68z M80 82l28 28`,
|
|
detail: `M56 34a20 20 0 1 1 0 40 20 20 0 0 1 0-40z` },
|
|
cog: { base: `M64 34a30 30 0 1 1 0 60 30 30 0 0 1 0-60z M58 8h12v18H58z M58 102h12v18H58z M8 58h18v12H8z M102 58h18v12h-18z`,
|
|
detail: `M64 52a12 12 0 1 1 0 24 12 12 0 0 1 0-24z` },
|
|
regulator: { base: `M20 40h88v48H20z`, detail: `M34 54h20v20H34z M74 54h20v20H74z`,
|
|
mark: `M54 64h20v4H54z M44 88v14M84 88v14`, stroke: ["mark"] },
|
|
radio: { base: `M28 52h72v56H28z M44 52V26h8v26`, detail: `M44 66a10 10 0 1 1 0 20 10 10 0 0 1 0-20z`,
|
|
mark: `M64 66h20M64 80h20`, stroke: ["mark"] },
|
|
flask: { base: `M52 14h24v34l24 50a12 12 0 0 1-10 18H38a12 12 0 0 1-10-18l24-50z`,
|
|
detail: `M40 78h48 M50 20h28`, stroke: ["detail"] },
|
|
lantern: { base: `M44 30h40v10H44z M38 40h52v58H38z M56 98h16v10H56z`,
|
|
detail: `M50 50h28v40H50z`, mark: `M58 60h12v20H58z` },
|
|
candle: { base: `M54 46h20v58H54z M44 104h40v10H44z`, detail: `M60 30c8 6 8 14 4 16-6-2-8-10-4-16z` },
|
|
compass: { base: `M64 16a48 48 0 1 1 0 96 48 48 0 0 1 0-96z`,
|
|
detail: `M64 34l12 30-12 30-12-30z`, mark: `M64 56a8 8 0 1 1 0 16 8 8 0 0 1 0-16z` },
|
|
spectacles: { base: `M20 56a20 20 0 1 1 0 20 20 20 0 0 1 0-20z M88 56a20 20 0 1 1 0 20 20 20 0 0 1 0-20z`,
|
|
detail: `M40 64h48`, stroke: ["detail"] },
|
|
camera: { base: `M18 44h92v56H18z M44 44l8-12h24l8 12`, detail: `M64 56a18 18 0 1 1 0 36 18 18 0 0 1 0-36z`,
|
|
mark: `M96 52h10v8H96z` },
|
|
binocs: { base: `M22 40h34v66H22z M72 40h34v66H72z`, detail: `M56 58h16v12H56z`,
|
|
mark: `M28 50h22v10H28z M78 50h22v10H78z` },
|
|
rope: { base: `M32 20c40 0 24 44 64 44M32 64c40 0 24 44 64 44`, stroke: ["base"] },
|
|
book: { base: `M24 20h38a14 14 0 0 1 14 14v74a14 14 0 0 0-14-12H24z M104 20H66a14 14 0 0 0-14 14v74a14 14 0 0 1 14-12h38z`,
|
|
detail: `M32 40h30M32 56h30M66 40h30M66 56h30`, stroke: ["detail"] },
|
|
folder: { base: `M14 30h40l10 14h50v66H14z`, detail: `M14 62h100 M28 78h60`, stroke: ["detail"] },
|
|
vial: { base: `M50 12h28v12l-8 6v72a10 10 0 0 1-12 0V30l-8-6z`, detail: `M58 70h12v24H58z` },
|
|
syringe: { base: `M18 92l40-40 18 18-40 40z M62 44l22-22 18 18-22 22z`,
|
|
detail: `M96 16l16 16 M30 96l-12 12`, stroke: ["detail"] },
|
|
mask_gas: { base: `M64 16c26 0 40 14 40 34 0 22-18 34-24 44-4 8-28 8-32 0-6-10-24-22-24-44 0-20 14-34 40-34z`,
|
|
detail: `M44 46a12 12 0 1 1 0 20 12 12 0 0 1 0-20z M84 46a12 12 0 1 1 0 20 12 12 0 0 1 0-20z`,
|
|
mark: `M54 84h20v14H54z` },
|
|
geiger: { base: `M26 46h58v50H26z M84 58h20v26H84z`, detail: `M36 56h38v14H36z`,
|
|
mark: `M40 80h30`, stroke: ["mark"] },
|
|
tape: { base: `M64 22a42 42 0 1 1 0 84 42 42 0 0 1 0-84z`, detail: `M64 50a14 14 0 1 1 0 28 14 14 0 0 1 0-28z`,
|
|
mark: `M22 64h84`, stroke: ["mark"] },
|
|
pocketwatch: { base: `M64 26a42 42 0 1 1 0 84 42 42 0 0 1 0-84z M56 8h16v18H56z`,
|
|
detail: `M64 44v24l16 10`, stroke: ["detail"] },
|
|
handaxe_tool: { base: `M40 92l40-56 12 8-40 56z`, detail: `M76 28l18-8 6 16-18 8z` },
|
|
chip: { base: `M34 34h60v60H34z`, detail: `M48 48h32v32H48z`,
|
|
mark: `M34 46H20M34 64H20M34 82H20M94 46h14M94 64h14M94 82h14`, stroke: ["mark"] },
|
|
drone: { base: `M52 52h24v24H52z`, detail: `M20 28a14 14 0 1 1 0 4zM94 28a14 14 0 1 1 0 4zM20 96a14 14 0 1 1 0 4zM94 96a14 14 0 1 1 0 4z`,
|
|
mark: `M34 40l18 12M94 40L76 52M34 88l18-12M94 88L76 76`, stroke: ["mark"] },
|
|
visor: { base: `M18 48h92v26a16 16 0 0 1-16 16H34a16 16 0 0 1-16-16z`,
|
|
detail: `M28 56h72v14H28z`, mark: `M36 60h20v6H36z` },
|
|
cannister: { base: `M44 20h40v88H44z`, detail: `M52 34h24v18H52z`, mark: `M44 62h40M44 78h40`, stroke: ["mark"] },
|
|
net: { base: `M20 20h88v88H20z`, detail: `M20 48h88M20 76h88M48 20v88M76 20v88`, stroke: ["detail"] },
|
|
manacles: { base: `M36 44a22 22 0 1 1 0 40 22 22 0 0 1 0-40z M92 44a22 22 0 1 1 0 40 22 22 0 0 1 0-40z`,
|
|
detail: `M58 62h12`, stroke: ["detail"] },
|
|
|
|
/* ---------------- powers: the Quiet Arts ---------------- */
|
|
thread: { base: `M20 100c30-70 58 40 88-30`, stroke: ["base"],
|
|
detail: `M20 100a6 6 0 1 1 0 2zM108 70a6 6 0 1 1 0 2z` },
|
|
echo: { base: `M64 64a10 10 0 1 1 0 2z`, detail: `M64 40a24 24 0 1 1 0 48 24 24 0 0 1 0-48zM64 22a42 42 0 1 1 0 84 42 42 0 0 1 0-84z`,
|
|
stroke: ["detail"] },
|
|
hollow: { base: `M64 12l48 28v48l-48 28-48-28V40z`, detail: `M64 40l22 13v26L64 92 42 79V53z` },
|
|
witness: { base: `M8 64c26-32 82-32 112 0-30 32-86 32-112 0z`, detail: `M64 44a20 20 0 1 1 0 40 20 20 0 0 1 0-40z`,
|
|
mark: `M64 56a8 8 0 1 1 0 16 8 8 0 0 1 0-16z` },
|
|
unmake: { base: `M28 28l72 72M100 28l-72 72`, stroke: ["base"],
|
|
detail: `M64 44a20 20 0 1 1 0 40 20 20 0 0 1 0-40z` },
|
|
ledger: { base: `M28 16h72v96H28z`, detail: `M40 36h48M40 52h48M40 68h30`, stroke: ["detail"],
|
|
mark: `M76 76l10 10 18-22`, },
|
|
|
|
/* ---------------- the rest, carried over ---------------- */
|
|
eye: { base: `M8 64c26-32 82-32 112 0-30 32-86 32-112 0z`, detail: `M64 44a20 20 0 1 1 0 40 20 20 0 0 1 0-40z` },
|
|
ear: { base: `M46 20c30-8 46 12 44 34-2 24-22 24-24 40s-16 20-26 12c-12-10-6-24 2-30 10-8-16-46 4-56z`, stroke: ["base"] },
|
|
brain: { base: `M40 28c-14 6-18 22-10 32-8 12-2 28 12 30 4 12 20 16 28 8 10 8 24 2 26-10 12-8 10-26-2-32 2-14-12-26-26-22-6-8-18-10-28-6z`,
|
|
detail: `M64 24v78`, stroke: ["base", "detail"] },
|
|
hand: { base: `M40 60V28a8 8 0 0 1 16 0v26 M56 54V20a8 8 0 0 1 16 0v34 M72 56V28a8 8 0 0 1 16 0v34c0 30-12 46-32 46s-32-14-32-34v-8a8 8 0 0 1 16 0`, stroke: ["base"] },
|
|
boot: { base: `M36 16h26v50l34 20v26H36z`, detail: `M36 76h26`, stroke: ["detail"] },
|
|
mask: { base: `M22 34h84v34c0 26-20 46-42 52-22-6-42-26-42-52z`, detail: `M44 56h14M70 56h14`, stroke: ["detail"] },
|
|
ring: { base: `M64 16a48 48 0 1 1 0 96 48 48 0 0 1 0-96z`, detail: `M64 42a22 22 0 1 1 0 44 22 22 0 0 1 0-44z` },
|
|
triune: { base: `M34 22a14 14 0 1 1 0 28 14 14 0 0 1 0-28z M94 22a14 14 0 1 1 0 28 14 14 0 0 1 0-28z M30 68h68v18H30z`,
|
|
detail: `M34 50l14 18h32l14-18 M38 86v30M64 86v30M90 86v30`, stroke: ["detail"] },
|
|
figure: { base: `M64 14a18 18 0 1 1 0 36 18 18 0 0 1 0-36z M40 56h48v40H40z`,
|
|
detail: `M46 96l-6 24M82 96l6 24`, stroke: ["detail"] },
|
|
target: { base: `M64 12a52 52 0 1 1 0 104 52 52 0 0 1 0-104z`,
|
|
detail: `M64 40a24 24 0 1 1 0 48 24 24 0 0 1 0-48z`, mark: `M64 58a6 6 0 1 1 0 12 6 6 0 0 1 0-12z` },
|
|
wave: { base: `M10 64c14-26 28-26 42 0s28 26 42 0 22-14 24-6`, stroke: ["base"] },
|
|
door: { base: `M30 16h68v96H30z`, detail: `M84 60a5 5 0 1 1 0 10 5 5 0 0 1 0-10z` }
|
|
};
|
|
|
|
const ERAS = { antique: 1, modern: 1, future: 1, anomalous: 1 };
|
|
|
|
function layers(glyph, ink, mid, rim) {
|
|
const g = typeof G[glyph] === "string" ? { base: G[glyph] } : (G[glyph] ?? G.disc);
|
|
const stroke = new Set(g.stroke ?? []);
|
|
const draw = (d, fill, isStroke, w = 9) => !d ? "" : isStroke
|
|
? `<path d="${d}" fill="none" stroke="${fill}" stroke-width="${w}" stroke-linecap="round" stroke-linejoin="round"/>`
|
|
: `<path d="${d}" fill="${fill}" stroke="none"/>`;
|
|
return [
|
|
draw(g.base, ink, stroke.has("base")),
|
|
draw(g.detail, mid, stroke.has("detail"), 6),
|
|
draw(g.mark, rim, stroke.has("mark"), 6)
|
|
].join("\n");
|
|
}
|
|
|
|
function svg(glyph, hue, { dark = false, era = "modern" } = {}) {
|
|
const muted = era === "antique";
|
|
const bg1 = hsl(hue, muted ? 38 : 62, dark ? 22 : (muted ? 30 : 34));
|
|
const bg2 = hsl(hue, muted ? 44 : 68, dark ? 14 : 22);
|
|
const ink = hsl(hue, muted ? 55 : 90, muted ? 82 : 88);
|
|
const mid = hsl(hue, muted ? 40 : 70, muted ? 52 : 46);
|
|
const rim = hsl(hue, 80, 64);
|
|
|
|
// era tab: a corner treatment, so the century reads before the name does
|
|
let corner = "", scan = "", rimPath =
|
|
`<rect x="5" y="5" width="${S - 10}" height="${S - 10}" rx="16" fill="none" stroke="${rim}" stroke-width="4"/>`;
|
|
if (era === "antique") corner = `<path d="M0 0h30L0 30z" fill="${bg2}"/>`;
|
|
if (era === "future") {
|
|
corner = `<path d="M${S - 32} 0H${S}v32z" fill="${rim}" opacity="0.55"/>`;
|
|
scan = `<path d="M12 ${S - 26}h${S - 24}" stroke="${rim}" stroke-width="2" opacity="0.7"/>`;
|
|
}
|
|
if (era === "anomalous") {
|
|
rimPath = `<rect x="5" y="5" width="${S - 10}" height="${S - 10}" rx="16" fill="none"
|
|
stroke="${rim}" stroke-width="4" stroke-dasharray="26 13" stroke-dashoffset="7"/>`;
|
|
}
|
|
|
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${S} ${S}" width="${S}" height="${S}">
|
|
<rect width="${S}" height="${S}" rx="20" fill="${bg1}"/>
|
|
${corner}
|
|
${rimPath}
|
|
${layers(glyph, ink, mid, rim)}
|
|
${scan}
|
|
</svg>`;
|
|
}
|
|
|
|
export async function writeIcon(name, glyph, family, opts) {
|
|
const fam = FAMILY[family] ?? FAMILY.gear;
|
|
await mkdir(OUT, { recursive: true });
|
|
const hue = (fam.h + (opts?.shift ?? 0) + 360) % 360;
|
|
await writeFile(path.join(OUT, `${name}.svg`), svg(glyph, hue, opts), "utf8");
|
|
return `systems/ringbrp/icons/${name}.svg`;
|
|
}
|
|
|
|
export { FAMILY, G, ERAS };
|
|
|
|
/* ---------------- main ---------------- */
|
|
import { ICON_MAP } from "./icons.mjs";
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
let n = 0, byEra = {};
|
|
for (const [name, spec] of Object.entries(ICON_MAP)) {
|
|
const [glyph, family, shift, era = "modern"] = spec;
|
|
await writeIcon(name, glyph, family, { shift, era });
|
|
byEra[era] = (byEra[era] ?? 0) + 1;
|
|
n++;
|
|
}
|
|
console.log(`Custodians | ${n} icons written to icons/`);
|
|
console.log(" by era: " + Object.entries(byEra).map(([k, v]) => `${k} ${v}`).join(" · "));
|
|
}
|