@optolith/entity-descriptions 0.2.0 → 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 +65 -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,356 @@
1
+ import { allSame } from "@elyukai/utils/array/filters";
2
+ import { ensureNonEmpty, isNotEmpty } from "@elyukai/utils/array/nonEmpty";
3
+ import { deepEqual } from "@elyukai/utils/equality";
4
+ import { identity, on } from "@elyukai/utils/function";
5
+ import { isNotNullish } from "@elyukai/utils/nullable";
6
+ import { mapObject } from "@optolith/helpers/object";
7
+ import { romanize } from "@optolith/helpers/roman";
8
+ import { assertExhaustive } from "@optolith/helpers/typeSafety";
9
+ import { MISSING_VALUE } from "./unknown.js";
10
+ const combineChunks = (a, b, join) => {
11
+ if (typeof a === "string") {
12
+ if (typeof b === "string") {
13
+ return join(a, b);
14
+ }
15
+ else if (typeof b === "function") {
16
+ return fs => join(a, b(fs));
17
+ }
18
+ else if (typeof b === "object") {
19
+ return mapObject(b, bValue => join(a, bValue));
20
+ }
21
+ return b;
22
+ }
23
+ else if (typeof a === "function") {
24
+ if (typeof b === "string") {
25
+ return fs => join(a(fs), b);
26
+ }
27
+ else if (typeof b === "function") {
28
+ return fs => join(a(fs), b(fs));
29
+ }
30
+ else if (typeof b === "object") {
31
+ return translateMap => join(a(translateMap), translateMap(b) ?? MISSING_VALUE);
32
+ }
33
+ return b;
34
+ }
35
+ else if (typeof a === "object") {
36
+ if (typeof b === "string") {
37
+ return mapObject(a, aValue => join(aValue, b));
38
+ }
39
+ else if (typeof b === "function") {
40
+ return translateMap => join(translateMap(a) ?? MISSING_VALUE, b(translateMap));
41
+ }
42
+ else if (typeof b === "object") {
43
+ const ret = {};
44
+ for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {
45
+ ret[key] = join(a[key] ?? MISSING_VALUE, b[key] ?? MISSING_VALUE);
46
+ }
47
+ return ret;
48
+ }
49
+ return b;
50
+ }
51
+ return a;
52
+ };
53
+ const combinePair = (pair) => combineChunks(pair[0], pair[1], (a, b) => `${a}: ${b}`);
54
+ const normalizeChunks = (chunk) => (Array.isArray(chunk) ? combinePair(chunk) : chunk);
55
+ /**
56
+ * Zips multiple name chunks together, separating them with commas. Pairs of chunks are combined with a colon.
57
+ */
58
+ const zipChunks = (chunks) => chunks
59
+ .map(normalizeChunks)
60
+ .reduce((acc, chunk) => acc === "" ? chunk : combineChunks(acc, chunk, (a, b) => `${a}, ${b}`), "");
61
+ const renderLevel = (formatAsPrerequisite, id, level) => level === undefined
62
+ ? undefined
63
+ : formatAsPrerequisite ||
64
+ id.kind === "Advantage" ||
65
+ id.kind === "Disadvantage" ||
66
+ level === 1
67
+ ? romanize(level)
68
+ : `I–${romanize(level)}`;
69
+ /**
70
+ * Converts a name chunk to a displayable string.
71
+ */
72
+ const renderActivatableNameChunk = (translateMap, chunk) => {
73
+ if (typeof chunk === "string") {
74
+ return chunk;
75
+ }
76
+ else if (typeof chunk === "function") {
77
+ return chunk(translateMap);
78
+ }
79
+ else if (typeof chunk === "object") {
80
+ return translateMap(chunk) ?? MISSING_VALUE;
81
+ }
82
+ return chunk;
83
+ };
84
+ /**
85
+ * This can be used to render an activatable entry with a selectable level, as it splits the text where the level should be inserted.
86
+ */
87
+ export const renderActivatableNameComponentsWithoutLevel = (translateMap, components) => {
88
+ const { levelPlacement, useParenthesis } = components.nameBuilderRules;
89
+ const wrapParens = useParenthesis
90
+ ? str => `(${str})`
91
+ : identity;
92
+ const base = renderActivatableNameChunk(translateMap, components.base);
93
+ const options = components.options === undefined
94
+ ? undefined
95
+ : wrapParens(renderActivatableNameChunk(translateMap, normalizeChunks(components.options)));
96
+ switch (levelPlacement.kind) {
97
+ case "BeforeOptions":
98
+ return [base, options];
99
+ case "AfterOptions":
100
+ return [[base, options].filter(isNotNullish).join(" ")];
101
+ default:
102
+ return assertExhaustive(levelPlacement);
103
+ }
104
+ };
105
+ /**
106
+ * Renders the name components of an activatable entry.
107
+ */
108
+ export const renderActivatableNameComponents = (translateMap, components, formatAsPrerequisite) => {
109
+ const levelText = renderLevel(formatAsPrerequisite, components.id, components.level);
110
+ const [beforeLevel, afterLevel] = renderActivatableNameComponentsWithoutLevel(translateMap, components);
111
+ return [beforeLevel, levelText, afterLevel].filter(isNotNullish).join(" ");
112
+ };
113
+ /**
114
+ * Combines multiple name components into a single one when all values except for options are the same, joining options according to the passed function.
115
+ *
116
+ * Returns `undefined` if the components cannot be combined.
117
+ */
118
+ export const combineNameComponents = (elements) => {
119
+ if (isNotEmpty(elements) &&
120
+ allSame(elements, on(item => [item.id, item.level], deepEqual))) {
121
+ return {
122
+ ...elements[0],
123
+ options: ensureNonEmpty(elements.map(e => e.options).filter(isNotNullish)) ?? [],
124
+ };
125
+ }
126
+ return undefined;
127
+ };
128
+ /**
129
+ * Renders the name components of an activatable entry, combining multiple options into one string.
130
+ */
131
+ export const renderCombinedActivatableNameComponents = (translateMap, components, formatAsPrerequisite, join = list => list.join(", ")) => renderActivatableNameComponents(translateMap, {
132
+ ...components,
133
+ options: join(components.options.map(chunk => renderActivatableNameChunk(translateMap, normalizeChunks(chunk)))),
134
+ }, formatAsPrerequisite);
135
+ /**
136
+ * Renders the name components of multiple activatable entries.
137
+ */
138
+ export const renderMultipleStandaloneActivatableNameComponents = (translateMap, components, formatAsPrerequisite, join = list => list.join(", ")) => join(components.map(item => renderActivatableNameComponents(translateMap, item, formatAsPrerequisite)));
139
+ /**
140
+ * Renders the name components of multiple activatable entries, combining parts if possible.
141
+ */
142
+ export const renderActivatableNameComponentsCombinedIfPossible = (translateMap, components, formatAsPrerequisite, join = list => list.join(", ")) => {
143
+ const combined = combineNameComponents(components);
144
+ if (combined === undefined) {
145
+ return renderMultipleStandaloneActivatableNameComponents(translateMap, components, formatAsPrerequisite, join);
146
+ }
147
+ else {
148
+ return renderCombinedActivatableNameComponents(translateMap, combined, formatAsPrerequisite, join);
149
+ }
150
+ };
151
+ // const getEntrySpecificFullName = (
152
+ // getInstanceById: GetInstanceById<"Aspect">,
153
+ // locale: LocaleEnvironment,
154
+ // id: ActivatableIdentifier,
155
+ // base: ActivatableNameChunk,
156
+ // level: number | undefined,
157
+ // printedOptions: ActivatableNameComponents["options"],
158
+ // ):
159
+ // | Pick<ActivatableNameComponents, "full" | "fullWithoutLevel" | "level">
160
+ // | undefined => {
161
+ // switch (id.kind) {
162
+ // case "Advantage":
163
+ // switch (id.Advantage) {
164
+ // case AdvantageIdentifier.HatredOf: {
165
+ // const [firstOption, ...rest] = printedOptions
166
+ // if (firstOption === undefined || Array.isArray(firstOption)) {
167
+ // return undefined
168
+ // }
169
+ // return combineBaseName(
170
+ // combineChunks(base, firstOption, (a, b) => `${a} ${b}`),
171
+ // level,
172
+ // rest,
173
+ // )
174
+ // }
175
+ // default:
176
+ // return undefined
177
+ // }
178
+ // case "Disadvantage":
179
+ // switch (id.Disadvantage) {
180
+ // case DisadvantageIdentifier.PersonalityFlaw: {
181
+ // const [selection, optionalText, ...rest] = printedOptions
182
+ // if (
183
+ // selection === undefined ||
184
+ // Array.isArray(selection) ||
185
+ // Array.isArray(optionalText)
186
+ // ) {
187
+ // return undefined
188
+ // }
189
+ // return combineBaseName(base, level, [
190
+ // optionalText === undefined ? selection : [selection, optionalText],
191
+ // ...rest,
192
+ // ])
193
+ // }
194
+ // default:
195
+ // return undefined
196
+ // }
197
+ // case "AdvancedCombatSpecialAbility":
198
+ // case "AdvancedKarmaSpecialAbility":
199
+ // case "AdvancedMagicalSpecialAbility":
200
+ // case "AdvancedSkillSpecialAbility":
201
+ // switch (id.AdvancedSkillSpecialAbility) {
202
+ // case AdvancedSkillSpecialAbilityIdentifier.Fachwissen: {
203
+ // const [skillId, firstApplicationId, secondApplicationId] = options ?? []
204
+ // const aspect =
205
+ // aspectId?.tag === "Aspect"
206
+ // ? getAspectById(aspectId.aspect)
207
+ // : undefined
208
+ // const aspectTranslations = locale.translateMap(aspect?.translations)
209
+ // if (aspectTranslations === undefined) {
210
+ // return undefined
211
+ // }
212
+ // return combineBaseName(
213
+ // combineChunks(
214
+ // base,
215
+ // aspectTranslations.master_of_aspect_suffix ??
216
+ // aspectTranslations.name,
217
+ // (a, b) => `${a} ${b}`,
218
+ // ),
219
+ // level,
220
+ // [],
221
+ // )
222
+ // const getApp = (
223
+ // getSid: (r: Record<ActiveObjectWithId>) => Maybe<string | number>,
224
+ // ) =>
225
+ // pipe(
226
+ // SA.applications,
227
+ // filter(pipe(AA.prerequisite, isNothing)),
228
+ // find(pipe(AA.id, elemF(getSid(hero_entry)))),
229
+ // fmap(AA.name),
230
+ // )
231
+ // return pipe_(
232
+ // hero_entry,
233
+ // AOWIA.sid,
234
+ // misStringM,
235
+ // bindF(lookupF(SDA.skills(staticData))),
236
+ // bindF(skill =>
237
+ // pipe_(
238
+ // List(getApp(AOWIA.sid2)(skill), getApp(AOWIA.sid3)(skill)),
239
+ // catMaybes,
240
+ // ensure(xs => flength(xs) === 2),
241
+ // fmap(
242
+ // pipe(
243
+ // sortStrings(staticData),
244
+ // formatList("conjunction")(staticData),
245
+ // apps => `${SA.name(skill)}: ${apps}`,
246
+ // ),
247
+ // ),
248
+ // ),
249
+ // ),
250
+ // )
251
+ // }
252
+ // default:
253
+ // return undefined
254
+ // }
255
+ // case "AncestorGlyph":
256
+ // case "ArcaneOrbEnchantment":
257
+ // case "AttireEnchantment":
258
+ // case "Beutelzauber":
259
+ // case "BlessedTradition":
260
+ // case "BowlEnchantment":
261
+ // case "BrawlingSpecialAbility":
262
+ // case "CauldronEnchantment":
263
+ // case "CeremonialItemSpecialAbility":
264
+ // case "ChronicleEnchantment":
265
+ // case "CombatSpecialAbility":
266
+ // case "CombatStyleSpecialAbility":
267
+ // case "CommandSpecialAbility":
268
+ // case "DaggerRitual":
269
+ // case "FamiliarSpecialAbility":
270
+ // case "FatePointSexSpecialAbility":
271
+ // case "FatePointSpecialAbility":
272
+ // case "FoolsHatEnchantment":
273
+ // return undefined
274
+ // case "GeneralSpecialAbility":
275
+ // switch (id.GeneralSpecialAbility) {
276
+ // case GeneralSpecialAbilityIdentifier.LanguageSpecialization: {
277
+ // const [languageId, specializationId] = options ?? []
278
+ // const language =
279
+ // languageId?.tag === "Language"
280
+ // ? getLanguageById(languageId.language)
281
+ // : undefined
282
+ // const specializations =
283
+ // language?.specializations?.tag === "Specific"
284
+ // ? language.specializations.specific.list
285
+ // : []
286
+ // const specialization =
287
+ // specializationId?.tag === "General"
288
+ // ? specializations.find(
289
+ // spec => spec.id === specializationId.general,
290
+ // )
291
+ // : undefined
292
+ // if (aspectTranslations === undefined) {
293
+ // return undefined
294
+ // }
295
+ // return pipe(
296
+ // SDA.specialAbilities,
297
+ // lookup<string>(SpecialAbilityId.Language),
298
+ // bindF(pipe(findSelectOption, thrush(AOWIA.sid(hero_entry)))),
299
+ // bindF(lang =>
300
+ // pipe(
301
+ // AOWIA.sid2,
302
+ // bindF(
303
+ // ifElse<string | number, string>(isString)<Maybe<string>>(
304
+ // Just,
305
+ // )(spec_id =>
306
+ // bind(SOA.specializations(lang))(subscriptF(spec_id - 1)),
307
+ // ),
308
+ // ),
309
+ // fmap(spec => `${SOA.name(lang)}: ${spec}`),
310
+ // )(hero_entry),
311
+ // ),
312
+ // )(staticData)
313
+ // }
314
+ const renderOptions = (displayedInProfession, getResolvedSelectOptionById, id, options) => {
315
+ const arr = options?.map(optionId => {
316
+ const optTranslations = getResolvedSelectOptionById(id, optionId)?.content
317
+ .translations;
318
+ return optTranslations === undefined
319
+ ? MISSING_VALUE
320
+ : mapObject(optTranslations, t10n => displayedInProfession
321
+ ? (t10n.name_in_profession ?? t10n.name)
322
+ : t10n.name);
323
+ }) ?? [];
324
+ if (isNotEmpty(arr) && arr.length > 1) {
325
+ const [first, ...rest] = arr;
326
+ return [first, zipChunks(rest)];
327
+ }
328
+ return arr[0];
329
+ };
330
+ /**
331
+ * Gets the name components for an activatable entry.
332
+ */
333
+ export const getNameComponents = (translate, id, options, level, nameBuilderRules, translations, getBaseName, getResolvedSelectOptionById, displayedInProfession) => {
334
+ const nameBuilderRulesWithDefaults = {
335
+ levelPlacement: nameBuilderRules?.levelPlacement ?? {
336
+ kind: "AfterOptions",
337
+ },
338
+ useParenthesis: nameBuilderRules?.useParenthesis ?? true,
339
+ };
340
+ const renderedBase = mapObject(translations, getBaseName);
341
+ const renderedOptions = renderOptions(displayedInProfession, getResolvedSelectOptionById, id, options);
342
+ const isTradition = id.kind === "MagicalTradition" || id.kind === "BlessedTradition";
343
+ const actualBase = isTradition ? translate("Tradition") : renderedBase;
344
+ const actualOptions = isTradition
345
+ ? renderedOptions === undefined
346
+ ? renderedBase
347
+ : [renderedBase, normalizeChunks(renderedOptions)]
348
+ : renderedOptions;
349
+ return {
350
+ id,
351
+ base: actualBase,
352
+ options: actualOptions,
353
+ level,
354
+ nameBuilderRules: nameBuilderRulesWithDefaults,
355
+ };
356
+ };
@@ -0,0 +1,9 @@
1
+ import type { ResolvedSelectOption, ResolvedSelectOptionIdentifier } from "optolith-database-schema/cache";
2
+ import type { ActivatableIdentifier, AdventurePointsValue } from "optolith-database-schema/gen";
3
+ import type { GetAllInstances } from "../../helpers/getTypes.js";
4
+ import type { LocaleEnvironment } from "../../helpers/locale.js";
5
+ import type { BaseActivatable, BaseActivatableTranslation } from "../activatable.js";
6
+ /**
7
+ * Renders an adventure points value to a human-readable string, using the provided locale for translations and formatting. For values that depend on select options or other instances, the necessary lookup functions are provided as arguments.
8
+ */
9
+ export declare const renderAdventurePointsValue: (locale: LocaleEnvironment, getNameForId: (id: ActivatableIdentifier) => string | undefined, getNameForSelectOptionId: (id: ResolvedSelectOptionIdentifier) => string | undefined, getAllSelectOptions: () => ResolvedSelectOption[], getAllInstances: GetAllInstances<"Script" | "AnimalShapeSize">, value: AdventurePointsValue | number, entry: BaseActivatable, translation: BaseActivatableTranslation) => string;
@@ -0,0 +1,243 @@
1
+ import { on } from "@elyukai/utils/function";
2
+ import { unique } from "@optolith/helpers/array";
3
+ import { deepEqual, numAsc } from "@optolith/helpers/compare";
4
+ import { romanize } from "@optolith/helpers/roman";
5
+ import { assertExhaustive } from "@optolith/helpers/typeSafety";
6
+ import { evaluateMathOperation } from "./mathOperation.js";
7
+ import { MISSING_VALUE } from "./unknown.js";
8
+ const renderSelectOptionsAdventurePointsValue = (locale, derivedLabel, getAllSelectOptions, getNameForSelectOptionId, config) => {
9
+ if (config === undefined) {
10
+ return MISSING_VALUE;
11
+ }
12
+ switch (config.kind) {
13
+ case "DerivedFromImprovementCost": {
14
+ const start = derivedLabel();
15
+ return `${start}: ${locale.translate("{$value} Adventure Points", {
16
+ value: Array.from({ length: 4 }, (_, index) => (index + 1) * (config.DerivedFromImprovementCost.multiplier ?? 1) +
17
+ (config.DerivedFromImprovementCost.offset ?? 0)).join("/"),
18
+ })}`;
19
+ }
20
+ case "Fixed":
21
+ return Map.groupBy(getAllSelectOptions().map((item) => [
22
+ config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))
23
+ ?.ap_value ?? config.Fixed.default,
24
+ item,
25
+ ]), item => item[0])
26
+ .entries()
27
+ .map(([apValue, items]) => [
28
+ apValue,
29
+ locale.join(items
30
+ .map(item => getNameForSelectOptionId(item[1].id))
31
+ .filter(name => name !== undefined)
32
+ .toSorted(locale.compare), "conjunction"),
33
+ ])
34
+ .toArray()
35
+ .toSorted(on(item => item[1], locale.compare))
36
+ .map(([apValue, itemNames]) => `${itemNames}: ${locale.translate("{$value} Adventure Points", { value: apValue })}`)
37
+ .join("; ");
38
+ default:
39
+ return assertExhaustive(config);
40
+ }
41
+ };
42
+ /**
43
+ * Renders an adventure points value to a human-readable string, using the provided locale for translations and formatting. For values that depend on select options or other instances, the necessary lookup functions are provided as arguments.
44
+ */
45
+ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelectOptionId, getAllSelectOptions, getAllInstances, value, entry, translation) => {
46
+ const { translate, translateMap } = locale;
47
+ if (typeof value === "number") {
48
+ return translate(".input {$value :number} {{{$value} Adventure Points}}", {
49
+ value,
50
+ });
51
+ }
52
+ switch (value.kind) {
53
+ case "Fixed": {
54
+ if (entry.levels === undefined) {
55
+ return translate(".input {$value :number} {{{$value} Adventure Points}}", { value: value.Fixed });
56
+ }
57
+ else {
58
+ return translate(".input {$value :number} {{{$value} Adventure Points per level}}", { value: value.Fixed });
59
+ }
60
+ }
61
+ case "ByLevel": {
62
+ if (entry.levels !== value.ByLevel.list.length) {
63
+ return MISSING_VALUE;
64
+ }
65
+ const mainValue = `${translate("Level {$level}", {
66
+ level: Array.from({ length: entry.levels }, (_, index) => romanize(index + 1)).join("/"),
67
+ })}: ${value.ByLevel.list.join("/")}`;
68
+ const { additionalBySizeCategory } = value.ByLevel;
69
+ if (additionalBySizeCategory === undefined) {
70
+ return mainValue;
71
+ }
72
+ else {
73
+ const sizeCategories = [
74
+ "tiny",
75
+ "small",
76
+ "medium",
77
+ "large",
78
+ "huge",
79
+ ];
80
+ const values = sizeCategories
81
+ .map(sizeCategory => additionalBySizeCategory[sizeCategory])
82
+ .join("/");
83
+ const labels = sizeCategories
84
+ .map(sizeCategory => translate(sizeCategory))
85
+ .join("/");
86
+ return `${mainValue} + ${translate("{$values} AP for size category {$labels} (per level)", { values, labels })}`;
87
+ }
88
+ }
89
+ case "DerivedFromSelection": {
90
+ const derivedSelectOptions = entry.select_options?.derived;
91
+ if (derivedSelectOptions === undefined) {
92
+ return MISSING_VALUE;
93
+ }
94
+ switch (derivedSelectOptions.kind) {
95
+ case "Blessings":
96
+ return MISSING_VALUE;
97
+ case "Cantrips":
98
+ return MISSING_VALUE;
99
+ case "TradeSecrets":
100
+ return translate("depending on the trade secret");
101
+ case "Scripts":
102
+ return translate("{$value} Adventure Points", {
103
+ value: unique(getAllInstances("Script")
104
+ .map(script => script.content.ap_value)
105
+ .filter(apValue => apValue !== undefined))
106
+ .toSorted(numAsc)
107
+ .join("/"),
108
+ });
109
+ case "AnimalShapes": {
110
+ const sizes = getAllInstances("AnimalShapeSize").toSorted(on(x => x.content.ap_value, numAsc));
111
+ return translate("{$values} adventure points for a {$sized} animal shape", {
112
+ values: sizes.map(size => size.content.ap_value).join("/"),
113
+ sized: sizes
114
+ .map(size => translateMap(size.content.translations)?.name ??
115
+ MISSING_VALUE)
116
+ .join("/"),
117
+ });
118
+ }
119
+ case "ArcaneBardTraditions":
120
+ return MISSING_VALUE;
121
+ case "ArcaneDancerTraditions":
122
+ return MISSING_VALUE;
123
+ case "SexPractices":
124
+ return MISSING_VALUE;
125
+ case "Races":
126
+ return MISSING_VALUE;
127
+ case "Cultures":
128
+ return MISSING_VALUE;
129
+ case "RacesAndCultures":
130
+ return MISSING_VALUE;
131
+ case "HomunculusTypes":
132
+ return MISSING_VALUE;
133
+ case "BlessedTraditions":
134
+ return MISSING_VALUE;
135
+ case "Elements":
136
+ return MISSING_VALUE;
137
+ case "Properties":
138
+ return MISSING_VALUE;
139
+ case "Aspects":
140
+ return MISSING_VALUE;
141
+ case "Diseases":
142
+ if (derivedSelectOptions.Diseases.use_half_level_as_ap_value === true) {
143
+ return translate("Half the chosen disease’s level in adventure points");
144
+ }
145
+ else {
146
+ return translate("The chosen disease’s level in adventure points");
147
+ }
148
+ case "Poisons":
149
+ if (derivedSelectOptions.Poisons.use_half_level_as_ap_value === true) {
150
+ return translate("Half the chosen poison’s level in adventure points");
151
+ }
152
+ else {
153
+ return translate("The chosen poison’s level in adventure points");
154
+ }
155
+ case "Languages":
156
+ return MISSING_VALUE;
157
+ case "Skills":
158
+ return renderSelectOptionsAdventurePointsValue(locale, () => {
159
+ switch (derivedSelectOptions.Skills.categories.length) {
160
+ case 1: {
161
+ const first = derivedSelectOptions.Skills.categories[0];
162
+ switch (first.kind) {
163
+ case "Skills":
164
+ return translate("A/B/C/D skill");
165
+ case "Spells":
166
+ return translate("A/B/C/D spell");
167
+ case "Rituals":
168
+ return translate("A/B/C/D ritual");
169
+ case "LiturgicalChants":
170
+ return translate("A/B/C/D liturgical chant");
171
+ case "Ceremonies":
172
+ return translate("A/B/C/D ceremony");
173
+ default:
174
+ return assertExhaustive(first);
175
+ }
176
+ }
177
+ case 2:
178
+ return derivedSelectOptions.Skills.categories.every(category => category.kind === "Spells" || category.kind === "Rituals")
179
+ ? translate("A/B/C/D spellwork")
180
+ : derivedSelectOptions.Skills.categories.every(category => category.kind === "LiturgicalChants" ||
181
+ category.kind === "Ceremonies")
182
+ ? translate("A/B/C/D liturgical chant or ceremony")
183
+ : translate("A/B/C/D ability");
184
+ default:
185
+ return translate("A/B/C/D ability");
186
+ }
187
+ }, getAllSelectOptions, getNameForSelectOptionId, derivedSelectOptions.Skills.ap_value);
188
+ case "CombatTechniques":
189
+ return renderSelectOptionsAdventurePointsValue(locale, () => translate("B/C/D combat technique"), getAllSelectOptions, getNameForSelectOptionId, derivedSelectOptions.CombatTechniques.ap_value);
190
+ case "TargetCategories":
191
+ return MISSING_VALUE;
192
+ default:
193
+ return assertExhaustive(derivedSelectOptions);
194
+ }
195
+ }
196
+ case "DependingOnActive":
197
+ return `${translate(".input {$value :number} {{{$value} Adventure Points}}", {
198
+ value: value.DependingOnActive.inactive,
199
+ })} (${translate(".input {$value :number} {{{$value} Adventure Points with {$name}}}", {
200
+ value: value.DependingOnActive.active,
201
+ name: getNameForId(value.DependingOnActive.id) ?? MISSING_VALUE,
202
+ })})`;
203
+ case "DependingOnActiveInstances":
204
+ switch (value.DependingOnActiveInstances.kind) {
205
+ case "Threshold":
206
+ return translate(".input {$value :number} {{{$value} Adventure Points}}", {
207
+ value: value.DependingOnActiveInstances.Threshold.normal,
208
+ });
209
+ case "Expression": {
210
+ const expression = value.DependingOnActiveInstances.Expression;
211
+ const values = Array.from({ length: entry.maximum ?? 3 }, (_, index) => evaluateMathOperation(expression, exprValue => {
212
+ switch (exprValue.kind) {
213
+ case "Constant":
214
+ return exprValue.Constant;
215
+ case "Active":
216
+ return index;
217
+ default:
218
+ return assertExhaustive(exprValue);
219
+ }
220
+ })).join("/");
221
+ const labels = Array.from({ length: entry.maximum ?? 3 }, (_, index) => `${index + 1}.`).join("/");
222
+ if (entry.maximum !== undefined) {
223
+ return translate("{$values} Adventure Points for the {$labels} purchase", {
224
+ values,
225
+ labels,
226
+ });
227
+ }
228
+ else {
229
+ return translate("{$values}/and so on Adventure Points for the {$labels}/and so on purchase", {
230
+ values,
231
+ labels,
232
+ });
233
+ }
234
+ }
235
+ default:
236
+ return assertExhaustive(value.DependingOnActiveInstances);
237
+ }
238
+ case "Indefinite":
239
+ return translation.ap_value ?? MISSING_VALUE;
240
+ default:
241
+ return assertExhaustive(value);
242
+ }
243
+ };
@@ -0,0 +1,11 @@
1
+ import type { AnimalType_ID } from "optolith-database-schema/gen";
2
+ import type { GetInstanceById } from "../../helpers/getTypes.js";
3
+ import type { LocaleCompare } from "../../helpers/locale.js";
4
+ import type { Translate, TranslateMap } from "../../helpers/translate.js";
5
+ /**
6
+ * Renders animal types an entry applies to.
7
+ */
8
+ export declare const renderAnimalTypesSection: (translate: Translate, translateMap: TranslateMap, localeCompare: LocaleCompare, getInstanceById: GetInstanceById<"AnimalType">, animalTypes: AnimalType_ID[]) => {
9
+ label: string;
10
+ value: string;
11
+ };
@@ -0,0 +1,13 @@
1
+ import { MISSING_VALUE } from "./unknown.js";
2
+ /**
3
+ * Renders animal types an entry applies to.
4
+ */
5
+ export const renderAnimalTypesSection = (translate, translateMap, localeCompare, getInstanceById, animalTypes) => ({
6
+ label: translate("Animal Types"),
7
+ value: animalTypes.length === 0
8
+ ? translate("All")
9
+ : animalTypes
10
+ .map(animalTypeId => translateMap(getInstanceById("AnimalType", animalTypeId)?.translations)?.name ?? MISSING_VALUE)
11
+ .toSorted(localeCompare)
12
+ .join(", "),
13
+ });
@@ -0,0 +1,16 @@
1
+ import type { CommonnessRatedAdvantageDisadvantage } from "optolith-database-schema/gen";
2
+ import type { TranslationKeysWithoutParams } from "../../helpers/translate.js";
3
+ import type { RawDefinitionListEntityDescriptionSectionItem } from "../../index.js";
4
+ import type { StdReader } from "./reader.js";
5
+ /**
6
+ * Render the names of either commonness-rated advantages or commonness-rated disadvantages.
7
+ */
8
+ export declare const renderCommonnessRatedAdvantagesOrDisadvantages: <E extends "Advantage" | "Disadvantage">(entity: E, items: CommonnessRatedAdvantageDisadvantage<string>[] | undefined) => StdReader<string, "t" | "tm" | "lc" | "ibi", E>;
9
+ /**
10
+ * Render the names of commonness-rated advantages and disadvantages together.
11
+ */
12
+ export declare const renderCommonnessRatedAdvantagesAndDisadvantages: (advantages: CommonnessRatedAdvantageDisadvantage<string>[] | undefined, disadvantages: CommonnessRatedAdvantageDisadvantage<string>[] | undefined) => StdReader<string, "t" | "tm" | "lc" | "ibi", "Advantage" | "Disadvantage">;
13
+ /**
14
+ * Render a value with a possible translation, falling back to explicity rendering the value if no translation is available.
15
+ */
16
+ export declare const renderValueWithPossibleTranslation: <T>(label: TranslationKeysWithoutParams, value: T, renderValue: (value: T) => string, valueTranslation: string | undefined) => StdReader<RawDefinitionListEntityDescriptionSectionItem, "t">;