@optolith/entity-descriptions 0.4.8 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/lib/entities/activatable.js +17 -14
- package/lib/entities/curriculum.js +14 -21
- package/lib/entities/derivedCharacteristic.d.ts +0 -2
- package/lib/entities/derivedCharacteristic.js +10 -17
- package/lib/entities/disease.js +5 -1
- package/lib/entities/elixir.js +12 -10
- package/lib/entities/equipment.d.ts +6 -5
- package/lib/entities/equipment.js +19 -6
- package/lib/entities/equipmentPackage.js +2 -1
- package/lib/entities/liturgicalChant.js +7 -3
- package/lib/entities/partial/commonnessRatedAdvantagesAndDisadvantages.js +8 -12
- package/lib/entities/partial/herbary.js +5 -8
- package/lib/entities/partial/markdown.d.ts +55 -0
- package/lib/entities/partial/markdown.js +59 -0
- package/lib/entities/partial/prerequisites/index.d.ts +3 -3
- package/lib/entities/partial/prerequisites/index.js +3 -3
- package/lib/entities/partial/prerequisites/part.js +8 -3
- package/lib/entities/partial/prerequisites/prerequisiteGroups.d.ts +1 -1
- package/lib/entities/partial/prerequisites/prerequisiteGroups.js +10 -7
- package/lib/entities/partial/prerequisites/single/culture.js +2 -1
- package/lib/entities/partial/prerequisites/single/personalityTrait.js +2 -1
- package/lib/entities/partial/prerequisites/single/profession.js +4 -1
- package/lib/entities/partial/prerequisites/single/race.js +2 -1
- package/lib/entities/partial/prerequisites/single/rated.js +2 -27
- package/lib/entities/partial/prerequisites/single/ratedMinimumNumber.js +2 -1
- package/lib/entities/partial/prerequisites/single/ratedSum.js +2 -1
- package/lib/entities/partial/prerequisites/single/state.js +2 -1
- package/lib/entities/partial/rated/activatable/checkResultBased.js +3 -1
- package/lib/entities/partial/reader.d.ts +33 -0
- package/lib/entities/partial/reader.js +13 -0
- package/lib/entities/personalityTrait.js +9 -2
- package/lib/entities/poison.js +20 -9
- package/lib/entities/profession.d.ts +2 -2
- package/lib/entities/profession.js +133 -38
- package/lib/entities/race.d.ts +1 -1
- package/lib/entities/race.js +23 -23
- package/lib/entities/spell.d.ts +4 -2
- package/lib/entities/spell.js +17 -29
- package/lib/index.d.ts +64 -64
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
|
@@ -7,12 +7,14 @@ import { omitKeys } from "@elyukai/utils/object";
|
|
|
7
7
|
import { compareNumber } from "@elyukai/utils/ordering";
|
|
8
8
|
import { Reader } from "@elyukai/utils/reader";
|
|
9
9
|
import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
10
|
+
import { Case, fromUniformCase } from "tsondb/schema/gen";
|
|
10
11
|
import { createEntityDescriptionCreator } from "../creator.js";
|
|
11
12
|
import { combineNameComponents, getNameComponents, renderActivatableNameComponents, renderCombinedActivatableNameComponents, } from "./partial/activatableNameChunks.js";
|
|
12
13
|
import { renderCommonnessRatedAdvantagesOrDisadvantages, renderValueWithPossibleTranslation, } from "./partial/commonnessRatedAdvantagesAndDisadvantages.js";
|
|
14
|
+
import { attributedCustomName, attributedInstance, attributedName } from "./partial/markdown.js";
|
|
13
15
|
import { printProfessionPrerequisites } from "./partial/prerequisites/index.js";
|
|
14
16
|
import { parensIf } from "./partial/rated/activatable/parensIf.js";
|
|
15
|
-
import { formatR, getChildInstancesForInstanceIdR, getInstanceByIdR, localeCompareR, localeJoinR, localeSortR,
|
|
17
|
+
import { attributedCustomNameR, attributedNameR, customNameR, formatR, getChildInstancesForInstanceIdR, getInstanceByIdR, localeCompareR, localeJoinR, localeSortR, strictNameR, translateMapFnR, translateR, translationR, } from "./partial/reader.js";
|
|
16
18
|
import { MISSING_VALUE, UNHANDLED_VALUE } from "./partial/unknown.js";
|
|
17
19
|
const prepareProfessionPackages = (professionId) => Reader.asks(({ getInstanceById, getChildInstancesForInstanceId }) => getChildInstancesForInstanceId("ProfessionPackage", professionId)
|
|
18
20
|
.map((professionPackage) => ({
|
|
@@ -79,12 +81,12 @@ const renderSkillSpecializationOption = (option) => Reader.asks(({ format, trans
|
|
|
79
81
|
switch (option.kind) {
|
|
80
82
|
case "Specific":
|
|
81
83
|
return translate("Skill Specialization {$possibleSkills}", {
|
|
82
|
-
possibleSkills: localeJoin(option.Specific.options.map(id => translateMap
|
|
84
|
+
possibleSkills: localeJoin(option.Specific.options.map(id => attributedName(translateMap, getInstanceById, "profession", "Skill", id) ??
|
|
85
|
+
MISSING_VALUE), "disjunction"),
|
|
83
86
|
});
|
|
84
87
|
case "Group":
|
|
85
88
|
return translate("Skill Specialization for a {$skillOfGroup}", {
|
|
86
|
-
skillOfGroup: format(translateMap
|
|
87
|
-
MISSING_VALUE, { hiddenCount: 1 }),
|
|
89
|
+
skillOfGroup: format(attributedCustomName(translateMap, getInstanceById, "profession", t => t.longName, "SkillGroup", option.Group) ?? MISSING_VALUE, { hiddenCount: 1 }),
|
|
88
90
|
});
|
|
89
91
|
default:
|
|
90
92
|
return assertExhaustive(option);
|
|
@@ -101,11 +103,11 @@ const renderCombatTechniquesOption = (option) => isNotEmpty(option.fixed)
|
|
|
101
103
|
})), firstTextR);
|
|
102
104
|
const completeTextR = fixedTextR.then(fixedText => option.rest_rating_modifier === undefined
|
|
103
105
|
? Reader.of(fixedText)
|
|
104
|
-
: translateR("{$previous},
|
|
106
|
+
: translateR("{$previous}, the others {$rating}", {
|
|
105
107
|
previous: fixedText,
|
|
106
108
|
rating: option.rest_rating_modifier + 6,
|
|
107
109
|
}));
|
|
108
|
-
const listR = Reader.traverse(option.options,
|
|
110
|
+
const listR = Reader.traverse(option.options, id => attributedNameR("profession", id).map(name => name ?? MISSING_VALUE))
|
|
109
111
|
.thenW(list => localeSortR(list))
|
|
110
112
|
.map(list => list.join(", "));
|
|
111
113
|
return completeTextR.thenW(completeText => listR.map(list => `${completeText}: ${list}`));
|
|
@@ -121,11 +123,37 @@ const getTotalingAPValues = (professionPackages, selector) => {
|
|
|
121
123
|
options[0].ap_value
|
|
122
124
|
: options.map(option => option?.ap_value ?? 0).join("/");
|
|
123
125
|
};
|
|
124
|
-
const renderSkillsOption = (professionPackages, skillGroup) =>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
const renderSkillsOption = (professionPackages, skillGroup) => allSame(professionPackages.filter(pkg => pkg.content.options?.skills?.group === skillGroup.id), on(pkg => pkg.content.options?.skills?.specific, deepEqual))
|
|
127
|
+
? (mapNullable(getTotalingAPValues(professionPackages, pkg => pkg.content.options?.skills?.group === skillGroup.id
|
|
128
|
+
? pkg.content.options.skills
|
|
129
|
+
: undefined), apValue => professionPackages[0].content.options?.skills?.specific === undefined
|
|
130
|
+
? translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
|
|
131
|
+
apValue,
|
|
132
|
+
skillsOfGroup: skillGroup.nameOfSkillsOfGroup,
|
|
133
|
+
})
|
|
134
|
+
: Reader.traverse(professionPackages[0].content.options.skills.specific, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE))
|
|
135
|
+
.thenW(localeSortR)
|
|
136
|
+
.thenW(list => translateR("{$apValue} AP to distribute among the following skills: {$list}", {
|
|
137
|
+
apValue,
|
|
138
|
+
list: list.join(", "),
|
|
139
|
+
}))) ?? Reader.of(undefined))
|
|
140
|
+
: Reader.traverse(professionPackages, pkg => {
|
|
141
|
+
const skillsOption = pkg.content.options?.skills;
|
|
142
|
+
return skillsOption === undefined
|
|
143
|
+
? Reader.of("—")
|
|
144
|
+
: skillsOption.specific === undefined
|
|
145
|
+
? translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
|
|
146
|
+
apValue: skillsOption.ap_value,
|
|
147
|
+
skillsOfGroup: skillGroup.nameOfSkillsOfGroup,
|
|
148
|
+
})
|
|
149
|
+
: Reader.traverse(skillsOption.specific, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE))
|
|
150
|
+
.thenW(localeSortR)
|
|
151
|
+
.thenW(list => translateR("{$apValue} AP to distribute among the following skills: {$list}", {
|
|
152
|
+
apValue: skillsOption.ap_value,
|
|
153
|
+
list: list.join(", "),
|
|
154
|
+
}));
|
|
155
|
+
}).map(list => list.join(" / "));
|
|
156
|
+
const renderCantripList = (cantripIds) => Reader.traverse(cantripIds, id => attributedNameR("profession", "Cantrip", id).map(name => name ?? MISSING_VALUE))
|
|
129
157
|
.thenW(localeSortR)
|
|
130
158
|
.map(list => list.join(", "));
|
|
131
159
|
const renderVariantCantripsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
|
|
@@ -184,16 +212,15 @@ const renderVariantSkillSpecializationOption = (baseOptions, variantOptions) =>
|
|
|
184
212
|
remove: (base) => {
|
|
185
213
|
switch (base.kind) {
|
|
186
214
|
case "Specific":
|
|
187
|
-
return Reader.traverse(base.Specific.options, id =>
|
|
215
|
+
return Reader.traverse(base.Specific.options, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE))
|
|
188
216
|
.thenW(localeSortR)
|
|
189
217
|
.thenW(list => localeJoinR(list, "disjunction"))
|
|
190
218
|
.thenW(skillsText => translateR("no Skill Specialization {$possibleSkills}", {
|
|
191
219
|
possibleSkills: skillsText,
|
|
192
220
|
}));
|
|
193
221
|
case "Group":
|
|
194
|
-
return
|
|
195
|
-
.
|
|
196
|
-
.map(t => t?.longName ?? MISSING_VALUE)
|
|
222
|
+
return attributedCustomNameR("profession", t => t.longName, "SkillGroup", base.Group)
|
|
223
|
+
.map(name => name ?? MISSING_VALUE)
|
|
197
224
|
.thenW(longName => formatR(longName, { hiddenCount: 1 }))
|
|
198
225
|
.thenW(skillOfGroup => translateR("no Skill Specialization for a {$skillOfGroup}", {
|
|
199
226
|
skillOfGroup,
|
|
@@ -205,8 +232,8 @@ const renderVariantSkillSpecializationOption = (baseOptions, variantOptions) =>
|
|
|
205
232
|
add: override => renderSkillSpecializationOption(override),
|
|
206
233
|
update: (base, override) => {
|
|
207
234
|
if (base.kind === "Specific" && override.kind === "Specific") {
|
|
208
|
-
const overrideSkills = Reader.traverse(override.Specific.options, id =>
|
|
209
|
-
const baseSkills = Reader.traverse(base.Specific.options, id =>
|
|
235
|
+
const overrideSkills = Reader.traverse(override.Specific.options, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE)).thenW(list => localeJoinR(list, "disjunction"));
|
|
236
|
+
const baseSkills = Reader.traverse(base.Specific.options, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE)).thenW(list => localeJoinR(list, "disjunction"));
|
|
210
237
|
return overrideSkills
|
|
211
238
|
.thenW(overrideSkillsText => baseSkills.thenW(baseSkillsText => insteadOfR(overrideSkillsText, baseSkillsText)))
|
|
212
239
|
.then(skillsText => translateR("Skill Specialization {$possibleSkills}", {
|
|
@@ -234,9 +261,7 @@ const renderVariantCursesOption = (baseOptions, variantOptions) => renderVariant
|
|
|
234
261
|
})),
|
|
235
262
|
});
|
|
236
263
|
const renderCursesOption = (option) => renderVariantCursesOption(undefined, { kind: "Override", Override: option });
|
|
237
|
-
const nameOfSkillsOfGroupR = (skillGroupId) =>
|
|
238
|
-
.thenW(translationR)
|
|
239
|
-
.map(t => t?.longName ?? MISSING_VALUE);
|
|
264
|
+
const nameOfSkillsOfGroupR = (skillGroupId) => customNameR(t => t.longName, "SkillGroup", skillGroupId).map(name => name ?? MISSING_VALUE);
|
|
240
265
|
const renderVariantSkillsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
|
|
241
266
|
remove: base => nameOfSkillsOfGroupR(base.group).thenW(nameOfSkillsOfGroup => translateR("no AP to improve other {$skillsOfGroup}", {
|
|
242
267
|
skillsOfGroup: nameOfSkillsOfGroup,
|
|
@@ -257,33 +282,103 @@ const renderVariantLiturgiesOption = (baseOptions, variantOptions) => renderVari
|
|
|
257
282
|
apValue,
|
|
258
283
|
})),
|
|
259
284
|
});
|
|
260
|
-
const
|
|
285
|
+
const getEnhancementBaseName = (translate, enhancement, parentName) => {
|
|
286
|
+
switch (enhancement.parent.kind) {
|
|
287
|
+
case "Spell":
|
|
288
|
+
return translate("Spell Enhancement {$spell}", { spell: parentName });
|
|
289
|
+
case "Ritual":
|
|
290
|
+
return translate("Ritual Enhancement {$ritual}", { ritual: parentName });
|
|
291
|
+
case "LiturgicalChant":
|
|
292
|
+
case "Ceremony":
|
|
293
|
+
return translate("Liturgical Enhancement {$liturgicalChantOrCeremony}", {
|
|
294
|
+
liturgicalChantOrCeremony: parentName,
|
|
295
|
+
});
|
|
296
|
+
default:
|
|
297
|
+
return assertExhaustive(enhancement.parent);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
const isEnhancementNameComponents = (components) => components.id.kind === "Enhancement";
|
|
301
|
+
const isActivatableNameComponents = (components) => !isEnhancementNameComponents(components);
|
|
302
|
+
const getEnhancementNameComponents = (getInstanceById, translate, translateMap, id) => {
|
|
303
|
+
const instance = getInstanceById("Enhancement", id);
|
|
304
|
+
if (instance === undefined) {
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
id: Case("Enhancement", id),
|
|
309
|
+
parent: instance.parent,
|
|
310
|
+
base: getEnhancementBaseName(translate, instance, attributedName(translateMap, getInstanceById, "profession", instance.parent) ?? MISSING_VALUE),
|
|
311
|
+
options: translateMap(instance.translations)?.name ?? MISSING_VALUE,
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
const renderEnhancementNameComponents = (chunk) => [chunk.base, chunk.options].join(" ");
|
|
315
|
+
const isCombinedEnhancementNameComponents = (components) => Array.isArray(components.id);
|
|
316
|
+
const combineEnhancementNameComponents = (chunks) => {
|
|
317
|
+
if (allSame(chunks, (a, b) => deepEqual(a.parent, b.parent)) && isNotEmpty(chunks)) {
|
|
318
|
+
const [first, ...rest] = chunks;
|
|
319
|
+
return {
|
|
320
|
+
id: [first.id, ...rest.map(chunk => chunk.id)],
|
|
321
|
+
parent: first.parent,
|
|
322
|
+
base: first.base,
|
|
323
|
+
options: [
|
|
324
|
+
[first.id, first.options],
|
|
325
|
+
...rest.map((chunk) => [
|
|
326
|
+
chunk.id,
|
|
327
|
+
chunk.options,
|
|
328
|
+
]),
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const renderCombinedEnhancementNameComponents = (chunk) => [
|
|
337
|
+
chunk.base,
|
|
338
|
+
chunk.options
|
|
339
|
+
.map(([optionId, optionName]) => attributedInstance(optionName, optionId.kind, optionId.Enhancement, {
|
|
340
|
+
context: `"profession"`,
|
|
341
|
+
}))
|
|
342
|
+
.join(" + "),
|
|
343
|
+
].join(" ");
|
|
344
|
+
const getSpecialAbilityNameComponents = (getInstanceById, getResolvedSelectOptionById, translate, translateMap, option) => {
|
|
345
|
+
if (option.id.kind === "Enhancement") {
|
|
346
|
+
return getEnhancementNameComponents(getInstanceById, translate, translateMap, option.id.Enhancement);
|
|
347
|
+
}
|
|
261
348
|
const instance = getInstanceById(option.id);
|
|
262
349
|
if (instance === undefined) {
|
|
263
350
|
return undefined;
|
|
264
351
|
}
|
|
265
352
|
return getNameComponents(translate, option.id, option.options, option.level, "nameBuilderRules" in instance ? instance.nameBuilderRules : undefined, instance.translations, t => t.name, getResolvedSelectOptionById, true);
|
|
266
353
|
};
|
|
354
|
+
const wrapActivatableInAttributedString = (text, id) => attributedInstance(text, id.kind, fromUniformCase(id), { context: '"prerequisite"' });
|
|
355
|
+
const renderSingleActivatableNameChunk = (translateMap, chunk) => chunk === undefined
|
|
356
|
+
? MISSING_VALUE
|
|
357
|
+
: wrapActivatableInAttributedString(isEnhancementNameComponents(chunk)
|
|
358
|
+
? renderEnhancementNameComponents(chunk)
|
|
359
|
+
: renderActivatableNameComponents(translateMap, chunk, false), chunk.id);
|
|
267
360
|
const renderSpecialAbilityName = (specialAbility) => Reader.asks(({ translate, translateMap, localeJoin, getInstanceById, getResolvedSelectOptionById, }) => {
|
|
268
361
|
switch (specialAbility.kind) {
|
|
269
362
|
case "Constant": {
|
|
270
|
-
const chunk = getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, specialAbility.Constant);
|
|
271
|
-
return chunk
|
|
272
|
-
? MISSING_VALUE
|
|
273
|
-
: renderActivatableNameComponents(translateMap, chunk, false);
|
|
363
|
+
const chunk = getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, translateMap, specialAbility.Constant);
|
|
364
|
+
return renderSingleActivatableNameChunk(translateMap, chunk);
|
|
274
365
|
}
|
|
275
366
|
case "Selection": {
|
|
276
|
-
const chunks = specialAbility.Selection.options.map(option => getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, option));
|
|
367
|
+
const chunks = specialAbility.Selection.options.map(option => getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, translateMap, option));
|
|
277
368
|
const possiblyCombined = isNotEmpty(specialAbility.Selection.options) && chunks.every(isNotNullish)
|
|
278
|
-
?
|
|
369
|
+
? chunks.every(isEnhancementNameComponents)
|
|
370
|
+
? combineEnhancementNameComponents(chunks)
|
|
371
|
+
: chunks.every(isActivatableNameComponents)
|
|
372
|
+
? combineNameComponents(chunks)
|
|
373
|
+
: undefined
|
|
279
374
|
: undefined;
|
|
280
375
|
if (possiblyCombined === undefined) {
|
|
281
|
-
return localeJoin(chunks.map(chunk => chunk
|
|
282
|
-
? MISSING_VALUE
|
|
283
|
-
: renderActivatableNameComponents(translateMap, chunk, false)), "disjunction");
|
|
376
|
+
return localeJoin(chunks.map(chunk => renderSingleActivatableNameChunk(translateMap, chunk)), "disjunction");
|
|
284
377
|
}
|
|
285
378
|
else {
|
|
286
|
-
return
|
|
379
|
+
return isCombinedEnhancementNameComponents(possiblyCombined)
|
|
380
|
+
? renderCombinedEnhancementNameComponents(possiblyCombined)
|
|
381
|
+
: wrapActivatableInAttributedString(renderCombinedActivatableNameComponents(translateMap, possiblyCombined, false, list => localeJoin(list, "disjunction")), possiblyCombined.id);
|
|
287
382
|
}
|
|
288
383
|
}
|
|
289
384
|
default:
|
|
@@ -310,7 +405,7 @@ const renderSpecialAbilities = (specialAbilities) => Reader.sequence([
|
|
|
310
405
|
return result === undefined ? translateR("none") : Reader.of(result);
|
|
311
406
|
});
|
|
312
407
|
const renderCombatTechniques = (professionPackages) => Reader.sequence([
|
|
313
|
-
renderNumericListAcrossPackages(professionPackages, pkg => Reader.of(pkg.content.combat_techniques?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, (ctId) =>
|
|
408
|
+
renderNumericListAcrossPackages(professionPackages, pkg => Reader.of(pkg.content.combat_techniques?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, (ctId) => attributedNameR("profession", ctId).map(name => name ?? MISSING_VALUE), 6),
|
|
314
409
|
professionPackages.some(pkg => pkg.content.options?.combat_techniques !== undefined)
|
|
315
410
|
? 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
411
|
: Reader.of(undefined),
|
|
@@ -318,7 +413,7 @@ const renderCombatTechniques = (professionPackages) => Reader.sequence([
|
|
|
318
413
|
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
414
|
renderNumericListAcrossPackages(professionPackages, pkg => Reader.asks(({ getInstanceById }) => pkg.content.skills
|
|
320
415
|
?.filter(skill => getInstanceById("Skill", skill.id)?.group === groupId)
|
|
321
|
-
.map(skill => [skill.id, skill.rating_modifier]) ?? []), equal, skillId =>
|
|
416
|
+
.map(skill => [skill.id, skill.rating_modifier]) ?? []), equal, skillId => attributedNameR("profession", "Skill", skillId).map(name => name ?? MISSING_VALUE), 0),
|
|
322
417
|
renderSkillsOption(professionPackages, {
|
|
323
418
|
id: groupId,
|
|
324
419
|
nameOfSkillsOfGroup: groupTranslation?.longName ?? MISSING_VALUE,
|
|
@@ -330,11 +425,11 @@ const renderSkills = (professionPackages) => Reader.asks(({ getAllInstances }) =
|
|
|
330
425
|
const renderSpellworkName = (spellworkIds) => Reader.traverse(spellworkIds, spellworkId => {
|
|
331
426
|
switch (spellworkId.kind) {
|
|
332
427
|
case "Spellwork":
|
|
333
|
-
return
|
|
428
|
+
return attributedNameR("profession", spellworkId.Spellwork.id).thenW(baseName => (spellworkId.Spellwork.tradition === undefined
|
|
334
429
|
? Reader.of(undefined)
|
|
335
|
-
:
|
|
430
|
+
: attributedNameR("profession", "MagicalTradition", spellworkId.Spellwork.tradition)).map(traditionName => (baseName ?? MISSING_VALUE) + parensIf(traditionName)));
|
|
336
431
|
case "MagicalAction":
|
|
337
|
-
return
|
|
432
|
+
return attributedNameR("profession", spellworkId.MagicalAction.id).map(name => name ?? MISSING_VALUE);
|
|
338
433
|
default:
|
|
339
434
|
return spellworkId;
|
|
340
435
|
}
|
|
@@ -358,7 +453,7 @@ const retrieveBlessedTraditionIdentifierFromPrerequisites = (prerequisites) => p
|
|
|
358
453
|
return assertExhaustive(part);
|
|
359
454
|
}
|
|
360
455
|
}) ?? [];
|
|
361
|
-
const renderBlessingList = (blessings) => Reader.traverse(blessings, id =>
|
|
456
|
+
const renderBlessingList = (blessings) => Reader.traverse(blessings, id => attributedNameR("profession", "Blessing", id).map(name => name ?? MISSING_VALUE)).thenW(localeSortR);
|
|
362
457
|
const renderRestrictedBlessings = (restrictedBlessings) => {
|
|
363
458
|
switch (restrictedBlessings.kind) {
|
|
364
459
|
case "Three":
|
|
@@ -406,7 +501,7 @@ const renderBlessingsForPackages = (professionPackages) => renderBlessings(profe
|
|
|
406
501
|
const renderBlessingsForVariant = (professionVariant) => renderBlessings([
|
|
407
502
|
retrieveBlessedTraditionIdentifierFromPrerequisites(professionVariant.prerequisites),
|
|
408
503
|
]).map(blessingsText => (blessingsText !== undefined ? [blessingsText] : []));
|
|
409
|
-
const renderLiturgicalChantName = (liturgyIds) => Reader.traverse(liturgyIds,
|
|
504
|
+
const renderLiturgicalChantName = (liturgyIds) => Reader.traverse(liturgyIds, id => attributedNameR("profession", id).map(name => name ?? MISSING_VALUE)).thenW(list => localeJoinR(list, "disjunction"));
|
|
410
505
|
const renderLiturgicalChants = (professionPackages) => Reader.sequence([
|
|
411
506
|
renderBlessingsForPackages(professionPackages),
|
|
412
507
|
renderNumericListAcrossPackages(professionPackages, (pkg) => Reader.of(pkg.content.liturgical_chants?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, renderLiturgicalChantName, 0),
|
package/lib/entities/race.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { CountInstances, GetAllChildInstancesForParent, GetInstanceById } f
|
|
|
3
3
|
* Get a JSON representation of the rules text for a race.
|
|
4
4
|
*/
|
|
5
5
|
export declare const getRaceEntityDescription: import("../creator.js").EntityDescriptionCreator<"Race", {
|
|
6
|
-
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "Advantage" | "Disadvantage" | "Culture">;
|
|
6
|
+
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "Advantage" | "Disadvantage" | "Culture" | "DerivedCharacteristic">;
|
|
7
7
|
countInstances: CountInstances<"Attribute">;
|
|
8
8
|
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"RaceVariant">;
|
|
9
9
|
}, import("../index.js").EntityDescription>;
|
package/lib/entities/race.js
CHANGED
|
@@ -3,24 +3,30 @@ import { isNotEmpty } from "@elyukai/utils/array/nonEmpty";
|
|
|
3
3
|
import { deepEqual } from "@elyukai/utils/equality";
|
|
4
4
|
import { on } from "@elyukai/utils/function";
|
|
5
5
|
import { isNotNullish } from "@elyukai/utils/nullable";
|
|
6
|
+
import { compareNumber } from "@elyukai/utils/ordering";
|
|
6
7
|
import { Reader } from "@elyukai/utils/reader";
|
|
7
8
|
import { sign } from "@elyukai/utils/string/number";
|
|
8
9
|
import { createEntityDescriptionCreator } from "../creator.js";
|
|
9
10
|
import { renderCommonnessRatedAdvantagesAndDisadvantages, renderCommonnessRatedAdvantagesOrDisadvantages, renderValueWithPossibleTranslation, } from "./partial/commonnessRatedAdvantagesAndDisadvantages.js";
|
|
11
|
+
import { attributedCustomName, attributedName } from "./partial/markdown.js";
|
|
12
|
+
import { getInstanceByIdR, translateMapR, } from "./partial/reader.js";
|
|
10
13
|
import { MISSING_VALUE } from "./partial/unknown.js";
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
const renderBaseValues = (values) => Reader.traverse(Object.entries(values), ([id, { value }]) => getInstanceByIdR("DerivedCharacteristic", id).thenW(derivedCharacteristic => translateMapR(derivedCharacteristic?.translations).thenW(translation => Reader.asks(({ translate }) => [
|
|
15
|
+
derivedCharacteristic?.position ?? 0,
|
|
16
|
+
{
|
|
17
|
+
label: translate("{$derivedCharacteristic} Base Value", {
|
|
18
|
+
derivedCharacteristic: translation?.name ?? MISSING_VALUE,
|
|
19
|
+
}),
|
|
20
|
+
value: value < 0 ? sign(value) : value.toFixed(),
|
|
21
|
+
},
|
|
22
|
+
])))).map(items => items.sort(on(item => item[0], compareNumber)).map(item => item[1]));
|
|
23
|
+
const renderAttributeAdjustmentsItem = (totalAttributesCount, adjustments) => Reader.asks(({ translate, translateMap, localeJoin, getInstanceById }) => {
|
|
24
|
+
const getAttributeAbbreviation = (id) => attributedCustomName(translateMap, getInstanceById, "race", t => t.abbreviation, "Attribute", id) ?? MISSING_VALUE;
|
|
25
|
+
return [
|
|
26
|
+
...(adjustments.fixed?.map(adj => `${getAttributeAbbreviation(adj.id)} ${sign(adj.value)}`) ??
|
|
27
|
+
[]),
|
|
28
|
+
...(adjustments.selectable?.map(adj => `${adj.list.length === totalAttributesCount ? translate("one attribute of your choice") : localeJoin(adj.list.map(getAttributeAbbreviation), "disjunction")} ${sign(adj.value)}`) ?? []),
|
|
29
|
+
].join("; ");
|
|
24
30
|
});
|
|
25
31
|
const renderVariantValues = (label, variants, selector, renderValue, translationSelector) => Reader.asks(({ translate, translateMap, localeCompare, localeJoin, }) => {
|
|
26
32
|
const values = variants.map(variant => {
|
|
@@ -69,16 +75,13 @@ const renderVariantValues = (label, variants, selector, renderValue, translation
|
|
|
69
75
|
const renderAutomaticAdvantagesOrDisadvantages = (entity, items) => Reader.asks(({ translate, translateMap, getInstanceById, localeCompare }) => items === undefined || !isNotEmpty(items)
|
|
70
76
|
? translate("none")
|
|
71
77
|
: items
|
|
72
|
-
.map(item =>
|
|
73
|
-
const instanceTranslation = translateMap(getInstanceById(entity, item.id)?.translations);
|
|
74
|
-
return (instanceTranslation?.name_in_library ?? instanceTranslation?.name ?? MISSING_VALUE);
|
|
75
|
-
})
|
|
78
|
+
.map(item => attributedCustomName(translateMap, getInstanceById, "race", t => t.name_in_library ?? t.name, entity, item.id) ?? MISSING_VALUE)
|
|
76
79
|
.toSorted(localeCompare)
|
|
77
80
|
.join(", "));
|
|
78
81
|
const renderCommonCultures = (items) => Reader.asks(({ translate, translateMap, getInstanceById, localeCompare }) => items === undefined || !isNotEmpty(items)
|
|
79
82
|
? translate("none")
|
|
80
83
|
: items
|
|
81
|
-
.map(itemId => translateMap
|
|
84
|
+
.map(itemId => attributedName(translateMap, getInstanceById, "race", "Culture", itemId))
|
|
82
85
|
.filter(isNotNullish)
|
|
83
86
|
.toSorted(localeCompare)
|
|
84
87
|
.join(", "));
|
|
@@ -112,11 +115,8 @@ export const getRaceEntityDescription = createEntityDescriptionCreator(({ getIns
|
|
|
112
115
|
value: entry.ap_value,
|
|
113
116
|
}),
|
|
114
117
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
renderBaseValueItem("Toughness Base Value", entry.base_values.toughness).run(env),
|
|
118
|
-
renderBaseValueItem("Movement Base Value", entry.base_values.movement).run(env),
|
|
119
|
-
renderAttributeAdjustmentsItem(totalAttributesCount, entry.attribute_adjustments).run(env),
|
|
118
|
+
...renderBaseValues(entry.base_values).run(env),
|
|
119
|
+
renderVariantValues("Attribute Adjustments", raceVariants, v => v.attribute_adjustments, attrs => renderAttributeAdjustmentsItem(totalAttributesCount, attrs).run(env)).run(env),
|
|
120
120
|
renderVariantValues("Common Cultures", raceVariants, v => v.common_cultures, cultures => renderCommonCultures(cultures).run(env)).run(env),
|
|
121
121
|
entry.automatic_advantages === undefined
|
|
122
122
|
? undefined
|
package/lib/entities/spell.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type RatedIdentifier } from "optolith-database-schema/gen";
|
|
1
|
+
import { type ActivatableIdentifier, type RatedIdentifier } from "optolith-database-schema/gen";
|
|
2
2
|
import type { GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js";
|
|
3
3
|
import { type IdMap } from "../index.js";
|
|
4
|
+
import type { GetResolvedSelectOptionById } from "./partial/prerequisites/single/activatable.js";
|
|
4
5
|
/**
|
|
5
6
|
* Get a JSON representation of the rules text for a cantrip.
|
|
6
7
|
*/
|
|
@@ -72,7 +73,8 @@ export declare const getAnimistPowerEntityDescription: import("../creator.js").E
|
|
|
72
73
|
* Get a JSON representation of the rules text for a Goede ritual.
|
|
73
74
|
*/
|
|
74
75
|
export declare const getGeodeRitualEntityDescription: import("../creator.js").EntityDescriptionCreator<"GeodeRitual", {
|
|
75
|
-
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "SkillModificationLevel" | "TargetCategory" | "Property" | "MagicalTradition" | "DerivedCharacteristic" | "Influence">;
|
|
76
|
+
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "SkillModificationLevel" | "TargetCategory" | "Property" | "MagicalTradition" | "DerivedCharacteristic" | "Influence" | ActivatableIdentifier["kind"] | "Aspect">;
|
|
77
|
+
getResolvedSelectOptionById: GetResolvedSelectOptionById;
|
|
76
78
|
}, import("../index.js").EntityDescription>;
|
|
77
79
|
/**
|
|
78
80
|
* Get a JSON representation of the rules text for a jester trick.
|
package/lib/entities/spell.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Case } from "tsondb/schema/gen";
|
|
|
11
11
|
import { createEntityDescriptionCreator } from "../creator.js";
|
|
12
12
|
import { renderAnimalTypesSection } from "./partial/animalTypes.js";
|
|
13
13
|
import { renderEnhancements } from "./partial/enhancements.js";
|
|
14
|
+
import { attributedCustomName, attributedName } from "./partial/markdown.js";
|
|
14
15
|
import { printAnimistPowerPrerequisites, printGeodeRitualPrerequisites, } from "./partial/prerequisites/index.js";
|
|
15
16
|
import { renderCastingTime, renderFastSkillNonModifiableCastingTime, renderSlowSkillNonModifiableCastingTime, } from "./partial/rated/activatable/castingTime.js";
|
|
16
17
|
import { renderMagicalActionCost, renderModifiableOneTimeCost, renderNonModifiableOneTimeCost, } from "./partial/rated/activatable/cost.js";
|
|
@@ -28,29 +29,18 @@ import { formatEnergyR, localeJoinR, responsiveR, responsiveTextR, responsiveTra
|
|
|
28
29
|
import { appendNoteIfNeeded, getResponsiveText, ResponsiveTextSize, } from "./partial/responsiveText.js";
|
|
29
30
|
import { formatTimeSpanR } from "./partial/units/timeSpan.js";
|
|
30
31
|
import { MISSING_VALUE } from "./partial/unknown.js";
|
|
31
|
-
const renderProperty = (id) => Reader.asks(({ translate, translateMap, getInstanceById }) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (staticEntryTranslation === undefined) {
|
|
36
|
-
return "";
|
|
37
|
-
}
|
|
38
|
-
return staticEntryTranslation.name;
|
|
39
|
-
})();
|
|
40
|
-
return {
|
|
41
|
-
label: translate("Property"),
|
|
42
|
-
value: text,
|
|
43
|
-
};
|
|
44
|
-
});
|
|
32
|
+
const renderProperty = (id) => Reader.asks(({ translate, translateMap, getInstanceById }) => ({
|
|
33
|
+
label: translate("Property"),
|
|
34
|
+
value: attributedName(translateMap, getInstanceById, "property", "Property", id) ?? "",
|
|
35
|
+
}));
|
|
45
36
|
const getTextForTraditions = (deps, value) => {
|
|
46
37
|
const text = (() => {
|
|
47
38
|
switch (value.kind) {
|
|
48
39
|
case "General":
|
|
49
40
|
return deps.translate("General");
|
|
50
41
|
case "Specific":
|
|
51
|
-
return value.Specific.map(trad => deps.translateMap
|
|
42
|
+
return value.Specific.map(trad => attributedCustomName(deps.translateMap, deps.getInstanceById, "traditions", t => t.name_for_arcane_spellworks ?? t.name, "MagicalTradition", trad))
|
|
52
43
|
.filter(isNotNullish)
|
|
53
|
-
.map(trad => trad.name_for_arcane_spellworks ?? trad.name)
|
|
54
44
|
.sort(deps.localeCompare)
|
|
55
45
|
.join(", ");
|
|
56
46
|
default:
|
|
@@ -62,10 +52,7 @@ const getTextForTraditions = (deps, value) => {
|
|
|
62
52
|
value: text,
|
|
63
53
|
};
|
|
64
54
|
};
|
|
65
|
-
const getTraditionNameForArcaneSpellworksById = (id, getInstanceById, translateMap) =>
|
|
66
|
-
const translation = translateMap(getInstanceById("MagicalTradition", id)?.translations);
|
|
67
|
-
return translation?.name_for_arcane_spellworks ?? translation?.name;
|
|
68
|
-
};
|
|
55
|
+
const getTraditionNameForArcaneSpellworksById = (id, getInstanceById, translateMap) => attributedCustomName(translateMap, getInstanceById, "cantrip-note", t => t.name_for_arcane_spellworks ?? t.name, "MagicalTradition", id);
|
|
69
56
|
/**
|
|
70
57
|
* Get a JSON representation of the rules text for a cantrip.
|
|
71
58
|
*/
|
|
@@ -107,7 +94,7 @@ export const getCantripEntityDescription = createEntityDescriptionCreator(({ get
|
|
|
107
94
|
.map(academyOrTradition => {
|
|
108
95
|
switch (academyOrTradition.kind) {
|
|
109
96
|
case "Academy":
|
|
110
|
-
return translateMap
|
|
97
|
+
return attributedName(translateMap, getInstanceById, "cantrip-note", "Curriculum", academyOrTradition.Academy);
|
|
111
98
|
case "Tradition": {
|
|
112
99
|
return mapNullable(getTraditionNameForArcaneSpellworksById(academyOrTradition.Tradition.id, getInstanceById, translateMap), name => name +
|
|
113
100
|
parensIf(translateMap(academyOrTradition.Tradition.translations)?.note));
|
|
@@ -296,7 +283,8 @@ export const getCurseEntityDescription = createEntityDescriptionCreator(({ getIn
|
|
|
296
283
|
const renderMagicalActionSkill = (skill) => Reader.asks(({ translate, translateMap, localeCompare, localeJoin, getInstanceById }) => ({
|
|
297
284
|
label: translate("Skill"),
|
|
298
285
|
value: localeJoin(skill
|
|
299
|
-
.map(id => translateMap
|
|
286
|
+
.map(id => attributedName(translateMap, getInstanceById, "magical-action-skill", "Skill", id) ??
|
|
287
|
+
MISSING_VALUE)
|
|
300
288
|
.toSorted(localeCompare), "disjunction"),
|
|
301
289
|
}));
|
|
302
290
|
/**
|
|
@@ -383,7 +371,7 @@ export const getDominationRitualEntityDescription = createEntityDescriptionCreat
|
|
|
383
371
|
const renderMusicTradition = (translate, translateMap, localeCompare, getInstanceById, entity, musicTraditions) => ({
|
|
384
372
|
label: translate("Music Tradition"),
|
|
385
373
|
value: ensureNonEmpty(musicTraditions
|
|
386
|
-
.map(trad => translateMap
|
|
374
|
+
.map(trad => attributedName(translateMap, getInstanceById, "music-tradition", entity, trad.id))
|
|
387
375
|
.filter(isNotNullish)
|
|
388
376
|
.toSorted(localeCompare))?.join(", ") ?? MISSING_VALUE,
|
|
389
377
|
});
|
|
@@ -472,7 +460,7 @@ export const getMagicalMelodyEntityDescription = createEntityDescriptionCreator(
|
|
|
472
460
|
const renderFamiliarsTrickProperty = (translateMap, getInstanceById, responsiveTextSize, property) => {
|
|
473
461
|
switch (property.kind) {
|
|
474
462
|
case "Fixed":
|
|
475
|
-
return (translateMap
|
|
463
|
+
return (attributedName(translateMap, getInstanceById, "property", "Property", property.Fixed) ??
|
|
476
464
|
MISSING_VALUE);
|
|
477
465
|
case "Indefinite":
|
|
478
466
|
return getResponsiveText(translateMap(property.Indefinite.translations)?.description, responsiveTextSize);
|
|
@@ -573,7 +561,7 @@ const renderAnimistPowerTribeTradition = (translate, translateMap, localeCompare
|
|
|
573
561
|
return translate("General");
|
|
574
562
|
}
|
|
575
563
|
return (ensureNonEmpty(tribeTradition
|
|
576
|
-
.map(id => translateMap
|
|
564
|
+
.map(id => attributedName(translateMap, getInstanceById, "tribe", "Tribe", id))
|
|
577
565
|
.filter(isNotNullish)
|
|
578
566
|
.toSorted(localeCompare))?.join(", ") ?? MISSING_VALUE);
|
|
579
567
|
};
|
|
@@ -678,7 +666,7 @@ export const getAnimistPowerEntityDescription = createEntityDescriptionCreator((
|
|
|
678
666
|
/**
|
|
679
667
|
* Get a JSON representation of the rules text for a Goede ritual.
|
|
680
668
|
*/
|
|
681
|
-
export const getGeodeRitualEntityDescription = createEntityDescriptionCreator(({ getInstanceById }, locale, { content: entry }) => {
|
|
669
|
+
export const getGeodeRitualEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getResolvedSelectOptionById }, locale, { content: entry }) => {
|
|
682
670
|
const { translate, translateMap } = locale;
|
|
683
671
|
const translation = translateMap(entry.translations);
|
|
684
672
|
if (translation === undefined) {
|
|
@@ -719,7 +707,7 @@ export const getGeodeRitualEntityDescription = createEntityDescriptionCreator(({
|
|
|
719
707
|
? undefined
|
|
720
708
|
: {
|
|
721
709
|
label: translate("Prerequisites"),
|
|
722
|
-
value: printGeodeRitualPrerequisites(getInstanceById, locale, entry.prerequisites),
|
|
710
|
+
value: printGeodeRitualPrerequisites(getInstanceById, getResolvedSelectOptionById, locale, entry.prerequisites),
|
|
723
711
|
},
|
|
724
712
|
renderProperty(entry.property).run(env),
|
|
725
713
|
{
|
|
@@ -848,7 +836,7 @@ const renderMagicalRuneCost = (options, cost) => {
|
|
|
848
836
|
}
|
|
849
837
|
};
|
|
850
838
|
const renderSplitMagicalRuneParameterTranslation = (parameter) => responsiveR(() => parameter.fast.full, () => parameter.fast.abbr).map2(responsiveR(() => parameter.slow.full, () => parameter.slow.abbr), (fast, slow) => `${slow} / ${fast}`);
|
|
851
|
-
const
|
|
839
|
+
const renderMagicalRuneCraftingTimePart = (craftingTime, unit) => formatTimeSpanR(unit, craftingTime.value).thenW(text => {
|
|
852
840
|
if (craftingTime.per === undefined) {
|
|
853
841
|
return Reader.of(text);
|
|
854
842
|
}
|
|
@@ -863,7 +851,7 @@ const renderMagicalRunCraftingTimePart = (craftingTime, unit) => formatTimeSpanR
|
|
|
863
851
|
}));
|
|
864
852
|
});
|
|
865
853
|
});
|
|
866
|
-
const renderMagicalRuneCraftingTime = (craftingTime) =>
|
|
854
|
+
const renderMagicalRuneCraftingTime = (craftingTime) => renderMagicalRuneCraftingTimePart(craftingTime, "Actions").map2(renderMagicalRuneCraftingTimePart(craftingTime, "Days"), (fast, slow) => `${slow} / ${fast}`);
|
|
867
855
|
const renderMagicalRuneDuration = (duration) => renderCheckResultBasedDuration(duration.fast).map2(renderCheckResultBasedDuration(duration.slow), (fast, slow) => `${slow} / ${fast}`);
|
|
868
856
|
const renderMagicalRuneImprovementCost = (options, improvementCost) => {
|
|
869
857
|
switch (improvementCost.kind) {
|