@heroiclands/package-build 23.3.0 → 23.3.2

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.
@@ -2715,6 +2715,104 @@ table would otherwise need: the authored `ORDER BY` decides the section order to
2715
2715
  **Beware `packFolder`.** It is a note's _pack_ folder, not its directory — the
2716
2716
  directory is `file.folder`.
2717
2717
 
2718
+ ##### Culture parents and inherited fields
2719
+
2720
+ A `lore` note of `subType: culture` may list the larger cultures it belongs to
2721
+ in `data.parents`. Each entry is an Address naming another culture lore note;
2722
+ a missing target, a target of another type or subtype, and a parent cycle are
2723
+ errors at the offending list entry. No other lore subtype accepts the key.
2724
+
2725
+ A culture inherits `data.language` and `data.appearanceProfile` independently
2726
+ when it leaves either field out. A stated value wins for that field. The
2727
+ profile is inherited as one complete object; its weights and ranges are not
2728
+ merged with another profile. Parents are searched in listed order, depth-first:
2729
+ the first parent's chain is searched before the next parent's chain. Sources
2730
+ at the same depth that disagree on a field produce a finding. Nothing is
2731
+ inherited through a broken parent address.
2732
+
2733
+ The emitted content-index JSONL carries the resolved fields in the child's
2734
+ `data`, exactly as if the child had stated them. `package-build npc new` and
2735
+ other consumers read those resolved values; they do not walk culture parents.
2736
+ The child's `data.culture` remains its own Address. A being, place or
2737
+ affiliation naming the parent in `data.culture` belongs to that named culture;
2738
+ it does not match the parent's children.
2739
+
2740
+ These complete notes give Riverfolk their parent culture's tongue and profile:
2741
+
2742
+ ```markdown
2743
+ ---
2744
+ shortcode: reedfolk
2745
+ name: { full: Reedfolk }
2746
+ type: lore
2747
+ subType: culture
2748
+ data:
2749
+ language: skill-reedtongue
2750
+ appearanceProfile:
2751
+ frame: { medium: 1 }
2752
+ eye_color: { brown: 1 }
2753
+ hair_color: { black: 1 }
2754
+ skin_color: { tawny: 1 }
2755
+ complexion: { clear: 1 }
2756
+ height:
2757
+ female: { min: 1.5m, max: 1.7m }
2758
+ male: { min: 1.6m, max: 1.8m }
2759
+ ---
2760
+
2761
+ Reedfolk share the reed tongue and live along the marsh's waterways. Their
2762
+ settlements trade by boat, hold seasonal gatherings and pass their stories
2763
+ between families at those gatherings.
2764
+ ```
2765
+
2766
+ ```markdown
2767
+ ---
2768
+ shortcode: riverfolk
2769
+ name: { full: Riverfolk }
2770
+ type: lore
2771
+ subType: culture
2772
+ data:
2773
+ parents: [lore-reedfolk]
2774
+ ---
2775
+
2776
+ Riverfolk belong to the Reedfolk culture and share its tongue. They live
2777
+ beside the river's deep channels, where their boats carry grain, timber and
2778
+ woven reeds between settlements.
2779
+ ```
2780
+
2781
+ The parent names this language note, whose lists supply generated names:
2782
+
2783
+ ```markdown
2784
+ ---
2785
+ shortcode: reedtongue
2786
+ name: { full: Reed Tongue }
2787
+ type: skill
2788
+ subType: language
2789
+ data:
2790
+ pack: skills
2791
+ names:
2792
+ pattern: "{given} {kin}"
2793
+ given:
2794
+ female: [Nara]
2795
+ male: [Taro]
2796
+ kin: [Mori]
2797
+ ---
2798
+
2799
+ The reed tongue is spoken beside the marsh's waterways. Its speakers use a
2800
+ given name followed by a family name, and carry those names between river
2801
+ settlements with their trade.
2802
+ ```
2803
+
2804
+ In a package named `reedflats`, the Riverfolk index record carries the Reedfolk
2805
+ profile and `data.language` as a published Address object:
2806
+
2807
+ ```json
2808
+ { "address": "reedflats-note-skill-reedtongue", "anchor": null, "anchorKind": null }
2809
+ ```
2810
+
2811
+ Adding `language: skill-rivertongue` to Riverfolk's `data` selects that language
2812
+ instead while retaining the inherited profile, provided the named language
2813
+ note exists. Stating an `appearanceProfile` there replaces the inherited
2814
+ profile while leaving the inherited tongue in place.
2815
+
2718
2816
  ##### Culture in the index
