@optolith/entity-descriptions 0.4.7 → 0.4.10

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 (33) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/lib/entities/activatable.d.ts +8 -3
  3. package/lib/entities/activatable.js +187 -15
  4. package/lib/entities/curriculum.js +14 -21
  5. package/lib/entities/derivedCharacteristic.js +5 -7
  6. package/lib/entities/equipmentPackage.js +2 -1
  7. package/lib/entities/liturgicalChant.js +7 -3
  8. package/lib/entities/partial/commonnessRatedAdvantagesAndDisadvantages.js +8 -12
  9. package/lib/entities/partial/herbary.js +5 -8
  10. package/lib/entities/partial/markdown.d.ts +55 -0
  11. package/lib/entities/partial/markdown.js +59 -0
  12. package/lib/entities/partial/prerequisites/index.d.ts +1 -1
  13. package/lib/entities/partial/prerequisites/index.js +26 -13
  14. package/lib/entities/partial/prerequisites/part.js +8 -3
  15. package/lib/entities/partial/prerequisites/single/culture.js +2 -1
  16. package/lib/entities/partial/prerequisites/single/personalityTrait.js +2 -1
  17. package/lib/entities/partial/prerequisites/single/profession.js +4 -1
  18. package/lib/entities/partial/prerequisites/single/race.js +2 -1
  19. package/lib/entities/partial/prerequisites/single/rated.js +2 -27
  20. package/lib/entities/partial/prerequisites/single/ratedMinimumNumber.js +2 -1
  21. package/lib/entities/partial/prerequisites/single/ratedSum.js +2 -1
  22. package/lib/entities/partial/prerequisites/single/state.js +2 -1
  23. package/lib/entities/partial/reader.d.ts +33 -0
  24. package/lib/entities/partial/reader.js +13 -0
  25. package/lib/entities/partial/unknown.d.ts +1 -1
  26. package/lib/entities/partial/unknown.js +1 -1
  27. package/lib/entities/personalityTrait.js +9 -2
  28. package/lib/entities/profession.js +25 -25
  29. package/lib/entities/race.js +4 -6
  30. package/lib/entities/spell.js +13 -25
  31. package/lib/index.d.ts +53 -53
  32. package/lib/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +2 -2
@@ -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 { 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, nameR, strictNameR, translateMapFnR, translateR, translationR, } from "./partial/reader.js";
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(getInstanceById("Skill", id)?.translations)?.name ?? MISSING_VALUE), "disjunction"),
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(getInstanceById("SkillGroup", option.Group)?.translations)?.longName ??
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);
@@ -105,7 +107,7 @@ const renderCombatTechniquesOption = (option) => isNotEmpty(option.fixed)
105
107
  previous: fixedText,
106
108
  rating: option.rest_rating_modifier + 6,
107
109
  }));
