@heroiclands/package-build 22.19.0 → 22.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG-content-build.md +0 -8
- package/CHANGELOG.md +20 -0
- package/ci/content-format-reference.mjs +31 -1
- package/docs/api.md +11 -10
- package/docs/authoring/frontmatter.md +3 -1
- package/docs/reference/format-details.md +93 -34
- package/docs/reference/note-types.md +28 -28
- package/engine/base-compiler.mjs +2 -2
- package/engine/frontmatter-lint.mjs +117 -15
- package/engine/helpers.mjs +5 -0
- package/engine/infobox-render.mjs +6 -1
- package/engine/infobox.mjs +88 -0
- package/engine/note-addresses.mjs +17 -8
- package/engine/note-frontmatter.mjs +6 -0
- package/engine/note-vocabulary.mjs +65 -7
- package/engine/reference-targets.mjs +13 -1
- package/engine/retired-fields.mjs +7 -18
- package/engine/site-index.mjs +21 -2
- package/engine/standing-terms.mjs +19 -0
- package/engine/standings.mjs +455 -0
- package/engine/svg-theme.mjs +15 -4
- package/engine/wikilinks.mjs +3 -1
- package/package.json +1 -1
- package/sohl/actors.mjs +61 -5
- package/types/engine/note-frontmatter.d.mts +4 -0
- package/types/engine/note-vocabulary.d.mts +34 -2
- package/types/engine/retired-fields.d.mts +5 -11
- package/types/engine/standing-terms.d.mts +16 -0
- package/types/engine/standings.d.mts +129 -0
- package/types/engine/wikilinks.d.mts +2 -1
- package/types/sohl/actors.d.mts +19 -0
|
@@ -62,7 +62,6 @@
|
|
|
62
62
|
pass, which reads them exactly as it reads a local pack.
|
|
63
63
|
|
|
64
64
|
Three things it does deliberately:
|
|
65
|
-
|
|
66
65
|
- **Pins, rather than following `latest`.** A published
|
|
67
66
|
`releases/latest/download/…` URL is rewritten to the declared
|
|
68
67
|
`compatibility.verified` version, so a build names one particular dependency
|
|
@@ -177,7 +176,6 @@
|
|
|
177
176
|
|
|
178
177
|
`content-build lint` now checks frontmatter as well as addresses. Five classes,
|
|
179
178
|
each previously reported somewhere other than where it was made, or not at all:
|
|
180
|
-
|
|
181
179
|
- **Unknown or retired type**, told what replaced it.
|
|
182
180
|
- **Missing required property** — `dimensions` on a map, `subType` on a skill.
|
|
183
181
|
- **Wrong value shape** — `weight: heavy` where a number belongs.
|
|
@@ -215,7 +213,6 @@
|
|
|
215
213
|
configures (#69).
|
|
216
214
|
|
|
217
215
|
Two new commands:
|
|
218
|
-
|
|
219
216
|
- `content-build format [paths..] [--write]` — Prettier, with the shared
|
|
220
217
|
configuration.
|
|
221
218
|
- `content-build markdown [paths..] [--fix]` — markdownlint, with a narrow,
|
|
@@ -310,7 +307,6 @@ version` regenerates it in every repository here.
|
|
|
310
307
|
entries) exactly as the scripts it replaces emit them, on the same toolchain.
|
|
311
308
|
|
|
312
309
|
Also new:
|
|
313
|
-
|
|
314
310
|
- `paths.manifestOut` (default `build/manifests`) — where the manifest is
|
|
315
311
|
written. Deliberately not `paths.manifests`, which is the _inbound_ directory
|
|
316
312
|
of vendored foreign manifests that `links` consumes.
|
|
@@ -377,7 +373,6 @@ compile`. Publishing the _same tree_ as a website was a script each consumer
|
|
|
377
373
|
exotic, so neither is left to care.
|
|
378
374
|
|
|
379
375
|
Also new:
|
|
380
|
-
|
|
381
376
|
- `engine/site-build.mjs` exports each stage (`collectContentPages`,
|
|
382
377
|
`collectTreePages`, `siteGates`, `renderPages`, `writeSectionLandings`) for a
|
|
383
378
|
consumer that needs a step rather than the whole command.
|
|
@@ -422,7 +417,6 @@ compile`. Publishing the _same tree_ as a website was a script each consumer
|
|
|
422
417
|
stale by `--check` on every clean checkout — the two undoing each other forever.
|
|
423
418
|
Three things were making that happen, and all three are fixed at the source
|
|
424
419
|
rather than by adding a formatting pass:
|
|
425
|
-
|
|
426
420
|
- Table columns are padded to their widest cell, which is what Prettier's
|
|
427
421
|
alignment comes to for this content.
|
|
428
422
|
- The worked example's fence said `yaml`, but the block is a whole note —
|
|
@@ -632,7 +626,6 @@ compile`. Publishing the _same tree_ as a website was a script each consumer
|
|
|
632
626
|
hide the next drift instead of surfacing it.
|
|
633
627
|
|
|
634
628
|
Two deliberate differences from the code it replaces:
|
|
635
|
-
|
|
636
629
|
- **The `corpus` derivation is dropped.** `corpus` is not a registered item
|
|
637
630
|
type, so nothing can compile to one and the branch matched nothing — the same
|
|
638
631
|
class of dead code as the gate that caused the bug. It was in SoHL's copy and
|
|
@@ -654,7 +647,6 @@ compile`. Publishing the _same tree_ as a website was a script each consumer
|
|
|
654
647
|
`gh release create`, because cutting the Release is what published. Nothing
|
|
655
648
|
enforced the last step, so on 2026-08-21 `main` carried 0.5.1 while npm served
|
|
656
649
|
0.4.0: two versions merged and never published, with no check red.
|
|
657
|
-
|
|
658
650
|
- Every pull request now declares its bump as a `.changeset/*.md` file, and CI's
|
|
659
651
|
**Changeset declared** job fails one that does not. `npx changeset add --empty`
|
|
660
652
|
is how a change says it needs no release — explicitly, rather than by omission.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.21.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
**Standing in a body** — A being's `affiliations` is now a map keyed by the body, and each entry says what the being is there: `rank`, a level on that body's own ladder, and `office`, one of the posts it names. A page prints the standing closing on the body it belongs to — "War Chief, Hárár, of Vrystwald Tribes" — and an office links to the description the body already carries. In SoHL a being now arrives carrying one affiliation item per body it belongs to, standing included.
|
|
8
|
+
|
|
9
|
+
**Offices read the right way round** — An affiliation's offices each take their own row, with the post's name as the label and its description beside it, and each row can be linked to directly.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Icons drawn with outlines follow the colour scheme instead of keeping a black rim in dark mode.
|
|
14
|
+
- An explicitly black outline also moves to ink in light mode, matching the icon's filled shapes.
|
|
15
|
+
|
|
16
|
+
## 22.20.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- Content checks require characters and NPCs to name an archetype and reject unrecognized archetypes; creatures may omit them.
|
|
21
|
+
- Names with unsupported fields now fail validation, and aliases appear beneath the name in infoboxes across published formats.
|
|
22
|
+
|
|
3
23
|
## 22.19.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
|
@@ -7,7 +7,11 @@ import path from "node:path";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { formatGenerated } from "../engine/format-generated.mjs";
|
|
9
9
|
import { loadContentFormat } from "../engine/content-format.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
BEING_ARCHETYPES,
|
|
12
|
+
NOTE_VOCABULARY,
|
|
13
|
+
SHARED_DATA_FIELDS,
|
|
14
|
+
} from "../engine/note-vocabulary.mjs";
|
|
11
15
|
|
|
12
16
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
17
|
const output = path.join(root, "docs/reference/note-types.md");
|
|
@@ -104,3 +108,29 @@ if (process.argv.includes("--check")) {
|
|
|
104
108
|
} else {
|
|
105
109
|
process.stdout.write(result);
|
|
106
110
|
}
|
|
111
|
+
|
|
112
|
+
const detailsPath = path.join(root, "docs/reference/format-details.md");
|
|
113
|
+
const details = fs.readFileSync(detailsPath, "utf8");
|
|
114
|
+
if (!/<!-- archetypes:start -->[\s\S]*?<!-- archetypes:end -->/.test(details)) {
|
|
115
|
+
throw new Error("The archetype reference requires its generation markers");
|
|
116
|
+
}
|
|
117
|
+
const archetypes = Object.entries(BEING_ARCHETYPES)
|
|
118
|
+
.map(([name, meaning]) => `- ${name}: ${meaning}`)
|
|
119
|
+
.join("\n");
|
|
120
|
+
const updatedDetails = await formatGenerated(
|
|
121
|
+
details.replace(
|
|
122
|
+
/<!-- archetypes:start -->[\s\S]*?<!-- archetypes:end -->/,
|
|
123
|
+
`<!-- archetypes:start -->\n${archetypes}\n<!-- archetypes:end -->`,
|
|
124
|
+
),
|
|
125
|
+
detailsPath,
|
|
126
|
+
);
|
|
127
|
+
if (process.argv.includes("--check")) {
|
|
128
|
+
if (details !== updatedDetails) {
|
|
129
|
+
console.error(
|
|
130
|
+
"docs/reference/format-details.md: error: run node ci/content-format-reference.mjs to refresh the archetype reference",
|
|
131
|
+
);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
fs.writeFileSync(detailsPath, updatedDetails);
|
|
136
|
+
}
|
package/docs/api.md
CHANGED
|
@@ -353,6 +353,7 @@ An addressed content note accepts exactly these top-level keys, in this order: `
|
|
|
353
353
|
| ------------------------- | -------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
354
354
|
| `DRAFT_TAG` | `const DRAFT_TAG` | — | naming the declared tag that marks a note as unfinished |
|
|
355
355
|
| `GM_TAG` | `const GM_TAG` | — | naming the declared tag that marks a GM-only note |
|
|
356
|
+
| `BEING_ARCHETYPES` | `const BEING_ARCHETYPES` | `Readonly<Record<string, string>>` | looking up the case-sensitive being archetypes and their meanings |
|
|
356
357
|
| `NOTE_VOCABULARY` | `const NOTE_VOCABULARY` | — | looking up, per content type, its `data:` field list and its closed `subType` values |
|
|
357
358
|
| `SHARED_DATA_FIELDS` | `const SHARED_DATA_FIELDS` | `readonly DataFieldSpec[]` | reading the `data:` keys every type accepts, whatever it is — the art slots legal everywhere |
|
|
358
359
|
| `DECLARED_TAGS` | `const DECLARED_TAGS` | — | looking up the tags a note type may declare, grouped |
|
|
@@ -1287,15 +1288,15 @@ The content tree, built into a book. The I/O half of the PDF surface: it reads t
|
|
|
1287
1288
|
|
|
1288
1289
|
Journals pack compiler — produces JSON pack files for the "journals" Foundry compendium from markdown notes in the `assets/content/` tree. The content root (`contentBase`) is walked recursively; any `.md` file whose frontmatter declares either `type: doc` or a **doc-carrying type** ({@link sohl.utils.packs.docEntryTypes} — every item type, plus `macro`) is compiled into one JournalEntry document. Each note's body is split on top-level H1 headings; the optional content before the first H1 becomes a lead page, and each subsequent H1 starts a new page named after its heading text. All page bodies are rendered to HTML.
|
|
1289
1290
|
|
|
1290
|
-
| Export | Signature | Returns
|
|
1291
|
-
| --------------------- | --------------------------------------------------------------------------- |
|
|
1292
|
-
| `splitPages` | `function splitPages(body, leadName` | —
|
|
1293
|
-
| `assertUniquePages` | `function assertUniquePages(rawPages, noteName)` | —
|
|
1294
|
-
| `assertUniqueAnchors` | `function assertUniqueAnchors(rawPages, noteName)` | —
|
|
1295
|
-
| `journalPageId` | `function journalPageId(entryId, page)` | {string} A 16-character Foundry id.
|
|
1296
|
-
| `buildPages` | `function buildPages(rawPages, entryId, noteName)` | {Array<{_id: string, name: string, type: string, title: {show: boolean, level: number}, text: {format: number, content: string}, _key: string}>} The page documents, in order. | Compile split pages into JournalEntryPage documents. |
|
|
1297
|
-
| `buildJournalEntry` | `buildJournalEntry({ id, name, markdown, leadName, folder, flags, stats })` | {object} The JournalEntry document, keyed for the pack.
|
|
1298
|
-
| `Journals` | `class Journals extends BasePackCompiler` | —
|
|
1291
|
+
| Export | Signature | Returns | Use it when |
|
|
1292
|
+
| --------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
1293
|
+
| `splitPages` | `function splitPages(body, leadName` | — | Splits a markdown body into pages by top-level H1 headings. |
|
|
1294
|
+
| `assertUniquePages` | `function assertUniquePages(rawPages, noteName)` | — | Two pages in one note that would derive the same id, which the LevelDB packer reports only as an opaque duplicate-key collision. |
|
|
1295
|
+
| `assertUniqueAnchors` | `function assertUniqueAnchors(rawPages, noteName)` | — | The anchor half of {@link assertUniquePages}, under its former name. |
|
|
1296
|
+
| `journalPageId` | `function journalPageId(entryId, page)` | {string} A 16-character Foundry id. | The id of one page within its entry. |
|
|
1297
|
+
| `buildPages` | `function buildPages(rawPages, entryId, noteName)` | {Array<{\_id: string, name: string, type: string, title: {show: boolean, level: number}, text: {format: number, content: string}, \_key: string}>} The page documents, in order. | Compile split pages into JournalEntryPage documents. |
|
|
1298
|
+
| `buildJournalEntry` | `buildJournalEntry({ id, name, markdown, leadName, folder, flags, stats })` | {object} The JournalEntry document, keyed for the pack. | Assemble one JournalEntry document from a note's converted markdown. |
|
|
1299
|
+
| `Journals` | `class Journals extends BasePackCompiler` | — | not called directly — imported and driven by `engine/generate.mjs` as the "journals" pack compiler |
|
|
1299
1300
|
|
|
1300
1301
|
### `engine.macros`
|
|
1301
1302
|
|
|
@@ -1360,7 +1361,7 @@ Bundles, as notes — the Foundry `Adventure` a `type: bundle` note compiles int
|
|
|
1360
1361
|
| `BUNDLE_TYPE` | `const BUNDLE_TYPE` | — | The note type a bundle is authored as. |
|
|
1361
1362
|
| `ADVENTURE_CONTENT_FIELD` | `const ADVENTURE_CONTENT_FIELD` | — | Foundry's `Adventure.contentFields`, keyed by the document class each holds. |
|
|
1362
1363
|
| `CONTENTS_FIELD` | `const CONTENTS_FIELD` | — | The `data:` key a bundle lists its members under. |
|
|
1363
|
-
| `stripAdventureKeys` | `function stripAdventureKeys(value)` | {
|
|
1364
|
+
| `stripAdventureKeys` | `function stripAdventureKeys(value)` | {\*} The same shape with every `_key` removed. | Strip the LevelDB keys from a document tree. |
|
|
1364
1365
|
| `bareAddress` | `function bareAddress(value)` | {string\|null} The bare address, or `null` for a blank entry. | An authored address with any wikilink brackets and label stripped. |
|
|
1365
1366
|
| `bundleContents` | `function bundleContents(fm)` | {string[]} The bare addresses. | The addresses a bundle note names, in the order it names them. |
|
|
1366
1367
|
| `missingMemberVerdict` | `function missingMemberVerdict(packSystem)` | {"omit"\|"fail"} What to do about a member the sources lack. | What a pass should do about a member its sources do not hold. |
|
|
@@ -21,7 +21,9 @@ data: {}
|
|
|
21
21
|
---
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`name
|
|
24
|
+
`name` is a map with a required, nonempty `full` display name. Every note may also use `aliases` for an ordered list of other names. The note infobox lists aliases directly beneath the full name. For a `being` with `subType: character`, `given` and `clan` are optional name components. The first alias of a character or NPC is its nickname. Omit optional keys when they do not apply; `given`, `clan`, and alias entries must be nonempty strings when present. No other keys are accepted under `name`.
|
|
25
|
+
|
|
26
|
+
`description` is the short page summary. `tags` hold draft state and descriptive labels. The note type's classification belongs in `subType` where it is declared. `data` holds facts about the subject shared by systems. A YAML comment is the place for an author-only note: commented values remain valid YAML but do not enter the content index or generated documents.
|
|
25
27
|
|
|
26
28
|
Use `tags: [gm]` for a note intended only for the GM. It is absent from the public website and book. Foundry includes it only when its document routes to a pack with `private: true`. A note with `gm` that also creates a prose JournalEntry needs a private JournalEntry pack. Links from untagged notes to GM notes are errors; GM notes may link to one another.
|
|
27
29
|
|
|
@@ -117,7 +117,8 @@ Item link.
|
|
|
117
117
|
order** — the very table each `### type:` section below states. There is no
|
|
118
118
|
second list, so a key added to a type appears in the book, on the website and
|
|
119
119
|
in a compendium journal with no further edit. A `Name` row stands first on
|
|
120
|
-
every note, because every note has one.
|
|
120
|
+
every note, because every note has one. An `Aliases` row follows it when the
|
|
121
|
+
note names any aliases, preserving their authored order.
|
|
121
122
|
|
|
122
123
|
A few keys carry no row, and the reason in each case is one of exactly two —
|
|
123
124
|
the key is **machinery**, steering a build or an interface rather than
|
|
@@ -333,6 +334,13 @@ sohl: {}
|
|
|
333
334
|
Any other top-level key is an error. To keep an author-only note beside the
|
|
334
335
|
content, comment out its YAML lines with `#`. Comments remain valid YAML text
|
|
335
336
|
and do not become data in the JSONL index, generated pages, or Foundry documents.
|
|
337
|
+
|
|
338
|
+
`name` is a map with a required nonempty `full` string when present. It may
|
|
339
|
+
also contain `aliases`, an ordered list of nonempty strings. A being with
|
|
340
|
+
`subType: character` may additionally contain nonempty `given` and `clan`
|
|
341
|
+
strings. Other keys under `name` are errors. For a character or NPC, the first
|
|
342
|
+
alias is its nickname. A being's homes belong in `data.homes`.
|
|
343
|
+
|
|
336
344
|
There is no custom site metadata. `description` supplies the page's short
|
|
337
345
|
description; `tags` carries publication and classification tags.
|
|
338
346
|
|
|
@@ -2432,6 +2440,8 @@ For JournalEntries, the following rules apply:
|
|
|
2432
2440
|
|
|
2433
2441
|
**Archetypes** describe characters in broad terms and help match them to an adventure. The list is:
|
|
2434
2442
|
|
|
2443
|
+
<!-- archetypes:start -->
|
|
2444
|
+
|
|
2435
2445
|
- warrior: Can hold a line and win a fight.
|
|
2436
2446
|
- skirmisher: Fights light — ambush, missile, mobility.
|
|
2437
2447
|
- infiltrator: Gets in unseen — locks, stealth, disguise.
|
|
@@ -2448,6 +2458,10 @@ For JournalEntries, the following rules apply:
|
|
|
2448
2458
|
- entertainer: Performs for an audience through acting, music, comedy, or similar arts.
|
|
2449
2459
|
- guildsperson: Has professional training, standing, or connections that open doors.
|
|
2450
2460
|
|
|
2461
|
+
<!-- archetypes:end -->
|
|
2462
|
+
|
|
2463
|
+
`character` and `npc` beings require at least one archetype. `creature` beings may omit archetypes or write an empty array. Values use the exact lowercase spellings above.
|
|
2464
|
+
|
|
2451
2465
|
Note that archetypes are descriptive, not proscriptive, and a character may be described by multiple archetypes at once.
|
|
2452
2466
|
`commoner` stands alone: a being with another fitting archetype does not also carry `commoner`.
|
|
2453
2467
|
Guild membership alone does not require `guildsperson`; the being's training, standing, or connections must matter to the role.
|
|
@@ -2462,34 +2476,34 @@ An Item can mark its readable description with `# ... {#description}`. That head
|
|
|
2462
2476
|
|
|
2463
2477
|
Generates a living (or undead, or spirit) being.
|
|
2464
2478
|
|
|
2465
|
-
| `data` property | Values | Description
|
|
2466
|
-
| --------------------------- | ---------------------------------------------- |
|
|
2467
|
-
| `tokenIcon` | `Address` | What a token on the canvas wears — an `icon` address; defaults to `icon`
|
|
2468
|
-
| `templatePriority` | `number` | Template priority, _null_ = not a template
|
|
2469
|
-
| `archetypes` | `Archetype[]` | What sort of character this is. **Always an array** —
|
|
2470
|
-
| `occupation` | `string` | Name of the character's occupation
|
|
2471
|
-
| `social` | `Map<string, unknown>` | The being's social profile
|
|
2472
|
-
| `stations` | `Address[]` | Name of the stations the character belongs to
|
|
2473
|
-
| `lore` | `Address[]` | Other lore concerning this being, such as social standing or law
|
|
2474
|
-
| `culture` | `Address` | Primary culture, naming a lore note with `subType: culture`
|
|
2475
|
-
| `homes` | `Address[]` | Place the being calls home
|
|
2476
|
-
| `affiliations` | `Address
|
|
2477
|
-
| `socialTies` | `Map<Address, Tie>` | Defining support and opposition, directed from this being to others
|
|
2478
|
-
| `gender` | `male \| female \| other` | Gender of the character
|
|
2479
|
-
| `species` | `Address` | Being's species (lore)
|
|
2480
|
-
| `born` | `date \| unknown` | When the being was born; absent, it was never born
|
|
2481
|
-
| `calendar` | `Address` | Calendar note used to print the being's dates
|
|
2482
|
-
| `died` | `date \| unknown` | When the being died; absent, it is alive
|
|
2483
|
-
| `age` | `34 \| ~34` | Age in years, stated only to override what `born` says; `~` marks an estimate
|
|
2484
|
-
| `ageYears` | `number` | Written by the compiler beside an `age` estimate — the `~` stripped; a note never authors this
|
|
2485
|
-
| `height` | `1.91m \| 6' 3"` | Height in metres or feet and inches
|
|
2486
|
-
| `weight` | `85kg \| 187 lbs` | Body weight in kilograms or pounds
|
|
2487
|
-
| `frame` | `scant \| light \| medium \| large \| massive` | Relative frame size
|
|
2488
|
-
| `appearance.eye_color` | `string` | Eye color
|
|
2489
|
-
| `appearance.hair_color` | `string` | Hair color
|
|
2490
|
-
| `appearance.skin_color` | `string` | Skin color
|
|
2491
|
-
| `appearance.complexion` | `string` | Complexion
|
|
2492
|
-
| `appearance.extra_features` | `string[]` | Extra features
|
|
2479
|
+
| `data` property | Values | Description |
|
|
2480
|
+
| --------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2481
|
+
| `tokenIcon` | `Address` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
|
|
2482
|
+
| `templatePriority` | `number` | Template priority, _null_ = not a template |
|
|
2483
|
+
| `archetypes` | `Archetype[]` | What sort of character this is. **Always an array** — character and npc require at least one; creature may omit it or write `[]`; `null` is an error. |
|
|
2484
|
+
| `occupation` | `string` | Name of the character's occupation |
|
|
2485
|
+
| `social` | `Map<string, unknown>` | The being's social profile |
|
|
2486
|
+
| `stations` | `Address[]` | Name of the stations the character belongs to |
|
|
2487
|
+
| `lore` | `Address[]` | Other lore concerning this being, such as social standing or law |
|
|
2488
|
+
| `culture` | `Address` | Primary culture, naming a lore note with `subType: culture` |
|
|
2489
|
+
| `homes` | `Address[]` | Place the being calls home |
|
|
2490
|
+
| `affiliations` | `Map<Address, Membership>` | Bodies the being belongs to, keyed by Address, each entry the standing it holds there |
|
|
2491
|
+
| `socialTies` | `Map<Address, Tie>` | Defining support and opposition, directed from this being to others |
|
|
2492
|
+
| `gender` | `male \| female \| other` | Gender of the character |
|
|
2493
|
+
| `species` | `Address` | Being's species (lore) |
|
|
2494
|
+
| `born` | `date \| unknown` | When the being was born; absent, it was never born |
|
|
2495
|
+
| `calendar` | `Address` | Calendar note used to print the being's dates |
|
|
2496
|
+
| `died` | `date \| unknown` | When the being died; absent, it is alive |
|
|
2497
|
+
| `age` | `34 \| ~34` | Age in years, stated only to override what `born` says; `~` marks an estimate |
|
|
2498
|
+
| `ageYears` | `number` | Written by the compiler beside an `age` estimate — the `~` stripped; a note never authors this |
|
|
2499
|
+
| `height` | `1.91m \| 6' 3"` | Height in metres or feet and inches |
|
|
2500
|
+
| `weight` | `85kg \| 187 lbs` | Body weight in kilograms or pounds |
|
|
2501
|
+
| `frame` | `scant \| light \| medium \| large \| massive` | Relative frame size |
|
|
2502
|
+
| `appearance.eye_color` | `string` | Eye color |
|
|
2503
|
+
| `appearance.hair_color` | `string` | Hair color |
|
|
2504
|
+
| `appearance.skin_color` | `string` | Skin color |
|
|
2505
|
+
| `appearance.complexion` | `string` | Complexion |
|
|
2506
|
+
| `appearance.extra_features` | `string[]` | Extra features |
|
|
2493
2507
|
|
|
2494
2508
|
Author `data.height` as a string of decimal metres (`1.91m` or `1.91 m`) or
|
|
2495
2509
|
whole feet with optional inches (`6'`, `6'3"`, or `6' 3"`). Inches must be
|
|
@@ -2503,15 +2517,60 @@ describe a being's body. Item gear weight remains a numeric `data.weight`.
|
|
|
2503
2517
|
`data.culture` names the culture in which a character belongs. It is one
|
|
2504
2518
|
Address to a `type: lore`, `subType: culture` note. A named character with a
|
|
2505
2519
|
known culture states it here; generic beings and templates can omit it.
|
|
2506
|
-
`data.lore` holds
|
|
2507
|
-
|
|
2520
|
+
`data.lore` holds the law the being lives under, the customs it is subject to
|
|
2521
|
+
and the traditions it was raised in. Record the primary culture in
|
|
2522
|
+
`data.culture` only, and a standing in `data.affiliations`.
|
|
2508
2523
|
|
|
2509
2524
|
```yaml
|
|
2510
2525
|
data:
|
|
2511
2526
|
culture: lore-vedyariclt
|
|
2512
|
-
lore: [lore-
|
|
2527
|
+
lore: [lore-marchlaw]
|
|
2528
|
+
```
|
|
2529
|
+
|
|
2530
|
+
#### The bodies a being belongs to, and its standing in each
|
|
2531
|
+
|
|
2532
|
+
**Membership**: `{rank: number, office: string}`, both optional.
|
|
2533
|
+
|
|
2534
|
+
`data.affiliations` is a **map keyed by the body's Address**, and each entry is
|
|
2535
|
+
the standing the being holds in that body. The key is what makes the pair
|
|
2536
|
+
expressible: a being in two bodies holds a standing in each, and two bodies may
|
|
2537
|
+
call a rung the same word.
|
|
2538
|
+
|
|
2539
|
+
An entry carries two optional keys and nothing else:
|
|
2540
|
+
|
|
2541
|
+
- **`rank`** — a **number**, the level on that body's own `governance.ranks`
|
|
2542
|
+
ladder. The body declares `{level, title, description}` for every rung, so a
|
|
2543
|
+
number indexes into it and the body stays the single source for what its
|
|
2544
|
+
level 5 is called.
|
|
2545
|
+
- **`office`** — a **string**, matched against the keys of that body's
|
|
2546
|
+
`governance.offices` map.
|
|
2547
|
+
|
|
2548
|
+
An entry with neither is valid and is the ordinary shape: it says the being
|
|
2549
|
+
belongs to the body and nothing more.
|
|
2550
|
+
|
|
2551
|
+
```yaml
|
|
2552
|
+
data:
|
|
2553
|
+
affiliations:
|
|
2554
|
+
affiliation-vrystwldtrbs: { rank: 5, office: War Chief }
|
|
2555
|
+
affiliation-greenwardens: { rank: 1 }
|
|
2556
|
+
affiliation-silverguild: {}
|
|
2513
2557
|
```
|
|
2514
2558
|
|
|
2559
|
+
Both values are checked against the body the entry is keyed by. A `rank` that
|
|
2560
|
+
is not a level the body confers and an `office` that is not one of its keys are
|
|
2561
|
+
each an error naming the file, line and column — so a renamed rung or a renamed
|
|
2562
|
+
post fails on every being pointing at it. Naming one body twice, by its short
|
|
2563
|
+
and full Address, still names one body and is an error.
|
|
2564
|
+
|
|
2565
|
+
The infobox prints the standing closing on its body — `War Chief, Hárár, of
|
|
2566
|
+
Vrystwald Tribes` — and links an office to the description the body itself
|
|
2567
|
+
declares, so recording the post needs no note about the post.
|
|
2568
|
+
|
|
2569
|
+
For SoHL, a being embeds one affiliation item per entry: `rank` **is** that
|
|
2570
|
+
item's `system.level` and `office` its `system.office`. A standing is a fact
|
|
2571
|
+
about the setting rather than about one system, so the `sohl:` block states
|
|
2572
|
+
none of it.
|
|
2573
|
+
|
|
2515
2574
|
#### The people who matter to a being
|
|
2516
2575
|
|
|
2517
2576
|
`data.socialTies` records relationships that define the subject's dossier, not
|
|
@@ -2544,8 +2603,8 @@ an error.
|
|
|
2544
2603
|
Each tie is one-sided. The target need not name the subject back, even when
|
|
2545
2604
|
`patron` and `dependent` would describe opposite ends of the same support.
|
|
2546
2605
|
Kinship, household membership, organizational membership, rank, and office are
|
|
2547
|
-
distinct facts. Their relevant effects may appear as ties, while
|
|
2548
|
-
belong in
|
|
2606
|
+
distinct facts. Their relevant effects may appear as ties, while a membership
|
|
2607
|
+
and the standing held in it belong in `data.affiliations`. A tie has no
|
|
2549
2608
|
mechanical modifier.
|
|
2550
2609
|
|
|
2551
2610
|
#### When a being was born, when it died, and how old it is
|
|
@@ -30,34 +30,34 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
30
30
|
|
|
31
31
|
**Subtypes:** `npc`, `character`, `creature`.
|
|
32
32
|
|
|
33
|
-
| Field | Shape
|
|
34
|
-
| -------------------------------- |
|
|
35
|
-
| `data.social` | map
|
|
36
|
-
| `data.tokenIcon` | an Address
|
|
37
|
-
| `data.templatePriority` | number
|
|
38
|
-
| `data.archetypes` | list
|
|
39
|
-
| `data.occupation` | string
|
|
40
|
-
| `data.stations` | list of Addresses
|
|
41
|
-
| `data.lore` | list of Addresses
|
|
42
|
-
| `data.culture` | an Address
|
|
43
|
-
| `data.homes` | list of Addresses
|
|
44
|
-
| `data.affiliations` | list of Addresses
|
|
45
|
-
| `data.socialTies` | a map keyed by Address
|
|
46
|
-
| `data.gender` | string
|
|
47
|
-
| `data.species` | an Address
|
|
48
|
-
| `data.born` | string
|
|
49
|
-
| `data.calendar` | an Address
|
|
50
|
-
| `data.died` | string
|
|
51
|
-
| `data.age` | string
|
|
52
|
-
| `data.ageYears` | number
|
|
53
|
-
| `data.height` | metres or feet and inches
|
|
54
|
-
| `data.weight` | kilograms or pounds
|
|
55
|
-
| `data.frame` | string
|
|
56
|
-
| `data.appearance.eye_color` | string
|
|
57
|
-
| `data.appearance.hair_color` | string
|
|
58
|
-
| `data.appearance.skin_color` | string
|
|
59
|
-
| `data.appearance.complexion` | string
|
|
60
|
-
| `data.appearance.extra_features` | list
|
|
33
|
+
| Field | Shape | Meaning |
|
|
34
|
+
| -------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `data.social` | map | The being's social profile. |
|
|
36
|
+
| `data.tokenIcon` | an Address | What a token on the canvas wears — an `icon` address; unset, it follows `icon`. |
|
|
37
|
+
| `data.templatePriority` | number | Template priority; unset means the note is not a template. |
|
|
38
|
+
| `data.archetypes` | list | Archetypal behaviours the being fits. |
|
|
39
|
+
| `data.occupation` | string | What the being does for a living. |
|
|
40
|
+
| `data.stations` | list of Addresses | Stations the being holds. |
|
|
41
|
+
| `data.lore` | list of Addresses | Lore concerning this being — the law it lives under, the customs it is subject to, the traditions it was raised in. |
|
|
42
|
+
| `data.culture` | an Address | The being's primary culture, as a culture lore note. |
|
|
43
|
+
| `data.homes` | list of Addresses | Places the being calls home. |
|
|
44
|
+
| `data.affiliations` | a standing map keyed by Address, or a list of Addresses | Bodies the being belongs to, keyed by Address, each entry holding the standing it holds there — `rank`, a level on that body's own ladder, and `office`, a post that body names. |
|
|
45
|
+
| `data.socialTies` | a map keyed by Address | Defining ties directed from this being to others: `patron` (Supports the subject from greater power, wealth, or status.); `friend` (Supports the subject out of goodwill.); `dependent` (Relies on the subject's support or protection.); `acquaintance` (Knows the subject without a strong disposition.); `rival` (Opposes the subject without implacable hostility.); `nemesis` (Opposes the subject personally and implacably.) |
|
|
46
|
+
| `data.gender` | string | `male`, `female` or `other`. |
|
|
47
|
+
| `data.species` | an Address | The being's species, as a lore note. |
|
|
48
|
+
| `data.born` | string | When the being was born — a date, or `unknown` where the birth is unrecorded. Absent, the being was never born. |
|
|
49
|
+
| `data.calendar` | an Address | Calendar used to display this being's dates. |
|
|
50
|
+
| `data.died` | string | When the being died — a date, or `unknown` where the death is unrecorded. Absent, the being is alive. |
|
|
51
|
+
| `data.age` | string | Age in years, stated only to override what `born` says — `34`, or `~34` for an estimate. Unstated beside a dated `born` it is computed; unstated beside an unknown or absent `born` the age is unknown. |
|
|
52
|
+
| `data.ageYears` | number | Written by the compiler beside an `age` estimate — the `~` stripped, the magnitude alone. A note never authors this. |
|
|
53
|
+
| `data.height` | metres or feet and inches | Height in metres or feet and inches. |
|
|
54
|
+
| `data.weight` | kilograms or pounds | Body weight in kilograms or pounds. |
|
|
55
|
+
| `data.frame` | string | Relative frame — `scant`, `light`, `medium`, `large` or `massive`. |
|
|
56
|
+
| `data.appearance.eye_color` | string | Eye colour. |
|
|
57
|
+
| `data.appearance.hair_color` | string | Hair colour. |
|
|
58
|
+
| `data.appearance.skin_color` | string | Skin colour. |
|
|
59
|
+
| `data.appearance.complexion` | string | Complexion. |
|
|
60
|
+
| `data.appearance.extra_features` | list | Anything else a stranger would notice. |
|
|
61
61
|
|
|
62
62
|
**System mappings**
|
|
63
63
|
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -1133,8 +1133,8 @@ export class BasePackCompiler {
|
|
|
1133
1133
|
// - `aliases:`: it fed the alias index, which the bare
|
|
1134
1134
|
// `[[Alias]]` form was looked up in; the form is retired, so the
|
|
1135
1135
|
// list has no reader left. The nested `name.aliases` is a
|
|
1136
|
-
// different field
|
|
1137
|
-
//
|
|
1136
|
+
// different field: the infobox displays it, and the note name
|
|
1137
|
+
// check validates it.
|
|
1138
1138
|
//
|
|
1139
1139
|
// Both are reported and counted — never skipped, which is how a
|
|
1140
1140
|
// tree naming a package nothing answers to used to compile zero
|