2719
2817
 
2720
2818
  `data.culture` in the index is the note's **resolved** culture, which is its
@@ -3679,6 +3777,165 @@ The words, and what each means:
3679
3777
 
3680
3778
  <!-- being-look:end -->
3681
3779
 
3780
+ #### Appearance profiles
3781
+
3782
+ **A culture states what its people look like in `data.appearanceProfile`**, and
3783
+ `package-build npc new` draws a person of that culture from it — see
3784
+ [the command](../commands.md#package-build-npc-new). Only a `lore` note of
3785
+ `subType: culture` takes the key; on any other note it is an error at the key.
3786
+
3787
+ The profile has six keys, all required:
3788
+
3789
+ - `frame`, `eye_color`, `hair_color`, `skin_color` and `complexion` each map
3790
+ words of that field's [vocabulary](#a-beings-look) to positive weights. A
3791
+ word's chance of being drawn is its weight over the sum of the weights in its
3792
+ map; a word left out is never drawn.
3793
+ - `height` maps a `gender` word to a range, `{min, max}`, each a height in
3794
+ metres or feet and inches as `data.height` takes it. `female` and `male` are
3795
+ required. A gender with no range of its own draws from the span of every
3796
+ range the profile states.
3797
+
3798
+ Weight is not in the profile. It follows from the height and frame drawn: each
3799
+ frame word means a band of body-mass index, so a heavy frame always weighs more
3800
+ than a light one of the same height.
3801
+
3802
+ | Frame | Body-mass index (kg/m²) |
3803
+ | --------- | ----------------------- |
3804
+ | `scant` | 16.5 to 19 |
3805
+ | `light` | 19 to 21.5 |
3806
+ | `medium` | 21.5 to 24.5 |
3807
+ | `heavy` | 24.5 to 28 |
3808
+ | `massive` | 28 to 33 |
3809
+
3810
+ ```yaml
3811
+ shortcode: khelathi
3812
+ name: { full: The Khelâthi }
3813
+ type: lore
3814
+ subType: culture
3815
+ data:
3816
+ appearanceProfile:
3817
+ frame: { light: 2, medium: 5, heavy: 2 }
3818
+ eye_color: { brown: 6, dark_brown: 3, hazel: 1 }
3819
+ hair_color: { black: 6, dark_brown: 3 }
3820
+ skin_color: { tawny: 5, olive: 3, brown: 2 }
3821
+ complexion: { clear: 3, weathered: 2, smooth: 2 }
3822
+ height:
3823
+ female:
3824
+ min: 4' 11"
3825
+ max: 5' 8"
3826
+ male:
3827
+ min: 5' 4"
3828
+ max: 6' 3"
3829
+ ```
3830
+
3831
+ `package-build lint` checks the profile, with a finding at the word, weight or
3832
+ key at fault: a word outside its field's vocabulary or from another field's
3833
+ list, a weight that is not a positive number, a word map with no words, a
3834
+ missing key, a missing `female` or `male` range, a height that does not read,
3835
+ a range whose `min` is above its `max`, and a `height` key that is not a
3836
+ gender.
3837
+
3838
+ `package-build npc new` refuses a culture with no resolved profile, naming the
3839
+ culture note. A culture may obtain the complete profile from its
3840
+ [parents](#culture-parents-and-inherited-fields).
3841
+
3842
+ #### Name lists
3843
+
3844
+ **A language states how its speakers are named in `data.names`**, and
3845
+ `package-build npc new` draws a person's name from it, or checks a name it is
3846
+ given against it — see [the command](../commands.md#package-build-npc-new).
3847
+ Only a `skill` note of `subType: language` takes the key; on any other skill it
3848
+ is an error at the key. A culture reaches the lists through `data.language`,
3849
+ which names the language skill note its people speak, so cultures sharing a
3850
+ tongue share its lists.
3851
+
3852
+ The lists have two required keys, and any number of lists named by the
3853
+ language:
3854
+
3855
+ - `pattern` is the name's shape: placeholders such as `{given}` and `{clan}`,
3856
+ each naming a list, with the text around them written as it stands. Text may
3857
+ sit directly against a placeholder, as `sen` does in `"{given.male}sen"`.
3858
+ `"{given} {clan}"` puts the given name first, `"{family} {given}"` puts the
3859
+ family name first, and `"{given} yra {clan}"` joins the two with a particle.
3860
+ A pattern takes one of three forms:
3861
+ - **one pattern**, which every name takes;
3862
+ - **a list of patterns**, for a tongue whose people are named in several
3863
+ forms — by standing, by place, or by given name alone. A name may take any
3864
+ of them, and a drawn name takes one of them;
3865
+ - **a map of `gender` words** to a pattern or a list of them, for a tongue
3866
+ whose forms differ by gender. A gender with no pattern of its own takes
3867
+ every pattern stated.
3868
+ - `given` maps a `gender` word to a list of given names. `female` and `male`
3869
+ are required. `{given}` draws from the list for the person's gender, or from
3870
+ every given list together where that gender has none. `{given.<gender>}`
3871
+ draws from that gender's list whatever the person's gender, as a patronymic
3872
+ draws a father's name.
3873
+ - Every other key is a list of names that the placeholder of the same name
3874
+ draws from. A list name is letters, digits and underscores, beginning with a
3875
+ letter.
3876
+
3877
+ Each placeholder draws one name from its list, each name as likely as any
3878
+ other, and a drawn name takes one of its gender's patterns, each as likely as
3879
+ any other.
3880
+
3881
+ A tongue whose people are named by standing states each form in a list:
3882
+
3883
+ ```yaml
3884
+ shortcode: khelathilng
3885
+ name: { full: Khelâthi Language }
3886
+ type: skill
3887
+ subType: language
3888
+ data:
3889
+ names:
3890
+ pattern: ["{given} {house}", "{given} ez {place}", "{given}"]
3891
+ given:
3892
+ female: [Rethutê, Agosi, Ahmuzê]
3893
+ male: [Aguri, Khelemûr, Gezebari]
3894
+ house: [Reth'el'Lêru, Uqa'el'Lêru]
3895
+ place: [Zileti, Iqu, Lut-Psaqa]
3896
+ ```
3897
+
3898
+ A Khelâthi culture names its tongue with `language: skill-khelathilng`. A
3899
+ person drawn from these lists is called something like _Rethutê Reth'el'Lêru_,
3900
+ _Khelemûr ez Iqu_ or _Ahmuzê_, and a name an author gives in any of the three
3901
+ forms fits. A name fits when it takes one of the gender's patterns with every
3902
+ placeholder filled by a name of its list, the given name from the list for the
3903
+ person's gender: _Agosi ez Zileti_ fits a woman and not a man, and _Aguri ez
3904
+ Lut_ fits nobody, because _Lut_ is not in `place`. Names compare in Unicode
3905
+ NFC, so a precomposed and a decomposed accent are the same letter.
3906
+
3907
+ A tongue whose people carry a parent's name, with an ending that depends on
3908
+ their own gender, keys its patterns by gender:
3909
+
3910
+ ```yaml
3911
+ shortcode: northlng
3912
+ name: { full: Northern Language }
3913
+ type: skill
3914
+ subType: language
3915
+ data:
3916
+ names:
3917
+ pattern:
3918
+ male: "{given} {given.male}sen"
3919
+ female: "{given} {given.male}dóttir"
3920
+ given:
3921
+ female: [Sigrid, Astrid, Ragna]
3922
+ male: [Ulf, Harald, Torvald]
3923
+ ```
3924
+
3925
+ A man drawn from these lists is called something like _Ulf Torvaldsen_ and a
3926
+ woman _Astrid Ulfdóttir_: the second name is a man's given name either way,
3927
+ with the ending the pattern for the person's gender gives it. _Ulf
3928
+ Haralddóttir_ does not fit a man, because it has the form of the women's
3929
+ pattern.
3930
+
3931
+ `package-build lint` checks the lists, with a finding at the pattern, list,
3932
+ name or key at fault: a pattern with an unmatched brace or no placeholder, a
3933
+ placeholder that names no list, a `{given.<gender>}` whose gender has no given
3934
+ list, a dotted placeholder on a list other than `given`, a list that no pattern
3935
+ names, a list name a placeholder cannot spell, an empty list of patterns or of
3936
+ names, an empty name, a missing `pattern` or `given`, a missing `female` or
3937
+ `male` list, and a `pattern` or `given` key that is not a gender.
3938
+
3682
3939
  #### The bodies a being belongs to, and its standing in each
3683
3940
 
3684
3941
  **Membership**: `{rank: number, office: string, societies: string[]}` — `rank`
@@ -4684,11 +4941,16 @@ SoHL sets `system.quantity` to `1` for each compendium item.
4684
4941
  - combat
4685
4942
  - combattechnique
4686
4943
 
4687
- | `data` property | Values | Description |
4688
- | ------------------ | --------- | ------------------------------------------ |
4689
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
4690
- | `masteryLevel` | `number` | HM3 mastery level |
4691
- | `parentSkill` | `Address` | Parent skill this skill specializes |
4944
+ | `data` property | Values | Description |
4945
+ | -------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
4946
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
4947
+ | `masteryLevel` | `number` | HM3 mastery level |
4948
+ | `parentSkill` | `Address` | Parent skill this skill specializes |
4949
+ | `names` | `{ pattern, given, <name>... }` | `language` only: how its speakers are named, for `package-build npc new` — see [name lists](#name-lists) |
4950
+ | `names.pattern` | `string`, `string[]` or map of `gender` words to either, required | Placeholders such as `{given}`, `{given.male}` and `{clan}`, each naming a list, with literal text around them; a list states several forms, a map each gender's forms |
4951
+ | `names.given` | map of `gender` words to lists of names, required | Given names by gender; `female` and `male` are required |
4952
+ | `names.given.<name>` | `string[]` | The given names of one gender |
4953
+ | `names.<name>` | `string[]` | A list the placeholder of the same name draws from |
4692
4954
 
4693
4955
  if a `sohl` property is present, a SoHL item of type "skill" is created.
4694
4956
 
@@ -4843,48 +5105,63 @@ data:
4843
5105
  The saga lists under **In song and story** on `place-ironfells`, which holds the
4844
5106
  sack, on `place-east` and on `lore-fords`.
4845
5107
 
4846
- Calendar fields are written only on a `calendar` note, and `form`, `subjects` and `language`
4847
- only on a `literature` note; written on any other lore subType, each is an error at its own key.
5108
+ Calendar fields are written only on a `calendar` note, `form` and `subjects`
5109
+ only on a `literature` note, `language` only on `literature` and `culture`, and
5110
+ `parents` only on `culture`. Written on any other lore subtype, each is an
5111
+ error at its own key.
4848
5112
  `culture` may be written on any lore subType. A culture note is itself the culture, so it
4849
5113
  leaves the field out or names itself, by its shortcode or by any longer form of its Address;
4850
5114
  naming any other note is an error at the value. A `lore` note that states no culture has
4851
- none, since a `lore` note has no `data.parents` to inherit through; a culture note reads as
4852
- its own Address in the index. See [Culture in the index](#culture-in-the-index).
4853
-
4854
- | `data` property | Values | Description |
4855
- | -------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
4856
- | `epoch` | canonical `<year>.<day>` | The canonical day that equals calendar year 1, day 1 |
4857
- | `months` | `{ name, abbreviation?, days }[]` | Ordered months; their days sum to the world's year |
4858
- | `months[].name` | `string`, required | The month's name |
4859
- | `months[].abbreviation` | `string` | Its short form, for a compact date |
4860
- | `months[].days` | `integer`, required | How many days the month holds |
4861
- | `weekdays` | `{ name, abbreviation? }[]` | Ordered week days, indexed from zero; weeks run continuously |
4862
- | `weekdays[].name` | `string`, required | The day's name |
4863
- | `weekdays[].abbreviation` | `string` | Its short form, for a compact date |
4864
- | `seasons` | `{ name, abbreviation?, start }[]` | Seasons starting on one-based days of year |
4865
- | `seasons[].name` | `string`, required | The season's name |
4866
- | `seasons[].abbreviation` | `string` | Its short form, for a compact date |
4867
- | `seasons[].start` | `integer`, required | The one-based day of the year it begins; starts increase down the list |
4868
- | `namedDays` | `{ name, abbreviation?, day }[]` | Names assigned to one-based days of year |
4869
- | `namedDays[].name` | `string`, required | The day's name |
4870
- | `namedDays[].abbreviation` | `string` | Its short form, for a compact date |
4871
- | `namedDays[].day` | `integer`, required | The one-based day of the year it names, distinct within the calendar |
4872
- | `eras` | `{ shortcode, name, abbreviation?, marker?, proclaimedBy?, start, label? }[]` | Year counts; `start` is `null` or an in-calendar year |
4873
- | `eras[].shortcode` | `string`, required | The era's own segment of `<calendar>.<era>`, unique within the calendar |
4874
- | `eras[].name` | `string`, required | The era's name |
4875
- | `eras[].marker` | `string` | Uppercase letters and digits, beginning with a letter, unique across the corpus; an authored date names the era by it |
4876
- | `eras[].abbreviation` | `string` | Its short form, for a compact date |
4877
- | `eras[].proclaimedBy` | `Address` | The body that began the reckoning |
4878
- | `eras[].start` | `null` or `integer`, required | The calendar year the era begins; `null` for the era before year 1 |
4879
- | `eras[].label` | `string` or `{ after?, before? }` | How a date in the era reads, around one `{date}` slot; a string is `after` |
4880
- | `eras[].label.after` | `string` | How a date from the era's start reads, with exactly one `{date}` |
4881
- | `eras[].label.before` | `string` | How a date before the era's start reads, with exactly one `{date}` |
4882
- | `formats` | map of names to Calendaria patterns | Named date formats; `std` is the preferred default |
4883
- | `culture` | `Address` | The people this lore belongs to — for a `literature` note, the people whose work it is — naming a lore note with `subType: culture`; not on a `culture` note |
4884
- | `form` | `string` | `literature` only: the kind of work in its people's own terms — `epic`, `saga`, `praise-song`, `elegy`. Free text |
4885
- | `subjects` | `Address[]` | `literature` only: the beings, places, gods and other notes the work concerns, and any event, as `note#id` |
4886
- | `language` | `Address` | `literature` only: the tongue it is composed in, naming a skill note with `subType: language` |
4887
- | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
5115
+ none; a culture note reads as its own Address in the index, including when it
5116
+ names parent cultures. Culture parents supply language and appearance fields,
5117
+ not a different cultural identity. See
5118
+ [Culture parents](#culture-parents-and-inherited-fields) and
5119
+ [Culture in the index](#culture-in-the-index).
5120
+
5121
+ | `data` property | Values | Description |
5122
+ | ------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5123
+ | `epoch` | canonical `<year>.<day>` | The canonical day that equals calendar year 1, day 1 |
5124
+ | `months` | `{ name, abbreviation?, days }[]` | Ordered months; their days sum to the world's year |
5125
+ | `months[].name` | `string`, required | The month's name |
5126
+ | `months[].abbreviation` | `string` | Its short form, for a compact date |
5127
+ | `months[].days` | `integer`, required | How many days the month holds |
5128
+ | `weekdays` | `{ name, abbreviation? }[]` | Ordered week days, indexed from zero; weeks run continuously |
5129
+ | `weekdays[].name` | `string`, required | The day's name |
5130
+ | `weekdays[].abbreviation` | `string` | Its short form, for a compact date |
5131
+ | `seasons` | `{ name, abbreviation?, start }[]` | Seasons starting on one-based days of year |
5132
+ | `seasons[].name` | `string`, required | The season's name |
5133
+ | `seasons[].abbreviation` | `string` | Its short form, for a compact date |
5134
+ | `seasons[].start` | `integer`, required | The one-based day of the year it begins; starts increase down the list |
5135
+ | `namedDays` | `{ name, abbreviation?, day }[]` | Names assigned to one-based days of year |
5136
+ | `namedDays[].name` | `string`, required | The day's name |
5137
+ | `namedDays[].abbreviation` | `string` | Its short form, for a compact date |
5138
+ | `namedDays[].day` | `integer`, required | The one-based day of the year it names, distinct within the calendar |
5139
+ | `eras` | `{ shortcode, name, abbreviation?, marker?, proclaimedBy?, start, label? }[]` | Year counts; `start` is `null` or an in-calendar year |
5140
+ | `eras[].shortcode` | `string`, required | The era's own segment of `<calendar>.<era>`, unique within the calendar |
5141
+ | `eras[].name` | `string`, required | The era's name |
5142
+ | `eras[].marker` | `string` | Uppercase letters and digits, beginning with a letter, unique across the corpus; an authored date names the era by it |
5143
+ | `eras[].abbreviation` | `string` | Its short form, for a compact date |
5144
+ | `eras[].proclaimedBy` | `Address` | The body that began the reckoning |
5145
+ | `eras[].start` | `null` or `integer`, required | The calendar year the era begins; `null` for the era before year 1 |
5146
+ | `eras[].label` | `string` or `{ after?, before? }` | How a date in the era reads, around one `{date}` slot; a string is `after` |
5147
+ | `eras[].label.after` | `string` | How a date from the era's start reads, with exactly one `{date}` |
5148
+ | `eras[].label.before` | `string` | How a date before the era's start reads, with exactly one `{date}` |
5149
+ | `formats` | map of names to Calendaria patterns | Named date formats; `std` is the preferred default |
5150
+ | `culture` | `Address` | The people this lore belongs to — for a `literature` note, the people whose work it is — naming a lore note with `subType: culture`; not on a `culture` note |
5151
+ | `form` | `string` | `literature` only: the kind of work in its people's own terms — `epic`, `saga`, `praise-song`, `elegy`. Free text |
5152
+ | `subjects` | `Address[]` | `literature` only: the beings, places, gods and other notes the work concerns, and any event, as `note#id` |
5153
+ | `parents` | `Address[]` | `culture` only: the larger cultures this one belongs to; each target is a culture lore note — see [culture parents](#culture-parents-and-inherited-fields) |
5154
+ | `language` | `Address` | `literature` and `culture` only: the tongue a work is composed in, or a culture's people speak, naming a skill note with `subType: language`; `package-build npc new` draws a culture's names from that language's [name lists](#name-lists) |
5155
+ | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
5156
+ | `appearanceProfile` | `{ frame, eye_color, hair_color, skin_color, complexion, height }` | `culture` only: what its people look like, for `package-build npc new` — see [appearance profiles](#appearance-profiles) |
5157
+ | `appearanceProfile.frame` | map of `frame` words to positive weights, required | How often each frame is drawn |
5158
+ | `appearanceProfile.eye_color` | map of `eye_color` words to positive weights, required | How often each eye colour is drawn |
5159
+ | `appearanceProfile.hair_color` | map of `hair_color` words to positive weights, required | How often each hair colour is drawn |
5160
+ | `appearanceProfile.skin_color` | map of `skin_color` words to positive weights, required | How often each skin colour is drawn |
5161
+ | `appearanceProfile.complexion` | map of `complexion` words to positive weights, required | How often each complexion is drawn |
5162
+ | `appearanceProfile.height` | map of `gender` words to `{ min, max }`, required | Height ranges by gender; `female` and `male` are required |
5163
+ | `appearanceProfile.height.<name>.min` | height in metres or feet and inches, required | The shortest height drawn for that gender |
5164
+ | `appearanceProfile.height.<name>.max` | height in metres or feet and inches, required | The tallest height drawn for that gender |
4888
5165
 
4889
5166
  `data.events` is available on every `lore` subType, and is described in full under [Events](#events).
4890
5167