108
- const listR = Reader.traverse(option.options, strictNameR)
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}`));
@@ -125,7 +127,7 @@ const renderSkillsOption = (professionPackages, skillGroup) => mapNullable(getTo
125
127
  apValue,
126
128
  skillsOfGroup: skillGroup.nameOfSkillsOfGroup,
127
129
  })) ?? Reader.of(undefined);
128
- const renderCantripList = (cantripIds) => Reader.traverse(cantripIds, id => strictNameR("Cantrip", id))
130
+ const renderCantripList = (cantripIds) => Reader.traverse(cantripIds, id => attributedNameR("profession", "Cantrip", id).map(name => name ?? MISSING_VALUE))
129
131
  .thenW(localeSortR)
130
132
  .map(list => list.join(", "));
131
133
  const renderVariantCantripsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
@@ -184,16 +186,15 @@ const renderVariantSkillSpecializationOption = (baseOptions, variantOptions) =>
184
186
  remove: (base) => {
185
187
  switch (base.kind) {
186
188
  case "Specific":
187
- return Reader.traverse(base.Specific.options, id => strictNameR("Skill", id))
189
+ return Reader.traverse(base.Specific.options, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE))
188
190
  .thenW(localeSortR)
189
191
  .thenW(list => localeJoinR(list, "disjunction"))
190
192
  .thenW(skillsText => translateR("no Skill Specialization {$possibleSkills}", {
191
193
  possibleSkills: skillsText,
192
194
  }));
193
195
  case "Group":
194
- return getInstanceByIdR("SkillGroup", base.Group)
195
- .thenW(translationR)
196
- .map(t => t?.longName ?? MISSING_VALUE)
196
+ return attributedCustomNameR("profession", t => t.longName, "SkillGroup", base.Group)
197
+ .map(name => name ?? MISSING_VALUE)
197
198
  .thenW(longName => formatR(longName, { hiddenCount: 1 }))
198
199
  .thenW(skillOfGroup => translateR("no Skill Specialization for a {$skillOfGroup}", {
199
200
  skillOfGroup,
@@ -205,8 +206,8 @@ const renderVariantSkillSpecializationOption = (baseOptions, variantOptions) =>
205
206
  add: override => renderSkillSpecializationOption(override),
206
207
  update: (base, override) => {
207
208
  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"));
209
+ const overrideSkills = Reader.traverse(override.Specific.options, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE)).thenW(list => localeJoinR(list, "disjunction"));
210
+ const baseSkills = Reader.traverse(base.Specific.options, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE)).thenW(list => localeJoinR(list, "disjunction"));
210
211
  return overrideSkills
211
212
  .thenW(overrideSkillsText => baseSkills.thenW(baseSkillsText => insteadOfR(overrideSkillsText, baseSkillsText)))
212
213
  .then(skillsText => translateR("Skill Specialization {$possibleSkills}", {
@@ -234,9 +235,7 @@ const renderVariantCursesOption = (baseOptions, variantOptions) => renderVariant
234
235
  })),
235
236
  });
236
237
  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);
238
+ const nameOfSkillsOfGroupR = (skillGroupId) => customNameR(t => t.longName, "SkillGroup", skillGroupId).map(name => name ?? MISSING_VALUE);
240
239
  const renderVariantSkillsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
241
240
  remove: base => nameOfSkillsOfGroupR(base.group).thenW(nameOfSkillsOfGroup => translateR("no AP to improve other {$skillsOfGroup}", {
242
241
  skillsOfGroup: nameOfSkillsOfGroup,
@@ -264,13 +263,14 @@ const getSpecialAbilityNameComponents = (getInstanceById, getResolvedSelectOptio
264
263
  }
265
264
  return getNameComponents(translate, option.id, option.options, option.level, "nameBuilderRules" in instance ? instance.nameBuilderRules : undefined, instance.translations, t => t.name, getResolvedSelectOptionById, true);
266
265
  };
266
+ const wrapActivatableInAttributedString = (text, id) => attributedInstance(text, id.kind, fromUniformCase(id), { context: '"prerequisite"' });
267
267
  const renderSpecialAbilityName = (specialAbility) => Reader.asks(({ translate, translateMap, localeJoin, getInstanceById, getResolvedSelectOptionById, }) => {
268
268
  switch (specialAbility.kind) {
269
269
  case "Constant": {
270
270
  const chunk = getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, specialAbility.Constant);
271
271
  return chunk === undefined
272
272
  ? MISSING_VALUE
273
- : renderActivatableNameComponents(translateMap, chunk, false);
273
+ : wrapActivatableInAttributedString(renderActivatableNameComponents(translateMap, chunk, false), chunk.id);
274
274
  }
275
275
  case "Selection": {
276
276
  const chunks = specialAbility.Selection.options.map(option => getSpecialAbilityNameComponents(getInstanceById, getResolvedSelectOptionById, translate, option));
@@ -280,10 +280,10 @@ const renderSpecialAbilityName = (specialAbility) => Reader.asks(({ translate, t
280
280
  if (possiblyCombined === undefined) {
281
281
  return localeJoin(chunks.map(chunk => chunk === undefined
282
282
  ? MISSING_VALUE
283
- : renderActivatableNameComponents(translateMap, chunk, false)), "disjunction");
283
+ : wrapActivatableInAttributedString(renderActivatableNameComponents(translateMap, chunk, false), chunk.id)), "disjunction");
284
284
  }
285
285
  else {
286
- return renderCombinedActivatableNameComponents(translateMap, possiblyCombined, false, list => localeJoin(list, "disjunction"));
286
+ return wrapActivatableInAttributedString(renderCombinedActivatableNameComponents(translateMap, possiblyCombined, false, list => localeJoin(list, "disjunction")), possiblyCombined.id);
287
287
  }
288
288
  }
289
289
  default:
@@ -310,7 +310,7 @@ const renderSpecialAbilities = (specialAbilities) => Reader.sequence([
310
310
  return result === undefined ? translateR("none") : Reader.of(result);
311
311
  });
312
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),
313
+ 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
314
  professionPackages.some(pkg => pkg.content.options?.combat_techniques !== undefined)
315
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
316
  : Reader.of(undefined),
@@ -318,7 +318,7 @@ const renderCombatTechniques = (professionPackages) => Reader.sequence([
318
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
319
  renderNumericListAcrossPackages(professionPackages, pkg => Reader.asks(({ getInstanceById }) => pkg.content.skills
320
320
  ?.filter(skill => getInstanceById("Skill", skill.id)?.group === groupId)
321
- .map(skill => [skill.id, skill.rating_modifier]) ?? []), equal, skillId => strictNameR("Skill", skillId), 0),
321
+ .map(skill => [skill.id, skill.rating_modifier]) ?? []), equal, skillId => attributedNameR("profession", "Skill", skillId).map(name => name ?? MISSING_VALUE), 0),
322
322
  renderSkillsOption(professionPackages, {
323
323
  id: groupId,
324
324
  nameOfSkillsOfGroup: groupTranslation?.longName ?? MISSING_VALUE,
@@ -330,11 +330,11 @@ const renderSkills = (professionPackages) => Reader.asks(({ getAllInstances }) =
330
330
  const renderSpellworkName = (spellworkIds) => Reader.traverse(spellworkIds, spellworkId => {
331
331
  switch (spellworkId.kind) {
332
332
  case "Spellwork":
333
- return strictNameR(spellworkId.Spellwork.id).thenW(baseName => (spellworkId.Spellwork.tradition === undefined
333
+ return attributedNameR("profession", spellworkId.Spellwork.id).thenW(baseName => (spellworkId.Spellwork.tradition === undefined
334
334
  ? Reader.of(undefined)
335
- : nameR("MagicalTradition", spellworkId.Spellwork.tradition)).map(traditionName => baseName + parensIf(traditionName)));
335
+ : attributedNameR("profession", "MagicalTradition", spellworkId.Spellwork.tradition)).map(traditionName => (baseName ?? MISSING_VALUE) + parensIf(traditionName)));
336
336
  case "MagicalAction":
337
- return strictNameR(spellworkId.MagicalAction.id);
337
+ return attributedNameR("profession", spellworkId.MagicalAction.id).map(name => name ?? MISSING_VALUE);
338
338
  default:
339
339
  return spellworkId;
340
340
  }
@@ -358,7 +358,7 @@ const retrieveBlessedTraditionIdentifierFromPrerequisites = (prerequisites) => p
358
358
  return assertExhaustive(part);
359
359
  }
360
360
  }) ?? [];
361
- const renderBlessingList = (blessings) => Reader.traverse(blessings, id => strictNameR("Blessing", id)).thenW(localeSortR);
361
+ const renderBlessingList = (blessings) => Reader.traverse(blessings, id => attributedNameR("profession", "Blessing", id).map(name => name ?? MISSING_VALUE)).thenW(localeSortR);
362
362
  const renderRestrictedBlessings = (restrictedBlessings) => {
363
363
  switch (restrictedBlessings.kind) {
364
364
  case "Three":
@@ -406,7 +406,7 @@ const renderBlessingsForPackages = (professionPackages) => renderBlessings(profe
406
406
  const renderBlessingsForVariant = (professionVariant) => renderBlessings([
407
407
  retrieveBlessedTraditionIdentifierFromPrerequisites(professionVariant.prerequisites),
408
408
  ]).map(blessingsText => (blessingsText !== undefined ? [blessingsText] : []));
409
- const renderLiturgicalChantName = (liturgyIds) => Reader.traverse(liturgyIds, strictNameR).thenW(list => localeJoinR(list, "disjunction"));
409
+ const renderLiturgicalChantName = (liturgyIds) => Reader.traverse(liturgyIds, id => attributedNameR("profession", id).map(name => name ?? MISSING_VALUE)).thenW(list => localeJoinR(list, "disjunction"));
410
410
  const renderLiturgicalChants = (professionPackages) => Reader.sequence([
411
411
  renderBlessingsForPackages(professionPackages),
412
412
  renderNumericListAcrossPackages(professionPackages, (pkg) => Reader.of(pkg.content.liturgical_chants?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, renderLiturgicalChantName, 0),
@@ -7,13 +7,14 @@ import { Reader } from "@elyukai/utils/reader";
7
7
  import { sign } from "@elyukai/utils/string/number";
8
8
  import { createEntityDescriptionCreator } from "../creator.js";
9
9
  import { renderCommonnessRatedAdvantagesAndDisadvantages, renderCommonnessRatedAdvantagesOrDisadvantages, renderValueWithPossibleTranslation, } from "./partial/commonnessRatedAdvantagesAndDisadvantages.js";
10
+ import { attributedCustomName, attributedName } from "./partial/markdown.js";
10
11
  import { MISSING_VALUE } from "./partial/unknown.js";
11
12
  const renderBaseValueItem = (label, value) => Reader.asks(({ translate }) => ({
12
13
  label: translate(label),
13
14
  value: value < 0 ? sign(value) : value.toFixed(),
14
15
  }));
15
16
  const renderAttributeAdjustmentsItem = (totalAttributesCount, adjustments) => Reader.asks(({ translate, translateMap, localeJoin, getInstanceById, }) => {
16
- const getAttributeAbbreviation = (id) => translateMap(getInstanceById("Attribute", id)?.translations)?.abbreviation ?? MISSING_VALUE;
17
+ const getAttributeAbbreviation = (id) => attributedCustomName(translateMap, getInstanceById, "race", t => t.abbreviation, "Attribute", id) ?? MISSING_VALUE;
17
18
  return {
18
19
  label: translate("Attribute Adjustments"),
19
20
  value: [
@@ -69,16 +70,13 @@ const renderVariantValues = (label, variants, selector, renderValue, translation
69
70
  const renderAutomaticAdvantagesOrDisadvantages = (entity, items) => Reader.asks(({ translate, translateMap, getInstanceById, localeCompare }) => items === undefined || !isNotEmpty(items)
70
71
  ? translate("none")
71
72
  : items
72
- .map(item => {
73
- const instanceTranslation = translateMap(getInstanceById(entity, item.id)?.translations);
74
- return (instanceTranslation?.name_in_library ?? instanceTranslation?.name ?? MISSING_VALUE);
75
- })
73
+ .map(item => attributedCustomName(translateMap, getInstanceById, "race", t => t.name_in_library ?? t.name, entity, item.id) ?? MISSING_VALUE)
76
74
  .toSorted(localeCompare)
77
75
  .join(", "));
78
76
  const renderCommonCultures = (items) => Reader.asks(({ translate, translateMap, getInstanceById, localeCompare }) => items === undefined || !isNotEmpty(items)
79
77
  ? translate("none")
80
78
  : items
81
- .map(itemId => translateMap(getInstanceById("Culture", itemId)?.translations)?.name)
79
+ .map(itemId => attributedName(translateMap, getInstanceById, "race", "Culture", itemId))
82
80
  .filter(isNotNullish)
83
81
  .toSorted(localeCompare)
84
82
  .join(", "));
@@ -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
- const text = (() => {
33
- const staticEntry = getInstanceById("Property", id);
34
- const staticEntryTranslation = translateMap(staticEntry?.translations);
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(deps.getInstanceById("MagicalTradition", trad)?.translations))
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(getInstanceById("Curriculum", academyOrTradition.Academy)?.translations)?.name;
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(getInstanceById("Skill", id)?.translations)?.name ?? MISSING_VALUE)
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(getInstanceById(entity, trad.id)?.translations)?.name)
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(getInstanceById("Property", property.Fixed)?.translations)?.name ??
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(getInstanceById("Tribe", id)?.translations)?.name)
564
+ .map(id => attributedName(translateMap, getInstanceById, "tribe", "Tribe", id))
577
565
  .filter(isNotNullish)
578
566
  .toSorted(localeCompare))?.join(", ") ?? MISSING_VALUE);
579
567
  };