@optolith/entity-descriptions 0.2.1 → 0.3.1

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.
Files changed (187) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/lib/creator.d.ts +32 -0
  3. package/lib/creator.js +72 -0
  4. package/lib/entities/activatable.d.ts +52 -0
  5. package/lib/entities/activatable.js +609 -0
  6. package/lib/entities/alternativeRule.d.ts +7 -0
  7. package/lib/entities/alternativeRule.js +21 -0
  8. package/lib/entities/attribute.d.ts +6 -0
  9. package/lib/entities/attribute.js +15 -0
  10. package/lib/entities/combatTechnique.d.ts +5 -7
  11. package/lib/entities/combatTechnique.js +34 -24
  12. package/lib/entities/condition.d.ts +12 -0
  13. package/lib/entities/condition.js +63 -0
  14. package/lib/entities/culture.d.ts +8 -0
  15. package/lib/entities/culture.js +309 -0
  16. package/lib/entities/curriculum.d.ts +11 -0
  17. package/lib/entities/curriculum.js +266 -0
  18. package/lib/entities/derivedCharacteristic.d.ts +9 -0
  19. package/lib/entities/derivedCharacteristic.js +91 -0
  20. package/lib/entities/disease.d.ts +9 -0
  21. package/lib/entities/disease.js +88 -0
  22. package/lib/entities/elixir.d.ts +10 -0
  23. package/lib/entities/elixir.js +76 -0
  24. package/lib/entities/equipment.d.ts +24 -0
  25. package/lib/entities/equipment.js +613 -0
  26. package/lib/entities/equipmentPackage.d.ts +8 -0
  27. package/lib/entities/equipmentPackage.js +301 -0
  28. package/lib/entities/experienceLevel.d.ts +3 -2
  29. package/lib/entities/experienceLevel.js +33 -28
  30. package/lib/entities/focusRule.d.ts +3 -4
  31. package/lib/entities/focusRule.js +13 -5
  32. package/lib/entities/influence.d.ts +7 -0
  33. package/lib/entities/influence.js +35 -0
  34. package/lib/entities/liturgicalChant.d.ts +10 -23
  35. package/lib/entities/liturgicalChant.js +169 -131
  36. package/lib/entities/optionalRule.d.ts +3 -2
  37. package/lib/entities/optionalRule.js +4 -3
  38. package/lib/entities/partial/activatableNameChunks.d.ts +58 -0
  39. package/lib/entities/partial/activatableNameChunks.js +356 -0
  40. package/lib/entities/partial/adventurePointsValue.d.ts +9 -0
  41. package/lib/entities/partial/adventurePointsValue.js +243 -0
  42. package/lib/entities/partial/animalTypes.d.ts +11 -0
  43. package/lib/entities/partial/animalTypes.js +13 -0
  44. package/lib/entities/partial/commonnessRatedAdvantagesAndDisadvantages.d.ts +16 -0
  45. package/lib/entities/partial/commonnessRatedAdvantagesAndDisadvantages.js +40 -0
  46. package/lib/entities/partial/dice.d.ts +10 -0
  47. package/lib/entities/partial/dice.js +13 -0
  48. package/lib/entities/partial/herbary.d.ts +28 -0
  49. package/lib/entities/partial/herbary.js +65 -0
  50. package/lib/entities/partial/map.d.ts +49 -0
  51. package/lib/entities/partial/map.js +43 -0
  52. package/lib/entities/partial/mathOperation.d.ts +36 -0
  53. package/lib/entities/partial/mathOperation.js +107 -0
  54. package/lib/entities/partial/prerequisites/displayOption.d.ts +7 -0
  55. package/lib/entities/partial/prerequisites/displayOption.js +19 -0
  56. package/lib/entities/partial/prerequisites/index.d.ts +67 -0
  57. package/lib/entities/partial/prerequisites/index.js +189 -0
  58. package/lib/entities/partial/prerequisites/part.d.ts +26 -0
  59. package/lib/entities/partial/prerequisites/part.js +88 -0
  60. package/lib/entities/partial/prerequisites/prerequisiteGroups.d.ts +65 -0
  61. package/lib/entities/partial/prerequisites/prerequisiteGroups.js +269 -0
  62. package/lib/entities/partial/prerequisites/single/activatable.d.ts +18 -0
  63. package/lib/entities/partial/prerequisites/single/activatable.js +40 -0
  64. package/lib/entities/partial/prerequisites/single/animistPower.d.ts +8 -0
  65. package/lib/entities/partial/prerequisites/single/animistPower.js +23 -0
  66. package/lib/entities/partial/prerequisites/single/blessedTradition.d.ts +7 -0
  67. package/lib/entities/partial/prerequisites/single/blessedTradition.js +32 -0
  68. package/lib/entities/partial/prerequisites/single/commonSuggestedByRCP.d.ts +6 -0
  69. package/lib/entities/partial/prerequisites/single/commonSuggestedByRCP.js +19 -0
  70. package/lib/entities/partial/prerequisites/single/culture.d.ts +8 -0
  71. package/lib/entities/partial/prerequisites/single/culture.js +20 -0
  72. package/lib/entities/partial/prerequisites/single/enhancement.d.ts +8 -0
  73. package/lib/entities/partial/prerequisites/single/enhancement.js +41 -0
  74. package/lib/entities/partial/prerequisites/single/influence.d.ts +8 -0
  75. package/lib/entities/partial/prerequisites/single/influence.js +17 -0
  76. package/lib/entities/partial/prerequisites/single/magicalTradition.d.ts +7 -0
  77. package/lib/entities/partial/prerequisites/single/magicalTradition.js +27 -0
  78. package/lib/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.d.ts +6 -0
  79. package/lib/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.js +8 -0
  80. package/lib/entities/partial/prerequisites/single/pact.d.ts +8 -0
  81. package/lib/entities/partial/prerequisites/single/pact.js +30 -0
  82. package/lib/entities/partial/prerequisites/single/personalityTrait.d.ts +8 -0
  83. package/lib/entities/partial/prerequisites/single/personalityTrait.js +28 -0
  84. package/lib/entities/partial/prerequisites/single/primaryAttribute.d.ts +7 -0
  85. package/lib/entities/partial/prerequisites/single/primaryAttribute.js +15 -0
  86. package/lib/entities/partial/prerequisites/single/publication.d.ts +8 -0
  87. package/lib/entities/partial/prerequisites/single/publication.js +19 -0
  88. package/lib/entities/partial/prerequisites/single/race.d.ts +8 -0
  89. package/lib/entities/partial/prerequisites/single/race.js +20 -0
  90. package/lib/entities/partial/prerequisites/single/rated.d.ts +8 -0
  91. package/lib/entities/partial/prerequisites/single/rated.js +41 -0
  92. package/lib/entities/partial/prerequisites/single/ratedMinimumNumber.d.ts +8 -0
  93. package/lib/entities/partial/prerequisites/single/ratedMinimumNumber.js +89 -0
  94. package/lib/entities/partial/prerequisites/single/ratedSum.d.ts +8 -0
  95. package/lib/entities/partial/prerequisites/single/ratedSum.js +21 -0
  96. package/lib/entities/partial/prerequisites/single/rule.d.ts +7 -0
  97. package/lib/entities/partial/prerequisites/single/rule.js +4 -0
  98. package/lib/entities/partial/prerequisites/single/sex.d.ts +7 -0
  99. package/lib/entities/partial/prerequisites/single/sex.js +19 -0
  100. package/lib/entities/partial/prerequisites/single/sexualCharacteristic.d.ts +7 -0
  101. package/lib/entities/partial/prerequisites/single/sexualCharacteristic.js +21 -0
  102. package/lib/entities/partial/prerequisites/single/socialStatus.d.ts +8 -0
  103. package/lib/entities/partial/prerequisites/single/socialStatus.js +21 -0
  104. package/lib/entities/partial/prerequisites/single/state.d.ts +8 -0
  105. package/lib/entities/partial/prerequisites/single/state.js +20 -0
  106. package/lib/entities/partial/prerequisites/single/text.d.ts +7 -0
  107. package/lib/entities/partial/prerequisites/single/text.js +9 -0
  108. package/lib/entities/partial/professions.d.ts +15 -0
  109. package/lib/entities/partial/professions.js +19 -0
  110. package/lib/entities/partial/rated/activatable/castingTime.d.ts +16 -7
  111. package/lib/entities/partial/rated/activatable/castingTime.js +35 -20
  112. package/lib/entities/partial/rated/activatable/checkResultBased.d.ts +18 -3
  113. package/lib/entities/partial/rated/activatable/checkResultBased.js +18 -10
  114. package/lib/entities/partial/rated/activatable/cost.d.ts +88 -8
  115. package/lib/entities/partial/rated/activatable/cost.js +183 -115
  116. package/lib/entities/partial/rated/activatable/duration.d.ts +36 -13
  117. package/lib/entities/partial/rated/activatable/duration.js +72 -61
  118. package/lib/entities/partial/rated/activatable/effect.d.ts +4 -4
  119. package/lib/entities/partial/rated/activatable/effect.js +32 -25
  120. package/lib/entities/partial/rated/activatable/index.d.ts +31 -27
  121. package/lib/entities/partial/rated/activatable/index.js +63 -28
  122. package/lib/entities/partial/rated/activatable/isMinimumMaximum.d.ts +5 -6
  123. package/lib/entities/partial/rated/activatable/isMinimumMaximum.js +8 -5
  124. package/lib/entities/partial/rated/activatable/nonModifiableSuffix.d.ts +2 -4
  125. package/lib/entities/partial/rated/activatable/nonModifiableSuffix.js +7 -42
  126. package/lib/entities/partial/rated/activatable/range.d.ts +22 -14
  127. package/lib/entities/partial/rated/activatable/range.js +54 -54
  128. package/lib/entities/partial/rated/activatable/speed.d.ts +11 -2
  129. package/lib/entities/partial/rated/activatable/speed.js +14 -1
  130. package/lib/entities/partial/rated/activatable/targetCategory.d.ts +4 -5
  131. package/lib/entities/partial/rated/activatable/targetCategory.js +19 -24
  132. package/lib/entities/partial/rated/improvementCost.d.ts +16 -4
  133. package/lib/entities/partial/rated/improvementCost.js +63 -4
  134. package/lib/entities/partial/rated/skillCheck.d.ts +20 -17
  135. package/lib/entities/partial/rated/skillCheck.js +56 -54
  136. package/lib/entities/partial/reader.d.ts +266 -0
  137. package/lib/entities/partial/reader.js +175 -0
  138. package/lib/entities/partial/responsiveText.d.ts +10 -5
  139. package/lib/entities/partial/responsiveText.js +19 -3
  140. package/lib/entities/partial/units/energy.d.ts +5 -8
  141. package/lib/entities/partial/units/energy.js +5 -23
  142. package/lib/entities/partial/units/length.d.ts +20 -2
  143. package/lib/entities/partial/units/length.js +24 -5
  144. package/lib/entities/partial/units/timeSpan.d.ts +25 -4
  145. package/lib/entities/partial/units/timeSpan.js +27 -15
  146. package/lib/entities/partial/unknown.d.ts +5 -1
  147. package/lib/entities/partial/unknown.js +5 -1
  148. package/lib/entities/personalityTrait.d.ts +7 -0
  149. package/lib/entities/personalityTrait.js +56 -0
  150. package/lib/entities/poison.d.ts +12 -0
  151. package/lib/entities/poison.js +356 -0
  152. package/lib/entities/profession.d.ts +12 -0
  153. package/lib/entities/profession.js +585 -0
  154. package/lib/entities/race.d.ts +9 -0
  155. package/lib/entities/race.js +146 -0
  156. package/lib/entities/sexPractice.d.ts +6 -0
  157. package/lib/entities/sexPractice.js +33 -0
  158. package/lib/entities/skill.d.ts +9 -9
  159. package/lib/entities/skill.js +124 -91
  160. package/lib/entities/spell.d.ts +83 -26
  161. package/lib/entities/spell.js +835 -147
  162. package/lib/entities/state.d.ts +6 -0
  163. package/lib/entities/state.js +17 -0
  164. package/lib/helpers/enums.d.ts +11 -0
  165. package/lib/helpers/enums.js +6 -0
  166. package/lib/helpers/getTypes.d.ts +12 -482
  167. package/lib/helpers/getTypes.js +1 -0
  168. package/lib/helpers/identifiers.d.ts +314 -0
  169. package/lib/helpers/identifiers.js +333 -0
  170. package/lib/helpers/locale.d.ts +21 -2
  171. package/lib/helpers/translate.d.ts +47 -5
  172. package/lib/helpers/translate.js +13 -6
  173. package/lib/index.d.ts +848 -21
  174. package/lib/index.js +182 -17
  175. package/lib/references/index.d.ts +6 -3
  176. package/lib/references/index.js +25 -33
  177. package/lib/references/page.d.ts +1 -1
  178. package/lib/references/page.js +14 -14
  179. package/lib/references/pageRange.d.ts +1 -5
  180. package/lib/references/pageRange.js +7 -16
  181. package/lib/tsconfig.tsbuildinfo +1 -0
  182. package/package.json +30 -10
  183. package/.prettierrc.yml +0 -1
  184. package/lib/entities/partial/rated/activatable/entity.d.ts +0 -11
  185. package/lib/entities/partial/rated/activatable/entity.js +0 -12
  186. package/lib/references/occurrence.d.ts +0 -4
  187. package/lib/references/occurrence.js +0 -3
