@heroiclands/package-build 19.0.0 → 20.0.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.md +624 -0
- package/CONTENT.md +79 -8
- package/bin/content-build.mjs +7 -1
- package/content-config.mjs +10 -1
- package/docs/content-format.md +394 -72
- package/engine/actor-compiler.mjs +197 -7
- package/engine/address-charset.mjs +23 -5
- package/engine/base-compiler.mjs +63 -2
- package/engine/bundles.mjs +9 -0
- package/engine/content-address.mjs +92 -1
- package/engine/content-format.mjs +102 -0
- package/engine/content-index.mjs +11 -8
- package/engine/content-links.mjs +37 -21
- package/engine/field-reference.mjs +57 -5
- package/engine/field-spec.mjs +214 -7
- package/engine/folder-notes.mjs +24 -1
- package/engine/foreign-catalog.mjs +4 -1
- package/engine/foundry-entries.mjs +14 -0
- package/engine/frontmatter-lint.mjs +186 -27
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +67 -10
- package/engine/helpers.mjs +86 -9
- package/engine/index.mjs +3 -0
- package/engine/item-compiler.mjs +37 -0
- package/engine/journals.mjs +21 -4
- package/engine/macros.mjs +8 -0
- package/engine/map-notes.mjs +7 -7
- package/engine/note-ids.mjs +25 -1
- package/engine/note-vocabulary.mjs +76 -9
- package/engine/retired-fields.mjs +57 -16
- package/engine/runtime-only-fields.mjs +204 -0
- package/engine/scenes.mjs +12 -19
- package/engine/schema-check.mjs +23 -1
- package/engine/site-index.mjs +17 -0
- package/engine/subtype-registry.mjs +30 -0
- package/engine/system-block.mjs +81 -3
- package/engine/web-wikilinks.mjs +33 -27
- package/engine/wikilink-syntax.mjs +7 -0
- package/engine/wikilinks.mjs +74 -16
- package/hm3/actors.mjs +63 -13
- package/package.json +2 -2
- package/sohl/actors.mjs +106 -7
- package/sohl/item-fields.mjs +203 -0
- package/sohl/note-schemas.mjs +6 -3
- package/types/engine/actor-compiler.d.mts +83 -3
- package/types/engine/address-charset.d.mts +22 -4
- package/types/engine/base-compiler.d.mts +54 -3
- package/types/engine/content-address.d.mts +64 -0
- package/types/engine/content-format.d.mts +9 -0
- package/types/engine/field-spec.d.mts +271 -3
- package/types/engine/folder-notes.d.mts +20 -0
- package/types/engine/foundry-entries.d.mts +6 -0
- package/types/engine/frontmatter-lint.d.mts +18 -2
- package/types/engine/frontmatter.d.mts +11 -11
- package/types/engine/generate.d.mts +27 -0
- package/types/engine/helpers.d.mts +37 -9
- package/types/engine/index.d.mts +1 -0
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/note-ids.d.mts +14 -0
- package/types/engine/retired-fields.d.mts +29 -13
- package/types/engine/runtime-only-fields.d.mts +102 -0
- package/types/engine/schema-check.d.mts +10 -1
- package/types/engine/subtype-registry.d.mts +21 -0
- package/types/engine/system-block.d.mts +28 -2
- package/types/sohl/actors.d.mts +3 -3
package/docs/content-format.md
CHANGED
|
@@ -161,6 +161,22 @@ document rather than the note. `title` is the one field this applies to; `subTyp
|
|
|
161
161
|
is the other declared item field spelled like a note-level key, and there the two
|
|
162
162
|
levels mean the same thing by design.
|
|
163
163
|
|
|
164
|
+
**Some of a system's fields are runtime state, and a note may not write any of
|
|
165
|
+
them.** A data model declares everything a document stores, and part of that is
|
|
166
|
+
what _play_ writes: an affliction's `onsetDate` is the world time its onset
|
|
167
|
+
fired at. Writing `<system>.system.<field>` reaches such a field as directly as
|
|
168
|
+
any other — the block is a verbatim passthrough, and the field really is in the
|
|
169
|
+
schema — so until #330 a note could stamp one, and the compiled pack shipped one
|
|
170
|
+
world's play state to every world that installed it.
|
|
171
|
+
|
|
172
|
+
So a field the document writes for itself is **declared as such**, and the
|
|
173
|
+
declaration says both halves of the fact: authoring it is an error naming the
|
|
174
|
+
note, the key and what the field holds, and the compiled document leaves the key
|
|
175
|
+
out entirely so the data model's own initial value stands. This is a property of
|
|
176
|
+
the field rather than a list of names, so it covers every such field a system
|
|
177
|
+
adds later. The per-type tables below never list one — they are the vocabulary
|
|
178
|
+
an author writes — and each type that has any names them under its table.
|
|
179
|
+
|
|
164
180
|
**A `WikiLink` becomes a shortcode where the target field expects one.** SoHL
|
|
165
181
|
stores cross-references as shortcode strings, which is what the `Code` suffix
|
|
166
182
|
marks: `data.assocSkill` is a link to a skill note, and `system.assocSkillCode`
|
|
@@ -208,6 +224,16 @@ first matching value. Where a mapping is missing for a whole class of note, that
|
|
|
208
224
|
is a gap in the vocabulary rather than something to write into every note: an
|
|
209
225
|
override that thousands of notes need is a missing subType value.
|
|
210
226
|
|
|
227
|
+
**A type's `subType` values are stated in one shape**, so that they can be read
|
|
228
|
+
and compared to the vocabulary that enforces them (#345). Under the type's
|
|
229
|
+
heading, write `**subType**:` on a line of its own, then one bullet per value —
|
|
230
|
+
`- <value>` or `- <value>: <definition>` — and nothing else between the marker
|
|
231
|
+
and the list. A type that has no `subType`, or whose values are not enumerated
|
|
232
|
+
yet, writes no marker at all. Any other spelling is a build error naming the
|
|
233
|
+
line: five were in use, and a reader that accepted them all would keep accepting
|
|
234
|
+
the sixth by reading a section as declaring nothing, which is exactly the drift
|
|
235
|
+
the comparison exists to catch.
|
|
236
|
+
|
|
211
237
|
### Mappings every type shares
|
|
212
238
|
|
|
213
239
|
Eight rows were identical in all sixteen tables below, so they are stated once
|
|
@@ -219,7 +245,7 @@ type.
|
|
|
219
245
|
| `name.full` | `name` | `name` |
|
|
220
246
|
| `img` | `img` | `img` |
|
|
221
247
|
| `id` | `_id` | `_id` |
|
|
222
|
-
| `packFolder`
|
|
248
|
+
| `packFolder` | `folder` | `folder` |
|
|
223
249
|
| `shortcode` | `system.shortcode` | NA |
|
|
224
250
|
| `data.templatePriority` | `system.templatePriority` | `flags.hm3.templatePriority` |
|
|
225
251
|
| `actionDefs` | `system.actionDefs` | NA |
|
|
@@ -231,6 +257,30 @@ no SoHL form, so its SoHL column is. Nothing else varies — which is why these
|
|
|
231
257
|
rows are worth stating once: repeated sixteen times they buried the differences
|
|
232
258
|
that matter.
|
|
233
259
|
|
|
260
|
+
**`img` maps onto a document that has one, and not every type's does.** The row
|
|
261
|
+
is shared because the key is legal on every note whatever its type, not because
|
|
262
|
+
every document carries artwork: `doc`, `place`, `lore` and `scenario` compile
|
|
263
|
+
into a JournalEntry, which has no image of any kind, and a `folder` compiles into
|
|
264
|
+
a Foundry `Folder`, which has none either. A `homepage` compiles into no
|
|
265
|
+
compendium document at all. On any of those the mapping has no destination, so an
|
|
266
|
+
authored path is simply dropped — the note validates, the tree compiles, and the
|
|
267
|
+
value goes nowhere.
|
|
268
|
+
|
|
269
|
+
Since a dropped value looks exactly like a value never written, the frontmatter
|
|
270
|
+
lint **reports it**: an `img:` (or `portrait:`) authored on a type whose passes
|
|
271
|
+
emit neither is a warning naming the note and the key. A warning rather than an
|
|
272
|
+
error, because the note still compiles correctly and the key is not certainly
|
|
273
|
+
unwanted — a note's top level is the generated page's front matter as well, so a
|
|
274
|
+
site template may read there what no document carries. `img: null` is never
|
|
275
|
+
reported: that is the blessed way to say "this note names no art", and on a type
|
|
276
|
+
with no art it is a true and harmless thing to say.
|
|
277
|
+
|
|
278
|
+
Which types those are is **not a list**. It is asked of the passes: a type routes
|
|
279
|
+
to a document, a document to the pass that compiles it, and each pass declares
|
|
280
|
+
the art it writes (`emitsArt`). A second table of iconless types would be a table
|
|
281
|
+
free to drift from what is actually emitted, which is the defect rather than the
|
|
282
|
+
check.
|
|
283
|
+
|
|
234
284
|
Actor types (`being`, `vehicle`) add one more:
|
|
235
285
|
|
|
236
286
|
| shared source | → sohl | → hm3 |
|
|
@@ -240,7 +290,20 @@ Actor types (`being`, `vehicle`) add one more:
|
|
|
240
290
|
An actor carries `img` (its token art) and `portrait` (its sheet portrait)
|
|
241
291
|
independently, which is why this is a row of its own rather than a second
|
|
242
292
|
spelling of the one above. An Item has no second image, so the row applies to
|
|
243
|
-
actor types alone.
|
|
293
|
+
actor types alone. Note the asymmetry in where the two are authored: `img` stays
|
|
294
|
+
at the note's top level and `portrait` moved under `data:`, because a note's
|
|
295
|
+
token art is a fact about the _note as a published artefact_ while the portrait
|
|
296
|
+
is a fact about the _subject_.
|
|
297
|
+
|
|
298
|
+
**A `data:` source is still read at the top level, for now.** `data:` (#128) did
|
|
299
|
+
not invent the facts it holds — it gathered them out of the top level, where
|
|
300
|
+
`portrait:` sat beside `img:` — so every key it collected has a **pre-`data:`
|
|
301
|
+
spelling** that is read after the declared one and reported as retiring. Write
|
|
302
|
+
`data.portrait`; a tree still on `portrait:` compiles to the identical document
|
|
303
|
+
and gets a warning naming the line, until a later release removes the position.
|
|
304
|
+
This is the shared level's counterpart to the in-block `<system>.<key>`
|
|
305
|
+
retirement, and the two are separate: a note may have moved one and not the
|
|
306
|
+
other.
|
|
244
307
|
|
|
245
308
|
**Two of the eight are Item-only in SoHL.** `actionDefs` and `notes` are declared
|
|
246
309
|
on every SoHL Item subtype and on no SoHL Actor, so on a `being` or a `vehicle`
|
|
@@ -268,6 +331,74 @@ item compiled from a template note loses the fact that it is one
|
|
|
268
331
|
(`HeroicLands/package-build#283`). The row states the mapping the format makes;
|
|
269
332
|
the gap is in the pass, not in the table.
|
|
270
333
|
|
|
334
|
+
#### An asset path's first segment says which package owns it
|
|
335
|
+
|
|
336
|
+
`img` and `portrait` are paths, and a path has to say **which package holds the
|
|
337
|
+
file** — because a module's content routinely cites the system's art, while the
|
|
338
|
+
system's content never cites the module's. The first segment answers that, and
|
|
339
|
+
there are exactly three answers:
|
|
340
|
+
|
|
341
|
+
| Authored path starts with | Owner | Emitted |
|
|
342
|
+
| ------------------------- | --------------------- | -------------------- |
|
|
343
|
+
| `systems/` | a separate **system** | unchanged |
|
|
344
|
+
| `modules/` | a separate **module** | unchanged |
|
|
345
|
+
| anything else | **this package** | `<assetRoot>/<path>` |
|
|
346
|
+
|
|
347
|
+
`<assetRoot>` is `<packageKind>/<foundryPackage>/assets`, derived from the
|
|
348
|
+
configuration — `systems/sohl/assets` for the system,
|
|
349
|
+
`modules/sohl-thalorna/assets` for that module. So one authored
|
|
350
|
+
`icons/relic.svg` means "my own `assets/icons/relic.svg`" in whichever package
|
|
351
|
+
writes it, while an authored `systems/sohl/assets/icons/noun/shield.svg` names
|
|
352
|
+
the system's file and is left exactly as written wherever it appears. That
|
|
353
|
+
second case is not hypothetical: every default this toolchain pairs with an item
|
|
354
|
+
type is a `systems/sohl/…` path, so a module's compiled documents carry it
|
|
355
|
+
verbatim.
|
|
356
|
+
|
|
357
|
+
**The third row is "anything else", not a list of directories.** It is a rule
|
|
358
|
+
about ownership: a package owns its whole `assets/` tree, and the directory
|
|
359
|
+
names inside it are that package's business. `sohl-kethira-basic` keeps art
|
|
360
|
+
under `assets/artwork/`, and `artwork/deity.webp` is rooted under its assets by
|
|
361
|
+
the same rule that roots `icons/…` and `images/…` there.
|
|
362
|
+
|
|
363
|
+
An address naming **no** package passes through untouched, which is that same
|
|
364
|
+
rule rather than an exception — an absolute URL, a `data:` URI and a `/`-rooted
|
|
365
|
+
path each already address something no package owns, so prefixing any of them
|
|
366
|
+
would break an address that was already correct.
|
|
367
|
+
|
|
368
|
+
`worlds/` is deliberately **not** exempt. A package may not ship art out of a
|
|
369
|
+
world, so a note writing one has made a mistake; prefixing it yields a plainly
|
|
370
|
+
broken path rather than a plausible one that 404s in Foundry with nothing
|
|
371
|
+
reporting it.
|
|
372
|
+
|
|
373
|
+
#### `banner:` addresses the CDN, not the Foundry install
|
|
374
|
+
|
|
375
|
+
**`banner:` is a path, and it does not follow the rule above.** It is worth
|
|
376
|
+
stating plainly, because the two fields look alike and a value written for one
|
|
377
|
+
resolves somewhere else entirely under the other.
|
|
378
|
+
|
|
379
|
+
`banner:` never reaches a compiled document — searching a built `packs-json`
|
|
380
|
+
tree for it turns up nothing. It is a top-level key, so it passes through to the
|
|
381
|
+
generated page, and its only consumer is the Hugo theme, whose
|
|
382
|
+
`partials/banner-url.html` applies its own rule: an absolute URL passes through,
|
|
383
|
+
and **anything else is prefixed with `images/`** and joined onto
|
|
384
|
+
`params.cdnBaseURL`. A `banner:` written to the package rule therefore resolves
|
|
385
|
+
to a doubled path:
|
|
386
|
+
|
|
387
|
+
```text
|
|
388
|
+
banner: systems/sohl/assets/images/banners/lore.webp
|
|
389
|
+
→ <cdnBaseURL>/images/systems/sohl/assets/images/banners/lore.webp
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
That can be made to work by mirroring the path on the CDN, and one consumer
|
|
393
|
+
does exactly that — but it is not what the author meant.
|
|
394
|
+
|
|
395
|
+
**The two are not reconciled, because they are not two spellings of one thing.**
|
|
396
|
+
`img:` addresses a file inside a Foundry install, where the package that holds
|
|
397
|
+
it is the question worth asking. `banner:` addresses a file on a CDN, where
|
|
398
|
+
there are no packages at all. Write a `banner:` relative to the CDN's `images/`
|
|
399
|
+
root — `banners/lore.webp`, not `images/banners/lore.webp` and not a
|
|
400
|
+
package-rooted path.
|
|
401
|
+
|
|
271
402
|
#### The pack a note compiles into
|
|
272
403
|
|
|
273
404
|
`pack` names which configured compendium receives the note's document.
|
|
@@ -396,10 +527,23 @@ _id = makeId("document", "<package>-<system>-<type>-<shortcode>")
|
|
|
396
527
|
```
|
|
397
528
|
|
|
398
529
|
— the note's canonical address, hashed with SHA-1 and truncated to the 16 hex
|
|
399
|
-
characters a Foundry id is. Nothing else feeds it.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
530
|
+
characters a Foundry id is. Nothing else feeds it.
|
|
531
|
+
|
|
532
|
+
**One type hashes under a different namespace, and a consumer must not assume
|
|
533
|
+
otherwise.** A `Folder` is a document of its own class, and its id is
|
|
534
|
+
`makeId("folder", <address>)` — a separate namespace so that a folder and an
|
|
535
|
+
item sharing a shortcode cannot derive one id, which Foundry would not report
|
|
536
|
+
because it keys folders and documents in separate collections. See
|
|
537
|
+
[`type: folder`](#type-folder).
|
|
538
|
+
|
|
539
|
+
**So the id is transported, not recomputed.** The content index publishes an
|
|
540
|
+
`id` on every entry it gives an identity to, beside the `uuid` that identity
|
|
541
|
+
ends in, and both are computed by whatever owns that entry's derivation — the
|
|
542
|
+
folder pass for a folder, the journals pass for a documentation journal, this
|
|
543
|
+
rule for everything else. Re-deriving one from the `canonical` key alone reads
|
|
544
|
+
correctly and is wrong for a folder, and wrong in a way nothing outside the
|
|
545
|
+
build can detect: the result is a plausible 16-character id that resolves to
|
|
546
|
+
nothing (#310).
|
|
403
547
|
|
|
404
548
|
**Why the address and not an authored string.** A note used to declare an
|
|
405
549
|
opaque 16-character `id` — 6,343 of them across the four content trees — which
|
|
@@ -409,7 +553,7 @@ every pack of a document type, which is exactly the scope a primary document's
|
|
|
409
553
|
id must be unique within, and it said nothing at all about a duplicate `id`. So
|
|
410
554
|
the derived id inherits a guard that already exists, where the authored one had
|
|
411
555
|
none. It is the same principle that turned `folder: ONXsqZAIZr2qzxTb` into
|
|
412
|
-
`packFolder: <
|
|
556
|
+
`packFolder: <address>` above: an opaque derived identity does not belong in
|
|
413
557
|
authored content.
|
|
414
558
|
|
|
415
559
|
**An authored `id` still wins**, and that is how a document's identity is
|
|
@@ -629,22 +773,58 @@ The full address is the unambiguous form, and almost nothing uses it — 92 of
|
|
|
629
773
|
12,056 links in the current trees. The rest rely on shorter forms, each dropping
|
|
630
774
|
segments from the left:
|
|
631
775
|
|
|
632
|
-
| form |
|
|
633
|
-
| ------------------------------- |
|
|
634
|
-
| `package-system-type-shortcode` |
|
|
635
|
-
| `
|
|
636
|
-
| `type-shortcode` |
|
|
637
|
-
| `shortcode` |
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
776
|
+
| form | expands to |
|
|
777
|
+
| ------------------------------- | -------------------------------------------------------- |
|
|
778
|
+
| `package-system-type-shortcode` | itself |
|
|
779
|
+
| `system-type-shortcode` | `<this package>-system-type-shortcode` |
|
|
780
|
+
| `type-shortcode` | `<this package>-<this block's system>-type-shortcode` |
|
|
781
|
+
| `shortcode` | as above, with the type from the field's own declaration |
|
|
782
|
+
|
|
783
|
+
There is no `package-type-shortcode`: the forms are exactly the **suffixes** of
|
|
784
|
+
the canonical address, so naming another package means naming its system too.
|
|
785
|
+
`kethira-place-tashal` is three segments, which reads as system `kethira`, and
|
|
786
|
+
fails.
|
|
787
|
+
|
|
788
|
+
#### An omitted segment defaults from where the link is written
|
|
789
|
+
|
|
790
|
+
It is **not** a wildcard and resolution is not a search. Every short form expands
|
|
791
|
+
to exactly one canonical address before anything is looked up, so a lookup either
|
|
792
|
+
finds one entry or none — there is no candidate set, and a cross-package
|
|
793
|
+
ambiguity is impossible by construction.
|
|
794
|
+
|
|
795
|
+
- **package** omitted → the current package. A short address therefore names
|
|
796
|
+
_this_ package and never falls through to a dependency; reaching another one is
|
|
797
|
+
the fully qualified form's job.
|
|
798
|
+
- **system** omitted → **the system block the link is written under**. Anywhere
|
|
799
|
+
under `sohl:` is `sohl`; anywhere under `hm3:` is `hm3`; **anywhere else** —
|
|
800
|
+
top-level frontmatter, `data:`, and body prose — is `none`. The enclosing block
|
|
801
|
+
decides at any depth, so `sohl.items[3].model` and `sohl.system.body.structure`
|
|
802
|
+
default alike; the field has no say.
|
|
803
|
+
|
|
804
|
+
**Under `none`, a system-bearing type addresses its documentation.** A note's
|
|
805
|
+
`none` address _is_ its `doc<type>` journal — the Item is the one with a system —
|
|
806
|
+
so a prose `[[affiliation-sirvadar|Sirvadar]]` names the page, which is what
|
|
807
|
+
prose almost always means. A prose link that means the **Item** states the
|
|
808
|
+
system: `[[sohl-affiliation-sirvadar|…]]`.
|
|
809
|
+
|
|
810
|
+
Only a type whose own document carries a system is redirected this way. A
|
|
811
|
+
`macro` and the map types have documentation journals too, but their own
|
|
812
|
+
documents are core ones already at `none`, so `macro-autoattack` names the Macro
|
|
813
|
+
and `docmacro-autoattack` its journal — two live addresses.
|
|
814
|
+
|
|
815
|
+
**An address is lowercase throughout** (#340). Every segment — package,
|
|
816
|
+
system, type and shortcode alike — is `^[a-z0-9]+$`, so a capital anywhere in an
|
|
817
|
+
address is an error naming the lowercase form.
|
|
818
|
+
|
|
819
|
+
The shortcode was the exception until #340: it was case-sensitive and routinely
|
|
820
|
+
mixed (`Clb`, `LtShoe`, `HsTunic`) while the address built from it was lowercased
|
|
821
|
+
wholesale, so the authored name and its address disagreed. Two names differing
|
|
822
|
+
only in case are two names nobody can tell apart, and they collapsed onto one
|
|
823
|
+
address, one `_id` and one URL with nothing to report it.
|
|
644
824
|
|
|
645
825
|
**Parsing is positional counting from the right, and nothing else.** Every
|
|
646
826
|
segment is alphanumeric — shortcodes, **types** and **subTypes** are all
|
|
647
|
-
`^[
|
|
827
|
+
`^[a-z0-9]+$`, systems come from a closed registry, and `contentPackage` is
|
|
648
828
|
alphanumeric — so the hyphen is purely a separator. There is no longest-match
|
|
649
829
|
against a roster and no vocabulary check before splitting.
|
|
650
830
|
|
|
@@ -659,7 +839,7 @@ against the same constant a shortcode is checked against, and a note carrying a
|
|
|
659
839
|
hyphenated value is reported where it wrote it:
|
|
660
840
|
|
|
661
841
|
```text
|
|
662
|
-
Trauma/Blood_Loss.md:3:1: error: `subType` "blood-loss" is not a well-formed subType — a subType is letters and digits only (^[
|
|
842
|
+
Trauma/Blood_Loss.md:3:1: error: `subType` "blood-loss" is not a well-formed subType — a subType is lowercase letters and digits only (^[a-z0-9]+$), the same charset a type, a shortcode and a contentPackage are held to. It is a vocabulary term the whole toolchain keys on, and one closed set away from being an address segment again, so a charset that held for every term but this one would be a rule nobody could state in a sentence
|
|
663
843
|
```
|
|
664
844
|
|
|
665
845
|
One declared value broke the rule and has been renamed: a `doc`'s `user-guide`
|
|
@@ -672,7 +852,7 @@ No retirement-specific code outlived the sweep.
|
|
|
672
852
|
|
|
673
853
|
That is a guarantee rather than an observation, and it holds: of **4,456 distinct
|
|
674
854
|
shortcodes** across the four content trees, not one contains a character outside
|
|
675
|
-
`[
|
|
855
|
+
`[a-z0-9]`. It is load-bearing, so relaxing the charset later would break
|
|
676
856
|
resolution with nothing to say so.
|
|
677
857
|
|
|
678
858
|
`type/shortcode` with a slash is the legacy form, still resolved so links written
|
|
@@ -735,14 +915,21 @@ nowhere is a typo or an omission, and both want fixing.
|
|
|
735
915
|
There are six ways a link can fail, and each is one **error** with one message
|
|
736
916
|
wherever it is met:
|
|
737
917
|
|
|
738
|
-
| finding | what it means
|
|
739
|
-
| ---------------- |
|
|
740
|
-
| `unlabelled` | no `\|`, so the link addresses nothing
|
|
741
|
-
| `not-an-address` | labelled, but the target is not an address
|
|
742
|
-
| `
|
|
743
|
-
| `
|
|
744
|
-
| `
|
|
745
|
-
| `
|
|
918
|
+
| finding | what it means | the fix |
|
|
919
|
+
| ---------------- | ----------------------------------------------- | ------------------------------------------------------ |
|
|
920
|
+
| `unlabelled` | no `\|`, so the link addresses nothing | write `[[type-shortcode\|Text]]` |
|
|
921
|
+
| `not-an-address` | labelled, but the target is not an address | write the address, not the name |
|
|
922
|
+
| `not-lowercase` | any segment of the address is capitalised | lowercase it — every segment is lowercase |
|
|
923
|
+
| `unknown-type` | qualified, but names no type this build knows | correct the type segment |
|
|
924
|
+
| `unresolved` | parses as an address; nothing publishes it | fix the shortcode, or qualify to reach another package |
|
|
925
|
+
| `ambiguous` | _unreachable since #336; kept for the manifest_ | — |
|
|
926
|
+
| `unknown-anchor` | the address resolves; the `#section` does not | correct the anchor |
|
|
927
|
+
|
|
928
|
+
`ambiguous` no longer fires. An omitted segment defaults rather than wildcarding,
|
|
929
|
+
so a written target expands to one canonical address and a lookup returns one
|
|
930
|
+
entry or none — two claimants is a state the grammar can no longer reach. The
|
|
931
|
+
reason is retained so a consumer switching on it does not break, and because a
|
|
932
|
+
vendored manifest built by an older toolchain may still carry the finding.
|
|
746
933
|
|
|
747
934
|
The vocabulary and the messages live in one module (`engine/wikilink-syntax.mjs`)
|
|
748
935
|
precisely because an author meets whichever build ran first. Three resolvers read
|
|
@@ -813,19 +1000,6 @@ Brackets belong in prose, where a link sits inside a sentence and needs marking
|
|
|
813
1000
|
off from the words around it. A frontmatter value has nothing to be marked off
|
|
814
1001
|
from.
|
|
815
1002
|
|
|
816
|
-
#### Not yet implemented
|
|
817
|
-
|
|
818
|
-
One rule in this section is settled but unbuilt, and describes the target rather
|
|
819
|
-
than current behaviour:
|
|
820
|
-
|
|
821
|
-
- **The `<system>` segment.** `readQualifier` reads package, type and shortcode;
|
|
822
|
-
there is no system segment. A four-segment target today parses as
|
|
823
|
-
`package-type-shortcode` with a hyphenated shortcode, or fails.
|
|
824
|
-
The corpus is close to the rule already. Of 12,056 links, 10,413 are
|
|
825
|
-
`[[type-shortcode|Label]]`, which is correct as written. Two authored links use
|
|
826
|
-
an unpiped multi-segment target, and 69 use a pipe with a note name where an
|
|
827
|
-
address belongs; those are the migration.
|
|
828
|
-
|
|
829
1003
|
### What a note produces
|
|
830
1004
|
|
|
831
1005
|
Note types fall into two groups, and only the first has a mapping table.
|
|
@@ -847,6 +1021,21 @@ having nothing worth summarising.
|
|
|
847
1021
|
Every note in **both** groups still produces its JournalEntry and its web page.
|
|
848
1022
|
The difference is only whether a system Actor or Item is created as well.
|
|
849
1023
|
|
|
1024
|
+
That has been true of actors only since #337. A being used to produce its Actor
|
|
1025
|
+
and nothing else, which left it the one system-bearing note with no address at
|
|
1026
|
+
`none` — so a prose link naming it had nowhere to land. It now carries a
|
|
1027
|
+
documentation journal like every other such note, addressed
|
|
1028
|
+
`<package>-none-docbeing-<shortcode>` beside the Actor's
|
|
1029
|
+
`<package>-<system>-being-<shortcode>`.
|
|
1030
|
+
|
|
1031
|
+
**A being keeps its prose inline as well.** `system.appearance` and
|
|
1032
|
+
`system.dossier` are still the rendered text, where an item's description is an
|
|
1033
|
+
`@UUID` pointer into its journal. The difference is deliberate and is about
|
|
1034
|
+
size: one item is embedded across hundreds of beings, so baking its description
|
|
1035
|
+
into every copy bloats the compendium by the length of the prose times the
|
|
1036
|
+
number of carriers, and the pointer buys that back. An actor is singular, so the
|
|
1037
|
+
same indirection would cost a reader a click and save nothing.
|
|
1038
|
+
|
|
850
1039
|
### One note is at most one document per system
|
|
851
1040
|
|
|
852
1041
|
A note produces **at most one document in each system**. That constraint is worth
|
|
@@ -1112,6 +1301,62 @@ Generates a living (or undead, or spirit) being.
|
|
|
1112
1301
|
| `appearance.complexion` | `string` | Complexion |
|
|
1113
1302
|
| `appearance.extra_features` | `string[]` | Extra features |
|
|
1114
1303
|
|
|
1304
|
+
#### A being's embedded items
|
|
1305
|
+
|
|
1306
|
+
`<system>.items` is a list, and each entry compiles into one embedded Item. An
|
|
1307
|
+
entry takes one of two shapes.
|
|
1308
|
+
|
|
1309
|
+
**A copy of a catalogue item** names it with `model:` — an address, read by the
|
|
1310
|
+
same grammar every wikilink is. The entry's remaining keys are merged over the
|
|
1311
|
+
model, so it carries the model's values except where it says otherwise.
|
|
1312
|
+
|
|
1313
|
+
```yaml
|
|
1314
|
+
sohl:
|
|
1315
|
+
items:
|
|
1316
|
+
- { model: skill-wpnc, system: { masteryLevelBase: 52 } }
|
|
1317
|
+
- { model: sohl-sohl-weapongear-whmr }
|
|
1318
|
+
- { model: sohl-sohl-weapongear-dgr, system: { shortcode: dgr2, name: Offhand dagger } }
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
| key | required | meaning |
|
|
1322
|
+
| -------- | -------- | ------------------------------------------------------------- |
|
|
1323
|
+
| `model` | yes | The address of the item this entry is a copy of |
|
|
1324
|
+
| `system` | no | Values that override the model's |
|
|
1325
|
+
| `name` | no | A name of this entry's own, where it differs from the model's |
|
|
1326
|
+
|
|
1327
|
+
**`type:` is not written beside a `model`.** The address already names the type,
|
|
1328
|
+
so a second statement of it is a place to be wrong, and it is refused.
|
|
1329
|
+
|
|
1330
|
+
**The address is written at whatever length says what it means.** Within this
|
|
1331
|
+
package `type-shortcode` is enough; reaching another package means the full
|
|
1332
|
+
`package-system-type-shortcode`, since the forms are suffixes of the canonical
|
|
1333
|
+
address. The system segment defaults from the block the entry sits in — an entry
|
|
1334
|
+
under `sohl.items` defaults to `sohl` — which is why the short form names an
|
|
1335
|
+
**Item** here while the same string in body prose names a page.
|
|
1336
|
+
|
|
1337
|
+
**A custom item** — one that copies nothing — states `name`, `type` **and**
|
|
1338
|
+
`system.shortcode`, all three required, plus whatever else its data model needs.
|
|
1339
|
+
It is written in **block form**, never inline, so the two kinds of entry are
|
|
1340
|
+
distinguishable at a glance:
|
|
1341
|
+
|
|
1342
|
+
```yaml
|
|
1343
|
+
sohl:
|
|
1344
|
+
items:
|
|
1345
|
+
- name: Whetstone
|
|
1346
|
+
type: miscgear
|
|
1347
|
+
system:
|
|
1348
|
+
shortcode: whetstone
|
|
1349
|
+
weight: 1
|
|
1350
|
+
value: 5
|
|
1351
|
+
durability: 0
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
**A top-level `shortcode:` is retired** (#334). It selected a template, while the
|
|
1355
|
+
`system.shortcode` beside it was the compiled item's identity — one word for two
|
|
1356
|
+
things — and it could not say which package a template came from, so an address
|
|
1357
|
+
resolved into a dependency only because no local pack claimed it and would have
|
|
1358
|
+
retargeted silently the day one did.
|
|
1359
|
+
|
|
1115
1360
|
#### Identifying a being's embedded items
|
|
1116
1361
|
|
|
1117
1362
|
Each entry in `sohl.items` compiles into one embedded Item, and its `_id` is
|
|
@@ -1121,28 +1366,26 @@ derived from **what the entry is**, never from where it sits in the list:
|
|
|
1121
1366
|
_id = makeId(<the actor's id>, "<subType>:<system.shortcode>")
|
|
1122
1367
|
```
|
|
1123
1368
|
|
|
1124
|
-
An entry's identity is its **own `system.shortcode`**. The
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1369
|
+
An entry's identity is its **own `system.shortcode`**. The `model:` is not it —
|
|
1370
|
+
a model names the item this entry is a _copy of_, and is never written to the
|
|
1371
|
+
document — so two entries may share one model and are then two embodiments, each
|
|
1372
|
+
stating its own identity:
|
|
1128
1373
|
|
|
1129
1374
|
```yaml
|
|
1130
1375
|
sohl:
|
|
1131
1376
|
items:
|
|
1132
|
-
-
|
|
1133
|
-
type: weapongear
|
|
1377
|
+
- model: weapongear-dgr # the catalogue's dagger
|
|
1134
1378
|
name: Dagger 1
|
|
1135
1379
|
system:
|
|
1136
1380
|
shortcode: dgr1 # this dagger's own identity
|
|
1137
|
-
-
|
|
1138
|
-
type: weapongear
|
|
1381
|
+
- model: weapongear-dgr
|
|
1139
1382
|
name: Dagger 2
|
|
1140
1383
|
system:
|
|
1141
1384
|
shortcode: dgr2
|
|
1142
1385
|
```
|
|
1143
1386
|
|
|
1144
1387
|
**Two entries resolving to one identity are a build error naming both.** Without
|
|
1145
|
-
their own `system.shortcode`, both daggers above carry the
|
|
1388
|
+
their own `system.shortcode`, both daggers above carry the model's `dgr`,
|
|
1146
1389
|
which makes them the same entity to everything that resolves by `(type,
|
|
1147
1390
|
shortcode)` — compendium/world reconciliation, template shadowing, cohort
|
|
1148
1391
|
membership, effect and expression references. A `name` cannot stand in: it is
|
|
@@ -1210,7 +1453,7 @@ the divine, magic, the spirit world — and that partition is load-bearing rathe
|
|
|
1210
1453
|
than descriptive: a system filtering which mystical practices may associate with
|
|
1211
1454
|
an affiliation can only be as precise as the distinction it filters on.
|
|
1212
1455
|
|
|
1213
|
-
subType
|
|
1456
|
+
**subType**:
|
|
1214
1457
|
|
|
1215
1458
|
- guild: A sworn association of craftsmen holding monopoly over a trade within a locality.
|
|
1216
1459
|
- order: A body of members bound by vows or a rule of life to a shared purpose.
|
|
@@ -1377,7 +1620,7 @@ one, at `sohl.system.title`.
|
|
|
1377
1620
|
|
|
1378
1621
|
Represents an affliction.
|
|
1379
1622
|
|
|
1380
|
-
**subType
|
|
1623
|
+
**subType**:
|
|
1381
1624
|
|
|
1382
1625
|
- disease: A biological affliction: an illness or parasite that infects the body or mind (e.g. typhoid, tuberculosis, river blindness)
|
|
1383
1626
|
- poisontoxin: A chemical affliction: a toxic substance or venom that impairs or kills the host (e.g. hemotoxin, mandrake, wasp venom)
|
|
@@ -1406,8 +1649,11 @@ Represents an affliction.
|
|
|
1406
1649
|
| `contagionIndex` | `number` | how contagious the disease is |
|
|
1407
1650
|
| `outcomeTraumas` | `SafeExpression` | Expression returning traumas that result from affliction recovery |
|
|
1408
1651
|
| `onsetDurationFormula` | `RollFormula` | Formula to calculate duration until onset after contracting affliction |
|
|
1652
|
+
| `onsetDurationBase` | `number` | That duration in seconds, stated outright instead of rolled |
|
|
1409
1653
|
| `healingCheckDurationFormula` | `RollFormula` | Formula to calculate duration until next healing check (measured from last healing check) |
|
|
1654
|
+
| `healingCheckDurationBase` | `number` | That duration in seconds, stated outright instead of rolled |
|
|
1410
1655
|
| `resolutionDurationFormula` | `RollFormula` | Formula to calculate duration after onset to resolution |
|
|
1656
|
+
| `resolutionDurationBase` | `number` | That duration in seconds, stated outright instead of rolled |
|
|
1411
1657
|
|
|
1412
1658
|
If `sohl` is present, this becomes an `affliction` item.
|
|
1413
1659
|
|
|
@@ -1420,8 +1666,41 @@ If `sohl` is present, this becomes an `affliction` item.
|
|
|
1420
1666
|
| `data.contagionIndex` | `system.contagionIndexBase` | NA |
|
|
1421
1667
|
| `data.outcomeTraumas` | `system.outcomeTraumas` | NA |
|
|
1422
1668
|
| `data.onsetDurationFormula` | `system.onsetDurationFormula` | NA |
|
|
1669
|
+
| `data.onsetDurationBase` | `system.onsetDurationBase` | NA |
|
|
1423
1670
|
| `data.healingCheckDurationFormula` | `system.healingCheckDurationFormula` | NA |
|
|
1671
|
+
| `data.healingCheckDurationBase` | `system.healingCheckDurationBase` | NA |
|
|
1424
1672
|
| `data.resolutionDurationFormula` | `system.resolutionDurationFormula` | NA |
|
|
1673
|
+
| `data.resolutionDurationBase` | `system.resolutionDurationBase` | NA |
|
|
1674
|
+
|
|
1675
|
+
**A timed phase is a triplet, and a note writes two thirds of it.** SoHL stores
|
|
1676
|
+
each phase as `{…DurationFormula, …DurationBase, …Date}`, and the three are
|
|
1677
|
+
authored differently:
|
|
1678
|
+
|
|
1679
|
+
| third | who writes it | what it says |
|
|
1680
|
+
| ------------------ | ------------- | -------------------------------------------------------- |
|
|
1681
|
+
| `…DurationFormula` | the note | what is **rolled** to get the interval |
|
|
1682
|
+
| `…DurationBase` | the note | the interval **outright**, in seconds, instead of a roll |
|
|
1683
|
+
| `…Date` | play | _when the phase actually fired_ |
|
|
1684
|
+
|
|
1685
|
+
Write the **formula** for a phase whose length varies — `"2d6*86400"` for an
|
|
1686
|
+
incubation of two-to-twelve days — and the **base** for one that does not. Both
|
|
1687
|
+
are intervals in **seconds**, and a bare number is a valid formula, so
|
|
1688
|
+
`onsetDurationFormula: 86400` and `onsetDurationBase: 86400` differ only in
|
|
1689
|
+
whether a roll is attempted.
|
|
1690
|
+
|
|
1691
|
+
**Omitting either omits the key**, rather than writing a default over it. That
|
|
1692
|
+
matters because the data model's own `null` is what several phases fall back on:
|
|
1693
|
+
a trauma that sets no `healingCheckDuration*` takes the world's configured
|
|
1694
|
+
healing-check interval, and one that sets no `bloodLossAdvanceDurationBase` does
|
|
1695
|
+
not bleed at all. A compile-time `null` is still a value, and would answer those
|
|
1696
|
+
questions for every note in every world.
|
|
1697
|
+
|
|
1698
|
+
**The `…Date` third is never authored.** It records when the phase fired, which
|
|
1699
|
+
only play can know, so `system.contractDate`, `system.onsetDate`,
|
|
1700
|
+
`system.treatmentDate` and `system.resolutionDate` are runtime state and a note
|
|
1701
|
+
that writes one fails the build. They are world times, and `0` is a valid one,
|
|
1702
|
+
so there is no blank a note could write either; leave them out and the data
|
|
1703
|
+
model's `null` stands.
|
|
1425
1704
|
|
|
1426
1705
|
### type: armorgear
|
|
1427
1706
|
|
|
@@ -1624,7 +1903,7 @@ If an `hm3` property is present, an HM3 item is created, and `hm3.type` states w
|
|
|
1624
1903
|
|
|
1625
1904
|
### type: projectilegear
|
|
1626
1905
|
|
|
1627
|
-
**
|
|
1906
|
+
**subType**:
|
|
1628
1907
|
|
|
1629
1908
|
- none
|
|
1630
1909
|
- arrow
|
|
@@ -1661,7 +1940,7 @@ HM3 side while remaining distinct on the SoHL side.
|
|
|
1661
1940
|
|
|
1662
1941
|
### type: skill
|
|
1663
1942
|
|
|
1664
|
-
**
|
|
1943
|
+
**subType**:
|
|
1665
1944
|
|
|
1666
1945
|
- social
|
|
1667
1946
|
- nature
|
|
@@ -1708,17 +1987,42 @@ Note: `hm3.system.type` (skill types) use the values "Craft", "Physical", "Commu
|
|
|
1708
1987
|
- shock: A prolonged physiological state of shock lasting hours or days, following severe trauma or blood loss — distinct from the transient combat-shock states.
|
|
1709
1988
|
- coma: A prolonged state of unconsciousness.
|
|
1710
1989
|
|
|
1711
|
-
| `data` property
|
|
1712
|
-
|
|
|
1713
|
-
| `templatePriority`
|
|
1990
|
+
| `data` property | Values | Description |
|
|
1991
|
+
| --------------------------------- | ------------- | ---------------------------------------------------------------------- |
|
|
1992
|
+
| `templatePriority` | `number` | Template priority, _null_ = not a template |
|
|
1993
|
+
| `healingCheckDurationFormula` | `RollFormula` | Formula for the interval between healing checks |
|
|
1994
|
+
| `healingCheckDurationBase` | `number` | That interval in seconds, stated outright instead of rolled |
|
|
1995
|
+
| `bloodLossAdvanceDurationFormula` | `RollFormula` | Formula for the interval between blood-loss advances |
|
|
1996
|
+
| `bloodLossAdvanceDurationBase` | `number` | That interval in seconds. Setting it is what makes the wound bleed |
|
|
1997
|
+
| `courseDurationFormula` | `RollFormula` | Formula for the interval between course tests — shock, coma, infection |
|
|
1998
|
+
| `courseDurationBase` | `number` | That interval in seconds, stated outright instead of rolled |
|
|
1714
1999
|
|
|
1715
2000
|
if a `sohl` property is present, a SoHL item of type "trauma" will be created.
|
|
1716
2001
|
|
|
1717
2002
|
If an `hm3` property is present, an HM3 item is created. `hm3.type` must be specified as either `injury` or `trait`.
|
|
1718
2003
|
|
|
1719
|
-
| shared source
|
|
1720
|
-
|
|
|
1721
|
-
| `subType`
|
|
2004
|
+
| shared source | → sohl | → hm3 |
|
|
2005
|
+
| -------------------------------------- | ---------------------------------------- | ----- |
|
|
2006
|
+
| `subType` | `system.subType` | NA |
|
|
2007
|
+
| `data.healingCheckDurationFormula` | `system.healingCheckDurationFormula` | NA |
|
|
2008
|
+
| `data.healingCheckDurationBase` | `system.healingCheckDurationBase` | NA |
|
|
2009
|
+
| `data.bloodLossAdvanceDurationFormula` | `system.bloodLossAdvanceDurationFormula` | NA |
|
|
2010
|
+
| `data.bloodLossAdvanceDurationBase` | `system.bloodLossAdvanceDurationBase` | NA |
|
|
2011
|
+
| `data.courseDurationFormula` | `system.courseDurationFormula` | NA |
|
|
2012
|
+
| `data.courseDurationBase` | `system.courseDurationBase` | NA |
|
|
2013
|
+
|
|
2014
|
+
**A trauma's three timed phases follow the triplet rule** stated under
|
|
2015
|
+
`affliction`: write the formula or the base, both in seconds, and omitting each
|
|
2016
|
+
leaves the key out so the data model answers. Two of them fall back to a **world
|
|
2017
|
+
setting** when the trauma sets neither half — the healing check and the course —
|
|
2018
|
+
which is why a default written here would be wrong rather than merely
|
|
2019
|
+
redundant. `bloodLossAdvanceDurationBase` is the switch that makes a wound
|
|
2020
|
+
bleed: a trauma that sets it bleeds, one that leaves it unset does not.
|
|
2021
|
+
|
|
2022
|
+
**`system.contractDate` and `system.treatmentDate` are never authored.** They
|
|
2023
|
+
are the world times the injury was taken and last treated — runtime state, for
|
|
2024
|
+
the reason `affliction`'s four dates are — so a note that writes one fails the
|
|
2025
|
+
build.
|
|
1722
2026
|
|
|
1723
2027
|
### type: weapongear
|
|
1724
2028
|
|
|
@@ -1749,7 +2053,7 @@ If an `hm3` property is present, an HM3 item is created, and `hm3.type` states w
|
|
|
1749
2053
|
|
|
1750
2054
|
In-world information about people, places, or concepts.
|
|
1751
2055
|
|
|
1752
|
-
subType
|
|
2056
|
+
**subType**:
|
|
1753
2057
|
|
|
1754
2058
|
- cosmology: The structure of reality — planes, realms, creation, and the ordering of what exists.
|
|
1755
2059
|
- deity: Individual gods and their attributed natures, domains, epithets, and aspects.
|
|
@@ -1765,6 +2069,12 @@ subType:
|
|
|
1765
2069
|
- culture: A social grouping of individuals with common beliefs, mores, and values.
|
|
1766
2070
|
- bestiary: A kind of creature that is not a people — beasts, monsters, and the made things
|
|
1767
2071
|
that were never born. What `folk` covers for the sapient, this covers for everything else.
|
|
2072
|
+
- gathering: A scheduled public occasion people travel to — a tournament or martial games, a
|
|
2073
|
+
great market or fair, a religious festival, a ceremony or rite. What these share is assembly
|
|
2074
|
+
on a cycle: a place, a time, and something contested or observed. Held apart from `calendar`,
|
|
2075
|
+
which covers the _reckoning_ — a festival's date is calendar and the festival is not, and a
|
|
2076
|
+
tournament is not a matter of time-reckoning at all — and from `culture`, which is a grouping
|
|
2077
|
+
of people rather than an occasion they attend.
|
|
1768
2078
|
|
|
1769
2079
|
| `data` property | Values | Description |
|
|
1770
2080
|
| --------------- | ------ | ----------- |
|
|
@@ -1792,7 +2102,7 @@ the place from the map is the end that cannot, because a map is written once and
|
|
|
1792
2102
|
depicts what it depicts. A place's maps are therefore derived — every map whose
|
|
1793
2103
|
`place` is this one — and the relation exists in exactly one place.
|
|
1794
2104
|
|
|
1795
|
-
subType
|
|
2105
|
+
**subType**:
|
|
1796
2106
|
|
|
1797
2107
|
- battlemap: Tactical scale, for a scene played out square by square.
|
|
1798
2108
|
- localmap: Roughly a kilometre across — a settlement, a holding, a small valley.
|
|
@@ -1851,11 +2161,16 @@ single Level is synthesised from `img`, `overlay`, `levelName` and
|
|
|
1851
2161
|
> system would want the identical art — so the field sits beside every other
|
|
1852
2162
|
> note's `img` rather than inside a system block.
|
|
1853
2163
|
>
|
|
1854
|
-
> `image`
|
|
1855
|
-
> (#56), and
|
|
1856
|
-
>
|
|
1857
|
-
>
|
|
1858
|
-
> a
|
|
2164
|
+
> `image` was **retired in favour of it**, in the three steps `package:` took
|
|
2165
|
+
> (#56), and all three have now run
|
|
2166
|
+
> ([package-build#149](https://github.com/HeroicLands/package-build/issues/149)).
|
|
2167
|
+
> Through the window both spellings were read, `img` won where a note carried
|
|
2168
|
+
> both, and a note still writing `image` got a located **warning** rather than a
|
|
2169
|
+
> refusal — it compiled to the byte-identical document, so failing a build over
|
|
2170
|
+
> it would have redded a tree that had done nothing wrong. The trees were then
|
|
2171
|
+
> swept, and the alias dropped. `image` is now simply not a key a map has: in a
|
|
2172
|
+
> `sohl:` block it is reported as unknown, and either way the note is refused
|
|
2173
|
+
> for the `img` it never declared.
|
|
1859
2174
|
|
|
1860
2175
|
**Two unit conventions, deliberately.** Geometry — walls, doors, lights, tiles,
|
|
1861
2176
|
sounds, region shapes — is authored in **pixels**, Foundry's native storage,
|
|
@@ -1895,7 +2210,7 @@ three maps, and none of those maps needs to know it is a keep.
|
|
|
1895
2210
|
|
|
1896
2211
|
### type: place
|
|
1897
2212
|
|
|
1898
|
-
subType
|
|
2213
|
+
**subType**:
|
|
1899
2214
|
|
|
1900
2215
|
- world: A self-contained whole in which places exist — a planet, plane, or realm.
|
|
1901
2216
|
- region: A bounded division of a world or larger region — continents, marches, uplands, provinces.
|
|
@@ -1938,7 +2253,7 @@ place it depicts — see `type: map` below.
|
|
|
1938
2253
|
|
|
1939
2254
|
Content prepared to be played — a situation with its cast, places, and possible outcomes.
|
|
1940
2255
|
|
|
1941
|
-
subType
|
|
2256
|
+
**subType**:
|
|
1942
2257
|
|
|
1943
2258
|
- campaign: A long arc toward a goal, spanning many adventures — carries standing cast, factions, and its own timeline.
|
|
1944
2259
|
- adventure: A self-contained undertaking with a specific objective, playable in a few sessions.
|
|
@@ -1957,7 +2272,7 @@ subType:
|
|
|
1957
2272
|
|
|
1958
2273
|
### type: doc
|
|
1959
2274
|
|
|
1960
|
-
subType
|
|
2275
|
+
**subType**:
|
|
1961
2276
|
|
|
1962
2277
|
- rules: The rules of the game, independent of medium — valid at a table with paper and dice.
|
|
1963
2278
|
- userguide: How to operate the Foundry implementation to play by the rules.
|
|
@@ -2198,3 +2513,10 @@ folder needs no invented id. An authored `id` is kept where one is present —
|
|
|
2198
2513
|
which is what lets a tree sweep its folder YAML into notes without a world that
|
|
2199
2514
|
already holds those folders losing them. Two folders claiming one id is a build
|
|
2200
2515
|
error.
|
|
2516
|
+
|
|
2517
|
+
The derivation is `makeId("folder", "<package>-none-folder-<shortcode>")`, and
|
|
2518
|
+
the namespace is **`folder`**, not the `document` one [every other note hashes
|
|
2519
|
+
under](#the-document-id): a folder and an item sharing a shortcode would
|
|
2520
|
+
otherwise derive the same id, and Foundry keys the two in separate collections
|
|
2521
|
+
so neither would complain. The content index publishes this value, so a consumer
|
|
2522
|
+
reads a folder's id rather than recomputing one (#310).
|