@@ -0,0 +1,585 @@
1
+ import { allSame } from "@elyukai/utils/array/filters";
2
+ import { ensureNonEmpty, isEmpty, isNotEmpty, } from "@elyukai/utils/array/nonEmpty";
3
+ import { deepEqual, equal } from "@elyukai/utils/equality";
4
+ import { on } from "@elyukai/utils/function";
5
+ import { isNotNullish, isNullish, mapNullable, nullableToArray, } from "@elyukai/utils/nullable";
6
+ import { compareNumber } from "@elyukai/utils/ordering";
7
+ import { Reader } from "@elyukai/utils/reader";
8
+ import { assertExhaustive } from "@elyukai/utils/typeSafety";
9
+ import { createEntityDescriptionCreator } from "../creator.js";
10
+ import { combineNameComponents, getNameComponents, renderActivatableNameComponents, renderCombinedActivatableNameComponents, } from "./partial/activatableNameChunks.js";
11
+ import { renderCommonnessRatedAdvantagesOrDisadvantages, renderValueWithPossibleTranslation, } from "./partial/commonnessRatedAdvantagesAndDisadvantages.js";
12
+ import { printProfessionPrerequisites } from "./partial/prerequisites/index.js";
13
+ import { parensIf } from "./partial/rated/activatable/parensIf.js";
14
+ import { formatR, getChildInstancesForInstanceIdR, getInstanceByIdR, localeCompareR, localeJoinR, localeSortR, nameR, strictNameR, translateR, translationR, } from "./partial/reader.js";
15
+ import { MISSING_VALUE, UNHANDLED_VALUE } from "./partial/unknown.js";
16
+ const prepareProfessionPackages = (professionId) => Reader.asks(({ getInstanceById, getChildInstancesForInstanceId }) => getChildInstancesForInstanceId("ProfessionPackage", professionId)
17
+ .map((professionPackage) => ({
18
+ ...professionPackage,
19
+ experienceLevel: getInstanceById("ExperienceLevel", professionPackage.content.experience_level),
20
+ }))
21
+ .filter((professionPackage) => professionPackage.experienceLevel !== undefined)
22
+ .toSorted(on(item => item.experienceLevel.adventure_points, compareNumber)));
23
+ const renderNumericListAcrossPackages = (packages, selector, equalityFn, renderText, defaultValue) => packages
24
+ .reduce((accR, pkg, pkgIndex) => accR.map2(selector(pkg), (acc, selected) => selected.reduce((filledAcc, [values, number]) => {
25
+ const existing = filledAcc.findIndex(([value]) => equalityFn(value, values));
26
+ if (existing >= 0) {
27
+ filledAcc[existing][1][pkgIndex] = number + defaultValue;
28
+ return filledAcc;
29
+ }
30
+ else {
31
+ return [
32
+ ...filledAcc,
33
+ [
34
+ values,
35
+ Array(pkgIndex)
36
+ .fill(defaultValue)
37
+ .concat(number + defaultValue),
38
+ ],
39
+ ];
40
+ }
41
+ }, acc.map(([value, numbers]) => [value, [...numbers, defaultValue]]))), Reader.of([]))
42
+ .thenW(list => Reader.traverse(list, ([value, numbers]) => renderText(value).map(text => `${text} ${numbers.join("/")}`)))
43
+ .thenW(list => localeSortR(list))
44
+ .map(list => ensureNonEmpty(list)?.join(", "));
45
+ const renderSinglePackageValue = (professionPackages, selector, equalityFn, renderText) => Reader.traverse(professionPackages, selector).thenW(selected => (allSame(selected, equalityFn) ? renderText([selected[0]]) : renderText(selected)));
46
+ const insteadOfR = (replacement, base) => translateR("{$replacement} instead of {$base}", { replacement, base });
47
+ /**
48
+ * If the selected values of the base and the variant are equal, only renders the value once. Otherwise, renders both values as the variant value replacing the base value.
49
+ */
50
+ const plainOrInsteadOfR = (base, variant, selector, equality, render) => {
51
+ const baseValue = selector(base);
52
+ const variantValue = selector(variant);
53
+ return equality(baseValue, variantValue)
54
+ ? render(baseValue)
55
+ : render(variantValue).thenW(replacement => render(baseValue).thenW(baseRendered => insteadOfR(replacement, baseRendered)));
56
+ };
57
+ const renderVariantOptionPaths = (base, variant, paths) => {
58
+ switch (variant.kind) {
59
+ case "Remove":
60
+ return base === undefined ? Reader.of(MISSING_VALUE) : paths.remove(base);
61
+ case "Override":
62
+ return base === undefined ? paths.add(variant.Override) : paths.update(base, variant.Override);
63
+ default:
64
+ return assertExhaustive(variant);
65
+ }
66
+ };
67
+ const renderVariantLanguagesScriptsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
68
+ remove: base => translateR("no Languages and Literacy totaling {$apValue} AP", { apValue: base.ap_value }),
69
+ add: override => translateR("Languages and Literacy totaling {$apValue} AP", { apValue: override.ap_value }),
70
+ update: (base, override) => translateR("{$replacement} instead of {$base}", {
71
+ replacement: override.ap_value,
72
+ base: base.ap_value,
73
+ }).then(apValue => translateR("Languages and Literacy totaling {$apValue} AP", {
74
+ apValue,
75
+ })),
76
+ });
77
+ const renderLanguagesScriptsOption = (option) => renderVariantLanguagesScriptsOption(undefined, { kind: "Override", Override: option });
78
+ const renderSkillSpecializationOption = (option) => Reader.asks(({ format, translate, translateMap, localeJoin, getInstanceById }) => {
79
+ switch (option.kind) {
80
+ case "Specific":
81
+ return translate("Skill Specialization {$possibleSkills}", {
82
+ possibleSkills: localeJoin(option.Specific.options.map(id => translateMap(getInstanceById("Skill", id)?.translations)?.name ?? MISSING_VALUE), "disjunction"),
83
+ });
84
+ case "Group":
85
+ return translate("Skill Specialization for a {$skillOfGroup}", {
86
+ skillOfGroup: format(translateMap(getInstanceById("SkillGroup", option.Group)?.translations)?.longName ??
87
+ MISSING_VALUE, { hiddenCount: 1 }),
88
+ });
89
+ default:
90
+ return assertExhaustive(option);
91
+ }
92
+ });
93
+ const renderCombatTechniquesOption = (option) => isNotEmpty(option.fixed)
94
+ ? (() => {
95
+ const [first, ...others] = option.fixed;
96
+ const firstTextR = translateR(".input {$count :number} {{{$count} of the following combat techniques {$rating}}}", { count: first.number, rating: first.rating_modifier + 6 });
97
+ const fixedTextR = others.reduce((accTextR, other) => accTextR.then(accText => translateR(".input {$count :number} {{{$previous}, {$count} others {$rating}}}", {
98
+ count: other.number,
99
+ previous: accText,
100
+ rating: other.rating_modifier + 6,
101
+ })), firstTextR);
102
+ const completeTextR = fixedTextR.then(fixedText => option.rest_rating_modifier === undefined
103
+ ? Reader.of(fixedText)
104
+ : translateR("{$previous}, all others {$rating}", {
105
+ previous: fixedText,
106
+ rating: option.rest_rating_modifier + 6,
107
+ }));
108
+ const listR = Reader.traverse(option.options, strictNameR)
109
+ .thenW(list => localeSortR(list))
110
+ .thenW(list => localeJoinR(list, "disjunction"));
111
+ return completeTextR.thenW(completeText => listR.map(list => `${completeText}: ${list}`));
112
+ })()
113
+ : Reader.of(MISSING_VALUE);
114
+ const getTotalingAPValues = (professionPackages, selector) => {
115
+ const options = professionPackages.map(selector);
116
+ if (!isNotEmpty(options) || options.every(isNullish)) {
117
+ return undefined;
118
+ }
119
+ return allSame(options, deepEqual)
120
+ ? options[0].ap_value
121
+ : options.map(option => option?.ap_value ?? 0).join("/");
122
+ };
123
+ const renderSkillsOption = (professionPackages, skillGroup) => mapNullable(getTotalingAPValues(professionPackages, pkg => pkg.content.options?.skills?.group === skillGroup.id
124
+ ? pkg.content.options?.skills
125
+ : undefined), apValue => translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
126
+ apValue,
127
+ skillsOfGroup: skillGroup.nameOfSkillsOfGroup,
128
+ })) ?? Reader.of(undefined);
129
+ const renderCantripList = (cantripIds) => Reader.traverse(cantripIds, id => strictNameR("Cantrip", id))
130
+ .thenW(localeSortR)
131
+ .thenW(list => localeJoinR(list, "disjunction"));
132
+ const renderVariantCantripsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
133
+ remove: base => renderCantripList(base.options).thenW(list => translateR(".input {$count :number} {{{$count} cantrips}}", { count: 0 })
134
+ .then(count => translateR("{$count} from the following list", { count }))
135
+ .map(text => `${text}: ${list}`)),
136
+ add: override => renderCantripList(override.options).thenW(list => translateR(".input {$count :number} {{{$count} cantrips}}", { count: override.number })
137
+ .then(count => translateR("{$count} from the following list", { count }))
138
+ .map(text => `${text}: ${list}`)),
139
+ update: (base, override) => deepEqual(base.options, override.options)
140
+ ? renderCantripList(override.options).thenW(list => translateR(".input {$count :number} {{{$count} cantrips}}", {
141
+ count: override.number,
142
+ }).then(baseCount => translateR(".input {$count :number} {{{$count} cantrips}}", {
143
+ count: override.number,
144
+ })
145
+ .then(replacementCount => translateR("{$replacement} instead of {$base}", {
146
+ replacement: replacementCount,
147
+ base: baseCount,
148
+ }).then(count => translateR("{$count} from the following list", { count })))
149
+ .map(text => `${text}: ${list}`)))
150
+ : renderCantripList(override.options)
151
+ .thenW(list => translateR(".input {$count :number} {{{$count} cantrips}}", {
152
+ count: override.number,
153
+ })
154
+ .then(count => translateR("{$count} from the following list", { count }))
155
+ .map(text => `${text}: ${list}`))
156
+ .then(replacementText => renderCantripList(base.options)
157
+ .thenW(list => translateR(".input {$count :number} {{{$count} cantrips}}", {
158
+ count: base.number,
159
+ })
160
+ .then(count => translateR("{$count} from the following list", { count }))
161
+ .map(text => `${text}: ${list}`))
162
+ .then(baseText => translateR("{$replacement} instead of {$base}", {
163
+ replacement: replacementText,
164
+ base: baseText,
165
+ }))),
166
+ });
167
+ const renderSingleCantripsOption = (option) => renderVariantCantripsOption(undefined, { kind: "Override", Override: option });
168
+ const renderCantripsOption = (professionPackages) => {
169
+ const cantripsOptions = professionPackages.map(pkg => pkg.content.options?.cantrips);
170
+ if (!isNotEmpty(cantripsOptions) || cantripsOptions.every(isNullish)) {
171
+ return Reader.of(undefined);
172
+ }
173
+ if (allSame(cantripsOptions, deepEqual)) {
174
+ return renderSingleCantripsOption(cantripsOptions[0]);
175
+ }
176
+ else {
177
+ return Reader.traverse(cantripsOptions, option => option === undefined ? Reader.of("—") : renderSingleCantripsOption(option)).map(list => list.join(" / "));
178
+ }
179
+ };
180
+ const renderLiturgiesOption = (professionPackages) => mapNullable(getTotalingAPValues(professionPackages, pkg => pkg.content.options?.liturgies), apValue => translateR("Liturgies totaling {$apValue} AP", {
181
+ apValue,
182
+ })) ?? Reader.of(undefined);
183
+ const renderVariantSkillSpecializationOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
184
+ remove: (base) => {
185
+ switch (base.kind) {
186
+ case "Specific":
187
+ return Reader.traverse(base.Specific.options, id => strictNameR("Skill", id))
188
+ .thenW(localeSortR)
189
+ .thenW(list => localeJoinR(list, "disjunction"))
190
+ .thenW(skillsText => translateR("no Skill Specialization {$possibleSkills}", {
191
+ possibleSkills: skillsText,
192
+ }));
193
+ case "Group":
194
+ return getInstanceByIdR("SkillGroup", base.Group)
195
+ .thenW(translationR)
196
+ .map(t => t?.longName ?? MISSING_VALUE)
197
+ .thenW(longName => formatR(longName, { hiddenCount: 1 }))
198
+ .thenW(skillOfGroup => translateR("no Skill Specialization for a {$skillOfGroup}", {
199
+ skillOfGroup,
200
+ }));
201
+ default:
202
+ return assertExhaustive(base);
203
+ }
204
+ },
205
+ add: override => renderSkillSpecializationOption(override),
206
+ update: (base, override) => {
207
+ if (base.kind === "Specific" && override.kind === "Specific") {
208
+ const overrideSkills = Reader.traverse(override.Specific.options, id => strictNameR("Skill", id)).thenW(list => localeJoinR(list, "disjunction"));
209
+ const baseSkills = Reader.traverse(base.Specific.options, id => strictNameR("Skill", id)).thenW(list => localeJoinR(list, "disjunction"));
210
+ return overrideSkills
211
+ .thenW(overrideSkillsText => baseSkills.thenW(baseSkillsText => insteadOfR(overrideSkillsText, baseSkillsText)))
212
+ .then(skillsText => translateR("Skill Specialization {$possibleSkills}", {
213
+ possibleSkills: skillsText,
214
+ }));
215
+ }
216
+ else {
217
+ return renderSkillSpecializationOption(override).then(overrideText => renderSkillSpecializationOption(base).then(baseText => insteadOfR(overrideText, baseText)));
218
+ }
219
+ },
220
+ });
221
+ const renderVariantCombatTechniquesOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
222
+ remove: base => renderCombatTechniquesOption({
223
+ ...base,
224
+ fixed: base.fixed.map(option => ({ ...option, number: 0 })),
225
+ }),
226
+ add: override => renderCombatTechniquesOption(override),
227
+ update: (base, override) => renderCombatTechniquesOption(override).then(overrideText => renderCombatTechniquesOption(base).then(baseText => insteadOfR(overrideText, baseText))),
228
+ });
229
+ const renderVariantCursesOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
230
+ remove: base => translateR("no Curses totaling {$apValue} AP", { apValue: base.ap_value }),
231
+ add: override => translateR("Curses totaling {$apValue} AP", { apValue: override.ap_value }),
232
+ update: (base, override) => insteadOfR(override.ap_value, base.ap_value).then(apValue => translateR("Curses totaling {$apValue} AP", {
233
+ apValue,
234
+ })),
235
+ });
236
+ const renderCursesOption = (option) => renderVariantCursesOption(undefined, { kind: "Override", Override: option });
237
+ const nameOfSkillsOfGroupR = (skillGroupId) => getInstanceByIdR("SkillGroup", skillGroupId)
238
+ .thenW(translationR)
239
+ .map(t => t?.longName ?? MISSING_VALUE);
240
+ const renderVariantSkillsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
241
+ remove: base => nameOfSkillsOfGroupR(base.group).thenW(nameOfSkillsOfGroup => translateR("no AP to improve other {$skillsOfGroup}", {
242
+ skillsOfGroup: nameOfSkillsOfGroup,
243
+ })),
244
+ add: override => nameOfSkillsOfGroupR(override.group).thenW(nameOfSkillsOfGroup => translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
245
+ apValue: override.ap_value,
246
+ skillsOfGroup: nameOfSkillsOfGroup,
247
+ })),
248
+ update: (base, override) => plainOrInsteadOfR(base, override, b => b.ap_value, equal, Reader.of).thenW(apValue => plainOrInsteadOfR(base, override, b => b.group, equal, nameOfSkillsOfGroupR).thenW(nameOfSkillsOfGroup => translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
249
+ apValue,
250
+ skillsOfGroup: nameOfSkillsOfGroup,
251
+ }))),
252
+ });
253
+ const renderVariantLiturgiesOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
254
+ remove: base => translateR("no Liturgies totaling {$apValue} AP", { apValue: base.ap_value }),
255
+ add: override => translateR("Liturgies totaling {$apValue} AP", { apValue: override.ap_value }),
256
+ update: (base, override) => insteadOfR(override.ap_value, base.ap_value).then(apValue => translateR("Liturgies totaling {$apValue} AP", {
257
+ apValue,
258
+ })),
259
+ });
260
+ const getSpecialAbilityNameComponents = (getInstanceById, getResolvedSelectOptionById, translate, option) => {
261
+ const instance = getInstanceById(option.id);
262
+ if (instance === undefined) {
263
+ return undefined;
264
+ }
265
+ return getNameComponents(translate, option.id, option.options, option.level, "nameBuilderRules" in instance ? instance.nameBuilderRules : undefined, instance.translations, t => t.name, getResolvedSelectOptionById, true);
266
+ };
267
+ const renderSpecialAbilityName = (specialAbility) => Reader.asks(({ translate, translateMap, localeJoin, getInstanceById, getResolvedSelectOptionById, }) => {
268
+ switch (specialAbility.kind) {
269
+ case "Constant": {
270
+ const chunk = getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, specialAbility.Constant);
271
+ return chunk === undefined
272
+ ? MISSING_VALUE
273
+ : renderActivatableNameComponents(translateMap, chunk, false);
274
+ }
275
+ case "Selection": {
276
+ const chunks = specialAbility.Selection.options.map(option => getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, option));
277
+ const possiblyCombined = isNotEmpty(specialAbility.Selection.options) && chunks.every(isNotNullish)
278
+ ? combineNameComponents(chunks)
279
+ : undefined;
280
+ if (possiblyCombined === undefined) {
281
+ return localeJoin(chunks.map(chunk => chunk === undefined
282
+ ? MISSING_VALUE
283
+ : renderActivatableNameComponents(translateMap, chunk, false)), "disjunction");
284
+ }
285
+ else {
286
+ return renderCombinedActivatableNameComponents(translateMap, possiblyCombined, false, list => localeJoin(list, "disjunction"));
287
+ }
288
+ }
289
+ default:
290
+ return assertExhaustive(specialAbility);
291
+ }
292
+ });
293
+ const renderSpecialAbilities = (specialAbilities) => Reader.sequence([
294
+ specialAbilities.languagesScripts === undefined
295
+ ? Reader.of(undefined)
296
+ : renderLanguagesScriptsOption(specialAbilities.languagesScripts),
297
+ specialAbilities.curses === undefined
298
+ ? Reader.of(undefined)
299
+ : renderCursesOption(specialAbilities.curses),
300
+ specialAbilities.skillSpecialization === undefined
301
+ ? Reader.of(undefined)
302
+ : renderSkillSpecializationOption(specialAbilities.skillSpecialization),
303
+ specialAbilities.list === undefined
304
+ ? Reader.of(undefined)
305
+ : Reader.traverse(specialAbilities.list, renderSpecialAbilityName)
306
+ .thenW(localeSortR)
307
+ .map(ensureNonEmpty),
308
+ ]).then(rendered => {
309
+ const result = ensureNonEmpty(rendered.filter(isNotNullish).flat())?.join(", ");
310
+ return result === undefined ? translateR("none") : Reader.of(result);
311
+ });
312
+ const renderCombatTechniques = (professionPackages) => Reader.sequence([
313
+ renderNumericListAcrossPackages(professionPackages, pkg => Reader.of(pkg.content.combat_techniques?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, (ctId) => strictNameR(ctId), 6),
314
+ professionPackages.some(pkg => pkg.content.options?.combat_techniques !== undefined)
315
+ ? renderSinglePackageValue(professionPackages, pkg => Reader.of(pkg.content.options?.combat_techniques), deepEqual, options => Reader.traverse(options, option => option === undefined ? Reader.of("—") : renderCombatTechniquesOption(option)).map(renderedOptions => renderedOptions.join(" / ")))
316
+ : Reader.of(undefined),
317
+ ]).map(list => ensureNonEmpty(list.filter(isNotNullish))?.join(", ") ?? "—");
318
+ const renderSkills = (professionPackages) => Reader.asks(({ getAllInstances }) => getAllInstances("SkillGroup")).thenW(skillGroups => Reader.traverse(skillGroups.toSorted(on(group => group.content.position, compareNumber)), ({ id: groupId, content: group }) => translationR(group).thenW(groupTranslation => Reader.sequence([
319
+ renderNumericListAcrossPackages(professionPackages, pkg => Reader.asks(({ getInstanceById }) => pkg.content.skills
320
+ ?.filter(skill => getInstanceById("Skill", skill.id)?.group === groupId)
321
+ .map(skill => [skill.id, skill.rating_modifier]) ?? []), equal, skillId => strictNameR("Skill", skillId), 0),
322
+ renderSkillsOption(professionPackages, {
323
+ id: groupId,
324
+ nameOfSkillsOfGroup: groupTranslation?.longName ?? MISSING_VALUE,
325
+ }),
326
+ ]).map(value => ({
327
+ label: groupTranslation?.name ?? MISSING_VALUE,
328
+ value: ensureNonEmpty(value.filter(isNotNullish))?.join(", ") ?? "—",
329
+ })))));
330
+ const renderSpellworkName = (spellworkIds) => Reader.traverse(spellworkIds, spellworkId => {
331
+ switch (spellworkId.kind) {
332
+ case "Spellwork":
333
+ return strictNameR(spellworkId.Spellwork.id).thenW(baseName => (spellworkId.Spellwork.tradition === undefined
334
+ ? Reader.of(undefined)
335
+ : nameR("MagicalTradition", spellworkId.Spellwork.tradition)).map(traditionName => baseName + parensIf(traditionName)));
336
+ case "MagicalAction":
337
+ return strictNameR(spellworkId.MagicalAction.id);
338
+ default:
339
+ return spellworkId;
340
+ }
341
+ }).thenW(list => localeJoinR(list, "disjunction"));
342
+ const renderSpellworks = (professionPackages) => Reader.sequence([
343
+ renderCantripsOption(professionPackages),
344
+ renderNumericListAcrossPackages(professionPackages, (pkg) => Reader.of(pkg.content.spells?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, renderSpellworkName, 0),
345
+ ]).map(list => ensureNonEmpty(list.filter(isNotNullish))?.join("; "));
346
+ const retrieveBlessedTraditionIdentifierFromPrerequisiteGroup = (prerequisite) => prerequisite.kind === "Activatable" && prerequisite.Activatable.id.kind === "BlessedTradition"
347
+ ? [prerequisite.Activatable.id.BlessedTradition]
348
+ : [];
349
+ const retrieveBlessedTraditionIdentifierFromPrerequisites = (prerequisites) => prerequisites?.flatMap(part => {
350
+ switch (part.kind) {
351
+ case "Single":
352
+ return retrieveBlessedTraditionIdentifierFromPrerequisiteGroup(part.Single);
353
+ case "Disjunction":
354
+ return part.Disjunction.list.flatMap(retrieveBlessedTraditionIdentifierFromPrerequisiteGroup);
355
+ case "Group":
356
+ return part.Group.list.flatMap(retrieveBlessedTraditionIdentifierFromPrerequisiteGroup);
357
+ default:
358
+ return assertExhaustive(part);
359
+ }
360
+ }) ?? [];
361
+ const renderBlessingList = (blessings) => Reader.traverse(blessings, id => strictNameR("Blessing", id)).thenW(localeSortR);
362
+ const renderRestrictedBlessings = (restrictedBlessings) => {
363
+ switch (restrictedBlessings.kind) {
364
+ case "Three":
365
+ return renderBlessingList(restrictedBlessings.Three);
366
+ case "Six":
367
+ return renderBlessingList(restrictedBlessings.Six);
368
+ default:
369
+ return assertExhaustive(restrictedBlessings);
370
+ }
371
+ };
372
+ const renderSingleBlessings = (traditions) => Reader.traverse(traditions, traditionId => getInstanceByIdR("BlessedTradition", traditionId).map(tradition => tradition?.restricted_blessings)).thenW(restrictedBlessingsList => !isNotEmpty(restrictedBlessingsList)
373
+ ? Reader.of(undefined)
374
+ : restrictedBlessingsList.length === 1
375
+ ? (() => {
376
+ const [restrictedBlessings] = restrictedBlessingsList;
377
+ return restrictedBlessings === undefined
378
+ ? translateR("The Twelve Blessings")
379
+ : translateR("The Twelve Blessings").thenW(base => renderRestrictedBlessings(restrictedBlessings).thenW(list => translateR("except for {$list :list type=conjunction}", {
380
+ list,
381
+ }).map(note => base + parensIf(note))));
382
+ })()
383
+ : restrictedBlessingsList.some(isNotNullish)
384
+ ? translateR("The Twelve Blessings").map2(translateR("depends on selected tradition"), (base, note) => base + parensIf(note))
385
+ : translateR("The Twelve Blessings"));
386
+ const renderBlessings = (professionPackages) => {
387
+ const traditions = professionPackages.map(pkg => retrieveBlessedTraditionIdentifierFromPrerequisites(pkg.content.prerequisites));
388
+ if (traditions.every(isEmpty)) {
389
+ return Reader.of(undefined);
390
+ }
391
+ else if (allSame(traditions, deepEqual)) {
392
+ return renderSingleBlessings(traditions[0]);
393
+ }
394
+ else {
395
+ return Reader.traverse(traditions, option => option === undefined ? Reader.of("—") : renderSingleBlessings(option)).map(list => list.join(" / "));
396
+ }
397
+ };
398
+ const renderLiturgicalChantName = (liturgyIds) => Reader.traverse(liturgyIds, strictNameR).thenW(list => localeJoinR(list, "disjunction"));
399
+ const renderLiturgicalChants = (professionPackages) => Reader.sequence([
400
+ renderBlessings(professionPackages),
401
+ renderNumericListAcrossPackages(professionPackages, (pkg) => Reader.of(pkg.content.liturgical_chants?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, renderLiturgicalChantName, 0),
402
+ renderLiturgiesOption(professionPackages),
403
+ ]).map(list => ensureNonEmpty(list.filter(isNotNullish))?.join(", "));
404
+ const renderRatedVariantChanges = (baseList, variantList, renderInstance) => Reader.traverse(variantList ?? [], ({ id, rating_modifier }) => {
405
+ const baseValue = baseList?.find(item => deepEqual(item.id, id))?.rating_modifier ?? 0;
406
+ return renderInstance(id).thenW(name => translateR("{$replacement} instead of {$base}", {
407
+ replacement: `${name} ${baseValue + rating_modifier}`,
408
+ base: baseValue,
409
+ }));
410
+ }).thenW(localeSortR);
411
+ const renderVariantOption = (base, variant, key, render) => (variant.options?.[key] === undefined
412
+ ? Reader.of(undefined)
413
+ : render(base.options?.[key], variant.options[key])).map(text => nullableToArray(text));
414
+ const renderProfessionVariantLabel = (base, variant, translation) => translateR("{$value} AP", {
415
+ value: base.ap_value + (variant.ap_value ?? 0),
416
+ }).map(apValueText => translation.name.default + parensIf(apValueText));
417
+ const renderProfessionVariantText = (base, variant, translation) => translation.full_text !== undefined
418
+ ? Reader.of(translation.full_text)
419
+ : Reader.sequence([
420
+ Reader.asks(env => variant.prerequisites === undefined
421
+ ? []
422
+ : [
423
+ printProfessionPrerequisites(env.getInstanceById, env.getResolvedSelectOptionById, {
424
+ ...env,
425
+ join: env.localeJoin,
426
+ compare: env.localeCompare,
427
+ }, variant.prerequisites),
428
+ ]),
429
+ renderVariantOption(base, variant, "skill_specialization", renderVariantSkillSpecializationOption),
430
+ renderVariantOption(base, variant, "languages_scripts", renderVariantLanguagesScriptsOption),
431
+ renderVariantOption(base, variant, "combat_techniques", renderVariantCombatTechniquesOption),
432
+ renderVariantOption(base, variant, "cantrips", renderVariantCantripsOption),
433
+ renderVariantOption(base, variant, "curses", renderVariantCursesOption),
434
+ renderVariantOption(base, variant, "skills", renderVariantSkillsOption),
435
+ renderVariantOption(base, variant, "liturgies", renderVariantLiturgiesOption),
436
+ variant.special_abilities === undefined
437
+ ? Reader.of([])
438
+ : Reader.traverse(variant.special_abilities, specialAbility => {
439
+ switch (specialAbility.action.kind) {
440
+ case "Remove":
441
+ return translateR("no special ability").thenW(prefix => renderSpecialAbilityName(specialAbility.value).map(name => `${prefix} ${name}`));
442
+ case "Override":
443
+ return renderSpecialAbilityName(specialAbility.value);
444
+ default:
445
+ return assertExhaustive(specialAbility.action);
446
+ }
447
+ }),
448
+ renderRatedVariantChanges(base.combat_techniques, variant.combat_techniques, id => strictNameR(id)),
449
+ renderRatedVariantChanges(base.skills, variant.skills, id => strictNameR("Skill", id)),
450
+ renderRatedVariantChanges(base.spells, variant.spells, renderSpellworkName),
451
+ renderRatedVariantChanges(base.liturgical_chants, variant.liturgical_chants, renderLiturgicalChantName),
452
+ ]).map(lists => lists
453
+ .filter(isNotEmpty)
454
+ .map(list => list.join(", "))
455
+ .join("; ") + (translation.concluding_text ?? ""));
456
+ const renderProfessionVariant = (base, variant) => translationR(variant).thenW(translation => translation === undefined
457
+ ? Reader.of(undefined)
458
+ : renderProfessionVariantLabel(base, variant, translation).thenW(label => renderProfessionVariantText(base, variant, translation).map((value) => ({
459
+ label,
460
+ value,
461
+ }))));
462
+ const renderProfessionVariants = (professionPackages) => professionPackages.length > 1
463
+ ? Reader.of(UNHANDLED_VALUE)
464
+ : getChildInstancesForInstanceIdR("ProfessionVariant", professionPackages[0].id).thenW(variants => Reader.traverse(variants, variant => renderProfessionVariant(professionPackages[0].content, variant.content))
465
+ .map(list => list.filter(isNotNullish))
466
+ .thenW(list => list.length === 0
467
+ ? Reader.of(undefined)
468
+ : localeCompareR.map((localeCompare) => [
469
+ {
470
+ type: "definitionList",
471
+ style: "nested",
472
+ items: list.toSorted(on(item => item.label, localeCompare)),
473
+ },
474
+ ])));
475
+ /**
476
+ * Get a JSON representation of the rules text for a profession version.
477
+ */
478
+ export const getProfessionVersionEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getAllInstances, getChildInstancesForInstanceId, getResolvedSelectOptionById, }, locale, { id, content: entry }) => {
479
+ const { format, translate, translateMap, compare: localeCompare, join: localeJoin } = locale;
480
+ const translation = translateMap(entry.translations);
481
+ const env = {
482
+ format,
483
+ translate,
484
+ translateMap,
485
+ localeCompare,
486
+ localeJoin,
487
+ getInstanceById,
488
+ getAllInstances,
489
+ getChildInstancesForInstanceId,
490
+ getResolvedSelectOptionById,
491
+ };
492
+ const professionPackages = prepareProfessionPackages(id).run(env);
493
+ if (translation === undefined || !isNotEmpty(professionPackages)) {
494
+ return undefined;
495
+ }
496
+ return {
497
+ title: translation.name.default +
498
+ (professionPackages.length > 1
499
+ ? ` (${professionPackages.map(pkg => translateMap(pkg.experienceLevel.translations)?.name).join("/")})`
500
+ : ""),
501
+ className: "profession",
502
+ body: [
503
+ {
504
+ type: "definitionList",
505
+ items: [
506
+ {
507
+ label: translate("AP Value"),
508
+ value: renderSinglePackageValue(professionPackages, pkg => Reader.of(pkg.content.ap_value), equal, apValues => apValues.length === 1
509
+ ? translateR(".input {$value :number} {{{$value} Adventure Points}}", {
510
+ value: apValues[0],
511
+ })
512
+ : translateR("{$value} Adventure Points", {
513
+ value: apValues.join("/"),
514
+ })).run(env),
515
+ },
516
+ {
517
+ label: translate("Prerequisites"),
518
+ value: renderSinglePackageValue(professionPackages, pkg => Reader.of(pkg.content.prerequisites), deepEqual, prerequisitesLists => Reader.of(prerequisitesLists
519
+ .map(prerequisites => prerequisites === undefined
520
+ ? translate("none")
521
+ : printProfessionPrerequisites(getInstanceById, getResolvedSelectOptionById, locale, prerequisites))
522
+ .join(" / "))).run(env),
523
+ },
524
+ {
525
+ label: translate("Special Abilities"),
526
+ value: renderSinglePackageValue(professionPackages, pkg => Reader.of({
527
+ skillSpecialization: pkg.content.options?.skill_specialization,
528
+ languagesScripts: pkg.content.options?.languages_scripts,
529
+ curses: pkg.content.options?.curses,
530
+ list: pkg.content.special_abilities,
531
+ }), deepEqual, specialAbilityLists => Reader.of(specialAbilityLists
532
+ .map(specialAbilities => specialAbilities === undefined
533
+ ? translate("none")
534
+ : renderSpecialAbilities(specialAbilities).run(env))
535
+ .join(" / "))).run(env),
536
+ },
537
+ {
538
+ label: translate("Combat Techniques"),
539
+ value: renderCombatTechniques(professionPackages).run(env),
540
+ },
541
+ {
542
+ label: translate("Skills"),
543
+ value: [
544
+ {
545
+ type: "definitionList",
546
+ style: "nested",
547
+ items: renderSkills(professionPackages).run(env),
548
+ },
549
+ ],
550
+ },
551
+ renderSpellworks(professionPackages)
552
+ .map(value => value === undefined
553
+ ? undefined
554
+ : {
555
+ label: translate("Spellworks"),
556
+ value,
557
+ })
558
+ .run(env),
559
+ renderLiturgicalChants(professionPackages)
560
+ .map(value => value === undefined
561
+ ? undefined
562
+ : {
563
+ label: translate("Liturgical Chants"),
564
+ value,
565
+ })
566
+ .run(env),
567
+ renderValueWithPossibleTranslation("Suggested Advantages", entry.suggested_advantages, values => renderCommonnessRatedAdvantagesOrDisadvantages("Advantage", values).run(env), translation.suggested_advantages).run(env),
568
+ renderValueWithPossibleTranslation("Suggested Disadvantages", entry.suggested_disadvantages, values => renderCommonnessRatedAdvantagesOrDisadvantages("Disadvantage", values).run(env), translation.suggested_disadvantages).run(env),
569
+ renderValueWithPossibleTranslation("Unsuitable Advantages", entry.unsuitable_advantages, values => renderCommonnessRatedAdvantagesOrDisadvantages("Advantage", values).run(env), translation.unsuitable_advantages).run(env),
570
+ renderValueWithPossibleTranslation("Unsuitable Disadvantages", entry.unsuitable_disadvantages, values => renderCommonnessRatedAdvantagesOrDisadvantages("Disadvantage", values).run(env), translation.unsuitable_disadvantages).run(env),
571
+ renderProfessionVariants(professionPackages)
572
+ .map(value => value === undefined
573
+ ? undefined
574
+ : {
575
+ label: translate("Variants"),
576
+ value,
577
+ })
578
+ .run(env),
579
+ ],
580
+ },
581
+ ],
582
+ errata: translation.errata,
583
+ references: entry.src,
584
+ };
585
+ });
@@ -0,0 +1,9 @@
1
+ import type { CountInstances, GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js";
2
+ /**
3
+ * Get a JSON representation of the rules text for a race.
4
+ */
5
+ export declare const getRaceEntityDescription: import("../creator.js").EntityDescriptionCreator<"Race", {
6
+ getInstanceById: GetInstanceById<"Publication" | "Attribute" | "Advantage" | "Disadvantage" | "Culture">;
7
+ countInstances: CountInstances<"Attribute">;
8
+ getChildInstancesForInstanceId: GetAllChildInstancesForParent<"RaceVariant">;
9
+ }, import("../index.js").EntityDescription>;