@optolith/entity-descriptions 0.3.2 → 0.4.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 (93) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/lib/creator.d.ts +2 -2
  3. package/lib/creator.js +1 -1
  4. package/lib/entities/activatable.js +35 -64
  5. package/lib/entities/culture.js +18 -26
  6. package/lib/entities/curriculum.js +30 -33
  7. package/lib/entities/derivedCharacteristic.js +1 -1
  8. package/lib/entities/disease.js +3 -6
  9. package/lib/entities/elixir.js +6 -4
  10. package/lib/entities/equipment.js +17 -17
  11. package/lib/entities/equipmentPackage.js +4 -3
  12. package/lib/entities/experienceLevel.js +7 -7
  13. package/lib/entities/focusRule.js +2 -2
  14. package/lib/entities/influence.js +2 -2
  15. package/lib/entities/liturgicalChant.d.ts +1 -1
  16. package/lib/entities/partial/adventurePointsValue.js +14 -19
  17. package/lib/entities/partial/enhancements.d.ts +1 -1
  18. package/lib/entities/partial/enhancements.js +1 -1
  19. package/lib/entities/partial/herbary.js +13 -9
  20. package/lib/entities/partial/map.d.ts +3 -1
  21. package/lib/entities/partial/map.js +21 -8
  22. package/lib/entities/partial/mathOperation.js +12 -17
  23. package/lib/entities/partial/prerequisites/displayOption.d.ts +2 -2
  24. package/lib/entities/partial/prerequisites/index.d.ts +3 -3
  25. package/lib/entities/partial/prerequisites/index.js +10 -4
  26. package/lib/entities/partial/prerequisites/part.d.ts +1 -1
  27. package/lib/entities/partial/prerequisites/prerequisiteGroups.d.ts +4 -4
  28. package/lib/entities/partial/prerequisites/prerequisiteGroups.js +1 -1
  29. package/lib/entities/partial/prerequisites/single/activatable.d.ts +3 -3
  30. package/lib/entities/partial/prerequisites/single/animistPower.d.ts +2 -2
  31. package/lib/entities/partial/prerequisites/single/animistPower.js +1 -1
  32. package/lib/entities/partial/prerequisites/single/blessedTradition.d.ts +2 -2
  33. package/lib/entities/partial/prerequisites/single/commonSuggestedByRCP.d.ts +2 -2
  34. package/lib/entities/partial/prerequisites/single/culture.d.ts +4 -4
  35. package/lib/entities/partial/prerequisites/single/enhancement.d.ts +4 -4
  36. package/lib/entities/partial/prerequisites/single/influence.d.ts +3 -3
  37. package/lib/entities/partial/prerequisites/single/magicalTradition.d.ts +2 -2
  38. package/lib/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.d.ts +2 -2
  39. package/lib/entities/partial/prerequisites/single/pact.d.ts +3 -3
  40. package/lib/entities/partial/prerequisites/single/personalityTrait.d.ts +4 -4
  41. package/lib/entities/partial/prerequisites/single/primaryAttribute.d.ts +3 -3
  42. package/lib/entities/partial/prerequisites/single/primaryAttribute.js +1 -1
  43. package/lib/entities/partial/prerequisites/single/profession.d.ts +4 -4
  44. package/lib/entities/partial/prerequisites/single/publication.d.ts +4 -4
  45. package/lib/entities/partial/prerequisites/single/race.d.ts +4 -4
  46. package/lib/entities/partial/prerequisites/single/rated.d.ts +2 -2
  47. package/lib/entities/partial/prerequisites/single/rated.js +2 -2
  48. package/lib/entities/partial/prerequisites/single/ratedMinimumNumber.d.ts +3 -3
  49. package/lib/entities/partial/prerequisites/single/ratedMinimumNumber.js +1 -1
  50. package/lib/entities/partial/prerequisites/single/ratedSum.d.ts +4 -4
  51. package/lib/entities/partial/prerequisites/single/rule.d.ts +3 -3
  52. package/lib/entities/partial/prerequisites/single/sex.d.ts +1 -1
  53. package/lib/entities/partial/prerequisites/single/sexualCharacteristic.d.ts +3 -3
  54. package/lib/entities/partial/prerequisites/single/socialStatus.d.ts +3 -3
  55. package/lib/entities/partial/prerequisites/single/state.d.ts +4 -4
  56. package/lib/entities/partial/prerequisites/single/text.d.ts +3 -3
  57. package/lib/entities/partial/rated/activatable/castingTime.d.ts +1 -1
  58. package/lib/entities/partial/rated/activatable/castingTime.js +6 -8
  59. package/lib/entities/partial/rated/activatable/cost.d.ts +1 -1
  60. package/lib/entities/partial/rated/activatable/cost.js +17 -14
  61. package/lib/entities/partial/rated/activatable/duration.js +4 -4
  62. package/lib/entities/partial/rated/activatable/effect.js +1 -1
  63. package/lib/entities/partial/rated/activatable/isMinimumMaximum.js +2 -2
  64. package/lib/entities/partial/rated/activatable/range.js +6 -6
  65. package/lib/entities/partial/rated/activatable/speed.d.ts +1 -1
  66. package/lib/entities/partial/rated/activatable/targetCategory.js +2 -2
  67. package/lib/entities/partial/reader.d.ts +4 -2
  68. package/lib/entities/partial/responsiveText.d.ts +5 -1
  69. package/lib/entities/partial/responsiveText.js +3 -5
  70. package/lib/entities/partial/units/length.d.ts +1 -1
  71. package/lib/entities/partial/units/length.js +2 -10
  72. package/lib/entities/partial/units/timeSpan.d.ts +4 -4
  73. package/lib/entities/partial/units/timeSpan.js +21 -17
  74. package/lib/entities/personalityTrait.js +3 -4
  75. package/lib/entities/poison.js +4 -4
  76. package/lib/entities/profession.d.ts +1 -1
  77. package/lib/entities/profession.js +19 -14
  78. package/lib/entities/race.js +11 -13
  79. package/lib/entities/sexPractice.js +1 -3
  80. package/lib/entities/skill.js +2 -3
  81. package/lib/entities/spell.js +14 -4
  82. package/lib/helpers/getTypes.d.ts +2 -2
  83. package/lib/helpers/locale.d.ts +8 -2
  84. package/lib/helpers/translate.d.ts +1 -1
  85. package/lib/index.d.ts +11 -2
  86. package/lib/index.js +5 -0
  87. package/lib/references/index.d.ts +1 -1
  88. package/lib/references/index.js +1 -1
  89. package/lib/references/page.d.ts +3 -3
  90. package/lib/references/page.js +1 -1
  91. package/lib/references/pageRange.d.ts +2 -2
  92. package/lib/tsconfig.tsbuildinfo +1 -1
  93. package/package.json +4 -3
@@ -1,7 +1,7 @@
1
1
  import { mapNullable } from "@elyukai/utils/nullable";
2
2
  import { sign } from "@elyukai/utils/string/number";
3
3
  import { createEntityDescriptionCreator } from "../creator.js";
4
- import { renderAlternativeNames, renderLaboratoryLevel, } from "./partial/herbary.js";
4
+ import { renderAlternativeNames, renderLaboratoryLevel } from "./partial/herbary.js";
5
5
  import { printPlainGeneralPrerequisites } from "./partial/prerequisites/index.js";
6
6
  import { parensIf } from "./partial/rated/activatable/parensIf.js";
7
7
  /**
@@ -27,7 +27,9 @@ export const getElixirEntityDescription = createEntityDescriptionCreator(({ getI
27
27
  },
28
28
  {
29
29
  label: translate("Price of Ingredients/Level"),
30
- value: translate(".input {$value :number} {{{$value} silverthalers}}", { value: entry.cost_per_ingredient_level }),
30
+ value: translate(".input {$value :number} {{{$value} silverthalers}}", {
31
+ value: entry.cost_per_ingredient_level,
32
+ }),
31
33
  },
32
34
  {
33
35
  label: translate("Laboratory"),
@@ -39,7 +41,7 @@ export const getElixirEntityDescription = createEntityDescriptionCreator(({ getI
39
41
  },
40
42
  {
41
43
  label: `${translate("Prerequisites")} (${translate("Brewing Process")})`,
42
- value: translation?.brewing_process_prerequisites ?? translate("none"),
44
+ value: translation.brewing_process_prerequisites ?? translate("none"),
43
45
  },
44
46
  {
45
47
  label: `${translate("AP Value")} (${translate("Trade Secret")})`,
@@ -61,7 +63,7 @@ export const getElixirEntityDescription = createEntityDescriptionCreator(({ getI
61
63
  type: "definitionList",
62
64
  style: "nested",
63
65
  items: translation.quality_levels.map((effectForLevel, index) => ({
64
- label: (index + 1).toString(),
66
+ label: (index + 1).toFixed(),
65
67
  value: effectForLevel,
66
68
  })),
67
69
  },
@@ -66,15 +66,17 @@ const renderPrimaryAttributeAndDamageThreshold = (translateMap, getInstanceById,
66
66
  const getAttrAbbrv = (attrId) => translateMap(getInstanceById("Attribute", attrId)?.translations)?.abbreviation ?? MISSING_VALUE;
67
67
  switch (damageThreshold.kind) {
68
68
  case "Default":
69
- return `${closeCombatTechnique?.primary_attribute.map(getAttrAbbrv).join("/") ?? MISSING_VALUE} ${damageThreshold.Default.threshold}`;
69
+ return `${closeCombatTechnique?.primary_attribute.map(getAttrAbbrv).join("/") ?? MISSING_VALUE} ${damageThreshold.Default.threshold.toFixed()}`;
70
70
  case "List":
71
71
  if (isNotEmpty(damageThreshold.List.list)) {
72
72
  const { list } = damageThreshold.List;
73
73
  if (list.some(item => item.threshold !== list[0].threshold)) {
74
- return list.map(item => `${getAttrAbbrv(item.attribute)} ${item.threshold}`).join("/");
74
+ return list
75
+ .map(item => `${getAttrAbbrv(item.attribute)} ${item.threshold.toFixed()}`)
76
+ .join("/");
75
77
  }
76
78
  else {
77
- return `${list.map(item => getAttrAbbrv(item.attribute)).join("/")} ${list[0].threshold}`;
79
+ return `${list.map(item => getAttrAbbrv(item.attribute)).join("/")} ${list[0].threshold.toFixed()}`;
78
80
  }
79
81
  }
80
82
  else {
@@ -150,11 +152,9 @@ const renderReloadTime = (translate, reloadTime) => isNotEmpty(reloadTime)
150
152
  ? translate("{$value} actions", {
151
153
  value: reloadTime.map(time => time.value).join("/"),
152
154
  })
153
- : translate(".input {$value :number} {{{$value} actions}}", {
154
- value: reloadTime[0].value,
155
- })
155
+ : formatTimeSpan(translate, ResponsiveTextSize.Full, "Actions", reloadTime[0].value)
156
156
  : MISSING_VALUE;
157
- const renderRangeBrackets = (rangeBrackets) => `${rangeBrackets.close}/${rangeBrackets.medium}/${rangeBrackets.far}`;
157
+ const renderRangeBrackets = (rangeBrackets) => `${rangeBrackets.close.toFixed()}/${rangeBrackets.medium.toFixed()}/${rangeBrackets.far.toFixed()}`;
158
158
  const renderAmmunition = (translateMap, getInstanceById, ammunition) => ammunition === undefined
159
159
  ? "—"
160
160
  : (translateMap(getInstanceById("Ammunition", ammunition)?.translations)?.name ?? MISSING_VALUE);
@@ -320,7 +320,7 @@ const renderNote = (translate, translateMap, getInstanceById, localeJoin, melee_
320
320
  : renderMagicalTraditionRestriction(translate, translateMap, getInstanceById, localeJoin, name, restrictedTo.magicalTraditions),
321
321
  note,
322
322
  ].filter(isNotNullish))?.join("; ");
323
- const renderWeight = (translate, measurements, weight) => {
323
+ const renderWeight = (translate, formatNumber, measurements, weight) => {
324
324
  if (typeof weight === "number") {
325
325
  return {
326
326
  label: translate("Weight"),
@@ -334,18 +334,18 @@ const renderWeight = (translate, measurements, weight) => {
334
334
  label: translate("Weight (Bronze/Silver/Gold)"),
335
335
  value: translate("{$value} pounds", {
336
336
  value: [weight.bronze, weight.silver, weight.gold]
337
- .map(value => value * measurements.stonesMultiplier)
337
+ .map(value => formatNumber(value * measurements.stonesMultiplier))
338
338
  .join("/"),
339
339
  }),
340
340
  };
341
341
  }
342
342
  };
343
- const renderCost = (translate, translateMap, cost) => {
343
+ const renderCost = (translate, translateMap, formatNumber, cost) => {
344
344
  const renderBookCostVariant = (bookCostVariant) => {
345
345
  switch (bookCostVariant.kind) {
346
346
  case "Definite": {
347
347
  const translation = translateMap(bookCostVariant.Definite.translations);
348
- return (renderCost(translate, translateMap, bookCostVariant.Definite.cost) +
348
+ return (renderCost(translate, translateMap, formatNumber, bookCostVariant.Definite.cost).value +
349
349
  parensIf(translation?.label));
350
350
  }
351
351
  case "Indefinite": {
@@ -360,7 +360,7 @@ const renderCost = (translate, translateMap, cost) => {
360
360
  return {
361
361
  label: translate("Cost (Bronze/Silver/Gold)"),
362
362
  value: translate("{$value} silverthalers", {
363
- value: [cost.bronze, cost.silver, cost.gold].join("/"),
363
+ value: [cost.bronze, cost.silver, cost.gold].map(formatNumber).join("/"),
364
364
  }),
365
365
  };
366
366
  }
@@ -404,8 +404,8 @@ const renderCost = (translate, translateMap, cost) => {
404
404
  }
405
405
  };
406
406
  const renderArmorValues = (translate, translateMap, localeCompare, getInstanceById, idMap, values) => [
407
- { label: translate("Protection"), value: values.protection.toString() },
408
- { label: translate("Encumbrance"), value: values.encumbrance.toString() },
407
+ { label: translate("Protection"), value: values.protection.toFixed() },
408
+ { label: translate("Encumbrance"), value: values.encumbrance.toFixed() },
409
409
  {
410
410
  label: translate("Additional Penalties"),
411
411
  value: values.has_additional_penalties
@@ -525,7 +525,7 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
525
525
  baseItemTranslation?.language !== undefined || baseItemTranslation?.script !== undefined
526
526
  ? {
527
527
  label: translate("Language/Script"),
528
- value: [baseItemTranslation?.language, baseItemTranslation?.script]
528
+ value: [baseItemTranslation.language, baseItemTranslation.script]
529
529
  .map(value => value ?? "—")
530
530
  .join(" / "),
531
531
  }
@@ -542,8 +542,8 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
542
542
  }),
543
543
  }
544
544
  : undefined,
545
- mapNullable(baseItem.weight, weight => renderWeight(translate, locale.measurementAdjustments, weight)),
546
- mapNullable(baseItem.cost, cost => renderCost(translate, translateMap, cost)),
545
+ mapNullable(baseItem.weight, weight => renderWeight(translate, locale.formatNumber, locale.measurementAdjustments, weight)),
546
+ mapNullable(baseItem.cost, cost => renderCost(translate, translateMap, locale.formatNumber, cost)),
547
547
  mapNullable(renderNote(translate, translateMap, getInstanceById, locale.join, combatValues?.type === "Weapon" ? combatValues.values.melee_uses : undefined, combatValues?.values.restrictedTo, name, baseItemTranslation?.note), note => ({
548
548
  label: translate("Note"),
549
549
  value: note,
@@ -115,7 +115,7 @@ const getAtomicEquipmentCost = (entry) => {
115
115
  entry.content.cost.silver,
116
116
  entry.content.cost.gold,
117
117
  ];
118
- return costs.map(getAtomicCost).reduce(rangeAtomicEquipmentCost);
118
+ return costs.reduce(rangeAtomicEquipmentCost);
119
119
  }
120
120
  case "Ammunition":
121
121
  case "Animal":
@@ -188,7 +188,8 @@ const getAtomicEquipmentWeight = (entry) => {
188
188
  return assertExhaustive(entry.content.type);
189
189
  }
190
190
  case "Jewelry": {
191
- const values = Object.values(entry.content.weight);
191
+ const { bronze, silver, gold } = entry.content.weight;
192
+ const values = [bronze, silver, gold];
192
193
  return [Math.min(...values), Math.max(...values)];
193
194
  }
194
195
  case "Armor":
@@ -269,7 +270,7 @@ export const getEquipmentPackageEntityDescription = createEntityDescriptionCreat
269
270
  ...item,
270
271
  content: { entity: item.id.kind, content: getInstanceById(item.id) },
271
272
  }))
272
- .filter((item) => item.content !== undefined) ?? [];
273
+ .filter((item) => item.content.content !== undefined) ?? [];
273
274
  return {
274
275
  title: translation.name,
275
276
  className: "equipment-package",
@@ -16,31 +16,31 @@ export const getExperienceLevelEntityDescription = createEntityDescriptionCreato
16
16
  items: [
17
17
  {
18
18
  label: translate("Adventure Points"),
19
- value: entry.adventure_points.toString(),
19
+ value: entry.adventure_points.toFixed(),
20
20
  },
21
21
  {
22
22
  label: translate("Maximum Attribute Value"),
23
- value: entry.max_attribute_value.toString(),
23
+ value: entry.max_attribute_value.toFixed(),
24
24
  },
25
25
  {
26
26
  label: translate("Maximum Skill Value"),
27
- value: entry.max_skill_rating.toString(),
27
+ value: entry.max_skill_rating.toFixed(),
28
28
  },
29
29
  {
30
30
  label: translate("Maximum Combat Technique"),
31
- value: entry.max_combat_technique_rating.toString(),
31
+ value: entry.max_combat_technique_rating.toFixed(),
32
32
  },
33
33
  {
34
34
  label: translate("Maximum Attribute Total"),
35
- value: entry.max_attribute_total.toString(),
35
+ value: entry.max_attribute_total.toFixed(),
36
36
  },
37
37
  {
38
38
  label: translate("Number of Spells/Liturgical Chants"),
39
- value: entry.max_number_of_spells_liturgical_chants.toString(),
39
+ value: entry.max_number_of_spells_liturgical_chants.toFixed(),
40
40
  },
41
41
  {
42
42
  label: translate("Number from other Traditions"),
43
- value: entry.max_number_of_unfamiliar_spells.toString(),
43
+ value: entry.max_number_of_unfamiliar_spells.toFixed(),
44
44
  },
45
45
  ],
46
46
  },
@@ -10,8 +10,8 @@ export const getFocusRuleEntityDescription = createEntityDescriptionCreator(({ g
10
10
  return undefined;
11
11
  }
12
12
  return {
13
- title: `${translation.name} (${romanize(entry.level)})`,
14
- subtitle: topicTranslation.name,
13
+ title: translation.name,
14
+ badge: entry.level,
15
15
  className: "focus-rule",
16
16
  body: [
17
17
  {
@@ -24,10 +24,10 @@ export const getInfluenceEntityDescription = createEntityDescriptionCreator(({ g
24
24
  : {
25
25
  type: "definitionList",
26
26
  items: [
27
- ...(translation.effects?.map(effect => ({
27
+ ...translation.effects.map(effect => ({
28
28
  label: effect.label,
29
29
  value: effect.text,
30
- })) ?? []),
30
+ })),
31
31
  entry.prerequisites === undefined
32
32
  ? undefined
33
33
  : {
@@ -1,6 +1,6 @@
1
1
  import { type RatedIdentifier } from "optolith-database-schema/gen";
2
2
  import type { GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js";
3
- import { type IdMap } from "../index.js";
3
+ import type { IdMap } from "../index.js";
4
4
  /**
5
5
  * Get a JSON representation of the rules text for a blessing.
6
6
  */
@@ -19,8 +19,8 @@ const renderSelectOptionsAdventurePointsValue = (locale, derivedLabel, getAllSel
19
19
  }
20
20
  case "Fixed":
21
21
  return Map.groupBy(getAllSelectOptions().map((item) => [
22
- config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))
23
- ?.ap_value ?? config.Fixed.default,
22
+ config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))?.ap_value ??
23
+ config.Fixed.default,
24
24
  item,
25
25
  ]), item => item[0])
26
26
  .entries()
@@ -52,10 +52,14 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
52
52
  switch (value.kind) {
53
53
  case "Fixed": {
54
54
  if (entry.levels === undefined) {
55
- return translate(".input {$value :number} {{{$value} Adventure Points}}", { value: value.Fixed });
55
+ return translate(".input {$value :number} {{{$value} Adventure Points}}", {
56
+ value: value.Fixed,
57
+ });
56
58
  }
57
59
  else {
58
- return translate(".input {$value :number} {{{$value} Adventure Points per level}}", { value: value.Fixed });
60
+ return translate(".input {$value :number} {{{$value} Adventure Points per level}}", {
61
+ value: value.Fixed,
62
+ });
59
63
  }
60
64
  }
61
65
  case "ByLevel": {
@@ -70,19 +74,11 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
70
74
  return mainValue;
71
75
  }
72
76
  else {
73
- const sizeCategories = [
74
- "tiny",
75
- "small",
76
- "medium",
77
- "large",
78
- "huge",
79
- ];
77
+ const sizeCategories = ["tiny", "small", "medium", "large", "huge"];
80
78
  const values = sizeCategories
81
79
  .map(sizeCategory => additionalBySizeCategory[sizeCategory])
82
80
  .join("/");
83
- const labels = sizeCategories
84
- .map(sizeCategory => translate(sizeCategory))
85
- .join("/");
81
+ const labels = sizeCategories.map(sizeCategory => translate(sizeCategory)).join("/");
86
82
  return `${mainValue} + ${translate("{$values} AP for size category {$labels} (per level)", { values, labels })}`;
87
83
  }
88
84
  }
@@ -111,8 +107,7 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
111
107
  return translate("{$values} adventure points for a {$sized} animal shape", {
112
108
  values: sizes.map(size => size.content.ap_value).join("/"),
113
109
  sized: sizes
114
- .map(size => translateMap(size.content.translations)?.name ??
115
- MISSING_VALUE)
110
+ .map(size => translateMap(size.content.translations)?.name ?? MISSING_VALUE)
116
111
  .join("/"),
117
112
  });
118
113
  }
@@ -158,6 +153,7 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
158
153
  return renderSelectOptionsAdventurePointsValue(locale, () => {
159
154
  switch (derivedSelectOptions.Skills.categories.length) {
160
155
  case 1: {
156
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ensured by the schema
161
157
  const first = derivedSelectOptions.Skills.categories[0];
162
158
  switch (first.kind) {
163
159
  case "Skills":
@@ -177,8 +173,7 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
177
173
  case 2:
178
174
  return derivedSelectOptions.Skills.categories.every(category => category.kind === "Spells" || category.kind === "Rituals")
179
175
  ? translate("A/B/C/D spellwork")
180
- : derivedSelectOptions.Skills.categories.every(category => category.kind === "LiturgicalChants" ||
181
- category.kind === "Ceremonies")
176
+ : derivedSelectOptions.Skills.categories.every(category => category.kind === "LiturgicalChants" || category.kind === "Ceremonies")
182
177
  ? translate("A/B/C/D liturgical chant or ceremony")
183
178
  : translate("A/B/C/D ability");
184
179
  default:
@@ -218,7 +213,7 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
218
213
  return assertExhaustive(exprValue);
219
214
  }
220
215
  })).join("/");
221
- const labels = Array.from({ length: entry.maximum ?? 3 }, (_, index) => `${index + 1}.`).join("/");
216
+ const labels = Array.from({ length: entry.maximum ?? 3 }, (_, index) => `${(index + 1).toFixed()}.`).join("/");
222
217
  if (entry.maximum !== undefined) {
223
218
  return translate("{$values} Adventure Points for the {$labels} purchase", {
224
219
  values,
@@ -1,4 +1,4 @@
1
- import { RatedIdentifier, type ImprovementCost, type SkillWithEnhancementsIdentifier } from "optolith-database-schema/gen";
1
+ import type { ImprovementCost, RatedIdentifier, SkillWithEnhancementsIdentifier } from "optolith-database-schema/gen";
2
2
  import type { RawEntityDescriptionSection } from "../../index.js";
3
3
  import { type StdReader } from "./reader.js";
4
4
  /**
@@ -10,7 +10,7 @@ import { getChildInstancesForInstanceIdR, translateMapR, translateR, } from "./r
10
10
  */
11
11
  export const renderEnhancements = (parentId, parentImprovementCost) => getChildInstancesForInstanceIdR("Enhancement", parentId).thenW(enhancements => Reader.traverse(enhancements.toSorted(on(e => e.content.skill_rating, compareNumber)), enhancement => translateMapR(enhancement.content.translations).thenW(translation => Reader.ask().map(env => translation === undefined
12
12
  ? undefined
13
- : `- ^[${translation.name}](entity: "Enhancement") (${env.translate("SR {$value}", { value: enhancement.content.skill_rating })}, ${env.translate("{$value} AP", { value: enhancement.content.adventure_points_modifier * getAdventurePointsForActivation(parentImprovementCost.kind) })}): ${translation.effect ?? ""}${enhancement.content.prerequisites === undefined ? "" : ` ${env.translate(".input {$hiddenCount :number} {{Prerequisites}}", { hiddenCount: enhancement.content.prerequisites.length })}: ${printEnhancementPrerequisites(env.getInstanceById, { translate: env.translate, translateMap: env.translateMap, compare: env.localeCompare, join: env.localeJoin }, enhancement.content.prerequisites)}`}`))).thenW(enhancementDescriptions => {
13
+ : `- ^[${translation.name}](entity: "Enhancement") (${env.translate("SR {$value}", { value: enhancement.content.skill_rating })}, ${env.translate("{$value} AP", { value: enhancement.content.adventure_points_modifier * getAdventurePointsForActivation(parentImprovementCost.kind) })}): ${translation.effect}${enhancement.content.prerequisites === undefined ? "" : ` ${env.translate(".input {$hiddenCount :number} {{Prerequisites}}", { hiddenCount: enhancement.content.prerequisites.length })}: ${printEnhancementPrerequisites(env.getInstanceById, { translate: env.translate, translateMap: env.translateMap, compare: env.localeCompare, join: env.localeJoin }, enhancement.content.prerequisites)}`}`))).thenW(enhancementDescriptions => {
14
14
  const nonNullishDescriptions = enhancementDescriptions.filter(isNotNullish);
15
15
  if (nonNullishDescriptions.length === 0) {
16
16
  return Reader.of(undefined);
@@ -24,12 +24,16 @@ export const renderLaboratoryLevel = (translate, level) => {
24
24
  export const renderResistance = (translate, translateMap, getInstanceById, idMap, resistance) => {
25
25
  switch (resistance.kind) {
26
26
  case "Spirit":
27
- return (translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Spirit)?.translations)?.name ?? MISSING_VALUE);
27
+ return (translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Spirit)
28
+ ?.translations)?.name ?? MISSING_VALUE);
28
29
  case "Toughness":
29
- return (translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Toughness)?.translations)?.name ?? MISSING_VALUE);
30
+ return (translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Toughness)
31
+ ?.translations)?.name ?? MISSING_VALUE);
30
32
  case "LowerOfSpiritAndToughness": {
31
- const spiritTranslation = translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Spirit)?.translations)?.name ?? MISSING_VALUE;
32
- const toughnessTranslation = translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Toughness)?.translations)?.name ?? MISSING_VALUE;
33
+ const spiritTranslation = translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Spirit)
34
+ ?.translations)?.name ?? MISSING_VALUE;
35
+ const toughnessTranslation = translateMap(getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Toughness)
36
+ ?.translations)?.name ?? MISSING_VALUE;
33
37
  return translate("{$first} or {$second}, depending on which value is lower", {
34
38
  first: spiritTranslation,
35
39
  second: toughnessTranslation,
@@ -46,7 +50,7 @@ export const renderChance = (translate, translateMap, item, includePercentage =
46
50
  (item.chance === undefined
47
51
  ? undefined
48
52
  : translate("{$valueRange} on {$dice}", {
49
- valueRange: item.chance === 5 ? 1 : `1–${item.chance / 5}`,
53
+ valueRange: item.chance === 5 ? 1 : `1–${(item.chance / 5).toFixed()}`,
50
54
  dice: renderDice(translate, { number: 1, sides: 20 }),
51
55
  }) +
52
56
  (includePercentage
@@ -58,8 +62,8 @@ export const renderChance = (translate, translateMap, item, includePercentage =
58
62
  export const renderAlternativeNames = (translate, alternativeNames) => alternativeNames === undefined || !isNotEmpty(alternativeNames)
59
63
  ? undefined
60
64
  : {
61
- label: translate(".input {$hiddenCount :number} {{Alternative Names}}", { hiddenCount: alternativeNames.length }),
62
- value: alternativeNames
63
- .map(name => name.name + parensIf(name.region))
64
- .join(", "),
65
+ label: translate(".input {$hiddenCount :number} {{Alternative Names}}", {
66
+ hiddenCount: alternativeNames.length,
67
+ }),
68
+ value: alternativeNames.map(name => name.name + parensIf(name.region)).join(", "),
65
69
  };
@@ -1,4 +1,4 @@
1
- import type { ResponsiveTextOptional } from "optolith-database-schema/gen";
1
+ import type { MapStyle, ResponsiveTextOptional } from "optolith-database-schema/gen";
2
2
  import type { LocaleMap, Translate, TranslateMap } from "../../helpers/translate.js";
3
3
  import { type StdReader } from "./reader.js";
4
4
  /**
@@ -6,6 +6,7 @@ import { type StdReader } from "./reader.js";
6
6
  */
7
7
  export declare const renderMap: <M extends {
8
8
  options: MO[];
9
+ style?: MapStyle;
9
10
  translations?: LocaleMap<MT>;
10
11
  }, MOT = M extends {
11
12
  options: {
@@ -28,6 +29,7 @@ export declare const renderMap: <M extends {
28
29
  */
29
30
  export declare const renderResponsiveMap: <M extends {
30
31
  options: MO[];
32
+ style?: MapStyle;
31
33
  translations?: LocaleMap<{
32
34
  list_prepend?: ResponsiveTextOptional;
33
35
  listPrefix?: ResponsiveTextOptional;
@@ -8,14 +8,28 @@ import { MISSING_VALUE } from "./unknown.js";
8
8
  */
9
9
  export const renderMap = (translate, translateMap, map, getValue, surroundValues = values => values, getLabel, getListPrefix, getListSuffix, getReplacement, ...moreToAppend) => {
10
10
  const translation = translateMap(map.translations);
11
- if (translation === undefined) {
12
- return MISSING_VALUE;
13
- }
14
- const replacement = getReplacement(translation);
11
+ const replacement = mapNullable(translation, t => getReplacement(t));
15
12
  if (replacement !== undefined) {
16
13
  return replacement;
17
14
  }
18
- return `${surroundValues(map.options.map(option => getValue(option)).join("/"))} ${translate("for")} ${mapNullableDefault(getListPrefix(translation), listPrefix => `${listPrefix} `, "")}${map.options
15
+ if (map.style?.kind === "Verbose") {
16
+ return map.options
17
+ .map(option => {
18
+ const value = getValue(option);
19
+ const optionTranslation = translateMap(option.translations);
20
+ return `${surroundValues(typeof value === "number" ? value.toFixed() : value)} ${translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${optionTranslation === undefined ? MISSING_VALUE : getLabel(optionTranslation)}${mapNullableDefault(mapNullable(translation, getListSuffix), listSuffix => ` ${listSuffix}`, "")}${moreToAppend
21
+ .filter(isNotNullish)
22
+ .map(({ surround, getAdditionalValue }) => {
23
+ const additionalValue = optionTranslation === undefined
24
+ ? MISSING_VALUE
25
+ : getAdditionalValue(option, optionTranslation);
26
+ return surround(typeof additionalValue === "number" ? additionalValue.toFixed() : additionalValue);
27
+ })
28
+ .join("")}`;
29
+ })
30
+ .join(", ");
31
+ }
32
+ return `${surroundValues(map.options.map(option => getValue(option)).join("/"))} ${translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${map.options
19
33
  .map(option => {
20
34
  const optionTranslation = translateMap(option.translations);
21
35
  if (optionTranslation === undefined) {
@@ -23,7 +37,7 @@ export const renderMap = (translate, translateMap, map, getValue, surroundValues
23
37
  }
24
38
  return getLabel(optionTranslation);
25
39
  })
26
- .join("/")}${mapNullableDefault(getListSuffix(translation), listSuffix => ` ${listSuffix}`, "")}${moreToAppend
40
+ .join("/")}${mapNullableDefault(mapNullable(translation, getListSuffix), listSuffix => ` ${listSuffix}`, "")}${moreToAppend
27
41
  .filter(isNotNullish)
28
42
  .map(({ surround, getAdditionalValue }) => surround(map.options
29
43
  .map(option => {
@@ -39,5 +53,4 @@ export const renderMap = (translate, translateMap, map, getValue, surroundValues
39
53
  /**
40
54
  * Renders a map of options to a string.
41
55
  */
42
- export const renderResponsiveMap = (map, getValue, surroundValues, ...moreToAppend) => Reader.asks(({ translate, translateMap, responsiveTextSize }) => renderMap(translate, translateMap, map, getValue, surroundValues, translation => getResponsiveTextOptional(translation.label, responsiveTextSize) ??
43
- translation.label.full, translation => mapNullable(translation.listPrefix ?? translation.list_prepend, text => getResponsiveTextOptional(text, responsiveTextSize)), translation => mapNullable(translation.listSuffix ?? translation.list_append, text => getResponsiveTextOptional(text, responsiveTextSize)), translation => mapNullable(translation.replacement, text => getResponsiveTextOptional(text, responsiveTextSize)), ...moreToAppend));
56
+ export const renderResponsiveMap = (map, getValue, surroundValues, ...moreToAppend) => Reader.asks(({ translate, translateMap, responsiveTextSize }) => renderMap(translate, translateMap, map, getValue, surroundValues, translation => getResponsiveTextOptional(translation.label, responsiveTextSize) ?? translation.label.full, translation => mapNullable(translation.listPrefix ?? translation.list_prepend, text => getResponsiveTextOptional(text, responsiveTextSize)), translation => mapNullable(translation.listSuffix ?? translation.list_append, text => getResponsiveTextOptional(text, responsiveTextSize)), translation => mapNullable(translation.replacement, text => getResponsiveTextOptional(text, responsiveTextSize)), ...moreToAppend));
@@ -1,45 +1,44 @@
1
1
  import { assertExhaustive } from "@optolith/helpers/typeSafety";
2
+ const printOperand = (operand) => typeof operand === "number" ? operand.toFixed() : operand;
2
3
  /**
3
4
  * Typographic formatter for addition.
4
5
  */
5
6
  export const additionFormatter = (left, right) =>
6
7
  // eslint-disable-next-line no-irregular-whitespace
7
- `${left} + ${right}`;
8
+ `${printOperand(left)} + ${printOperand(right)}`;
8
9
  /**
9
10
  * Typographic formatter for subtraction.
10
11
  */
11
12
  export const subtractionFormatter = (left, right) =>
12
13
  // eslint-disable-next-line no-irregular-whitespace
13
- `${left} − ${right}`;
14
+ `${printOperand(left)} − ${printOperand(right)}`;
14
15
  /**
15
16
  * Typographic formatter for multiplication.
16
17
  */
17
18
  export const multiplicationFormatter = (left, right) =>
18
19
  // eslint-disable-next-line no-irregular-whitespace
19
- `${left} × ${right}`;
20
+ `${printOperand(left)} × ${printOperand(right)}`;
20
21
  /**
21
22
  * Typographic formatter for division.
22
23
  */
23
24
  export const divisionFormatter = (left, right) =>
24
25
  // eslint-disable-next-line no-irregular-whitespace
25
- `${left} / ${right}`;
26
+ `${printOperand(left)} / ${printOperand(right)}`;
26
27
  /**
27
28
  * Typographic formatter for exponentiation. Uses Markdown syntax.
28
29
  */
29
- export const exponentiationFormatter = (left, right) => `${left}^${right}^`;
30
+ export const exponentiationFormatter = (left, right) => `${printOperand(left)}^${printOperand(right)}^`;
30
31
  /**
31
32
  * Typographic formatter for grouping (parentheses).
32
33
  */
33
- export const groupFormatter = value => `(${value})`;
34
+ export const groupFormatter = value => `(${printOperand(value)})`;
34
35
  /**
35
36
  * Render a math operation as a string, using the provided function to render the values.
36
37
  */
37
38
  export const renderMathOperation = (operation, renderValue) => {
38
39
  const renderWithParenthesis = (op, addParenthesisTo = []) => {
39
40
  const rendered = renderMathOperation(op, renderValue);
40
- return addParenthesisTo.includes(op.kind)
41
- ? groupFormatter(rendered)
42
- : rendered;
41
+ return addParenthesisTo.includes(op.kind) ? groupFormatter(rendered) : rendered;
43
42
  };
44
43
  const renderBinary = ([left, right], options) => options.formatter(renderWithParenthesis(left, options.addParenthesisTo), renderWithParenthesis(right, options.addParenthesisToRight ?? options.addParenthesisTo));
45
44
  switch (operation.kind) {
@@ -79,23 +78,19 @@ export const evaluateMathOperation = (operation, evaluateValue) => {
79
78
  return evaluateValue(operation.Value);
80
79
  case "Addition": {
81
80
  const [left, right] = operation.Addition;
82
- return (evaluateMathOperation(left, evaluateValue) +
83
- evaluateMathOperation(right, evaluateValue));
81
+ return (evaluateMathOperation(left, evaluateValue) + evaluateMathOperation(right, evaluateValue));
84
82
  }
85
83
  case "Subtraction": {
86
84
  const [left, right] = operation.Subtraction;
87
- return (evaluateMathOperation(left, evaluateValue) -
88
- evaluateMathOperation(right, evaluateValue));
85
+ return (evaluateMathOperation(left, evaluateValue) - evaluateMathOperation(right, evaluateValue));
89
86
  }
90
87
  case "Multiplication": {
91
88
  const [left, right] = operation.Multiplication;
92
- return (evaluateMathOperation(left, evaluateValue) *
93
- evaluateMathOperation(right, evaluateValue));
89
+ return (evaluateMathOperation(left, evaluateValue) * evaluateMathOperation(right, evaluateValue));
94
90
  }
95
91
  case "Division": {
96
92
  const [left, right] = operation.Division;
97
- return (evaluateMathOperation(left, evaluateValue) /
98
- evaluateMathOperation(right, evaluateValue));
93
+ return (evaluateMathOperation(left, evaluateValue) / evaluateMathOperation(right, evaluateValue));
99
94
  }
100
95
  case "Exponentiation": {
101
96
  const [left, right] = operation.Exponentiation;
@@ -1,6 +1,6 @@
1
- import { DisplayOption } from "optolith-database-schema/gen";
1
+ import type { DisplayOption } from "optolith-database-schema/gen";
2
2
  import type { TranslateMap } from "../../../helpers/translate.js";
3
- import { PrerequisitePart } from "./part.js";
3
+ import type { PrerequisitePart } from "./part.js";
4
4
  /**
5
5
  * Get the translation of a display option.
6
6
  */
@@ -1,7 +1,7 @@
1
- import { AdvantageDisadvantagePrerequisites, AnimistPowerPrerequisites, ArcaneTraditionPrerequisites, DerivedCharacteristicPrerequisites, EnhancementPrerequisites, GeneralPrerequisites, GeodeRitualPrerequisites, InfluencePrerequisites, LanguagePrerequisites, LiturgyPrerequisites, PersonalityTraitPrerequisites, PlainGeneralPrerequisites, ProfessionPrerequisites, PublicationPrerequisites, SpecialAbilityIdentifier, SpellworkPrerequisites, type ActivatableIdentifier, type RatedIdentifier } from "optolith-database-schema/gen";
1
+ import type { ActivatableIdentifier, AdvantageDisadvantagePrerequisites, AnimistPowerPrerequisites, ArcaneTraditionPrerequisites, DerivedCharacteristicPrerequisites, EnhancementPrerequisites, GeneralPrerequisites, GeodeRitualPrerequisites, InfluencePrerequisites, LanguagePrerequisites, LiturgyPrerequisites, PersonalityTraitPrerequisites, PlainGeneralPrerequisites, ProfessionPrerequisites, PublicationPrerequisites, RatedIdentifier, SpecialAbilityIdentifier, SpellworkPrerequisites } from "optolith-database-schema/gen";
2
2
  import type { GetAllChildInstancesForParent, GetInstanceById } from "../../../helpers/getTypes.js";
3
- import { LocaleEnvironment } from "../../../helpers/locale.js";
4
- import { GetResolvedSelectOptionById } from "./single/activatable.js";
3
+ import type { LocaleEnvironment } from "../../../helpers/locale.js";
4
+ import type { GetResolvedSelectOptionById } from "./single/activatable.js";
5
5
  /**
6
6
  * Print derived characteristic prerequisites as a string.
7
7
  */
@@ -1,3 +1,4 @@
1
+ import { isNotEmpty } from "@elyukai/utils/array/nonEmpty";
1
2
  import { on } from "@elyukai/utils/function";
2
3
  import { mapNullable } from "@elyukai/utils/nullable";
3
4
  import { numAsc } from "@optolith/helpers/compare";
@@ -18,12 +19,14 @@ const printPrerequisitesDisjunction = (getPrerequisiteTranslation, locale, disju
18
19
  if (disjunction.display_option !== undefined) {
19
20
  return printDisplayOption(locale.translateMap, disjunction.display_option);
20
21
  }
21
- const [first, ...other] = disjunction.list.map(getPrerequisiteTranslation).filter(isNotNullish);
22
+ const { list: disjunctionList } = disjunction;
23
+ const [first, ...other] = disjunctionList.map(getPrerequisiteTranslation).filter(isNotNullish);
22
24
  if (first === undefined) {
23
25
  return undefined;
24
26
  }
25
- if (disjunction.list.length < 2 ||
26
- disjunction.list.slice(1).every(part => part.kind === disjunction.list[0].kind)) {
27
+ if (disjunctionList.length < 2 ||
28
+ (isNotEmpty(disjunctionList) &&
29
+ disjunctionList.slice(1).every(part => part.kind === disjunctionList[0].kind))) {
27
30
  return {
28
31
  label: first.label,
29
32
  value: hasPartValueObject(first) && other.every(hasPartValueObject)
@@ -36,7 +39,10 @@ const printPrerequisitesDisjunction = (getPrerequisiteTranslation, locale, disju
36
39
  };
37
40
  }
38
41
  return {
39
- value: locale.join([first, ...other].map(part => (part.label ?? "") + part.value), "disjunction"),
42
+ value: locale.join([first, ...other].map(part => (part.label ?? "") +
43
+ (typeof part.value === "string"
44
+ ? part.value
45
+ : renderActivatableNameComponents(locale.translateMap, part.value, true))), "disjunction"),
40
46
  sentenceType: undefined,
41
47
  isMeta: false,
42
48
  };
@@ -1,4 +1,4 @@
1
- import { SentenceType } from "optolith-database-schema/gen";
1
+ import type { SentenceType } from "optolith-database-schema/gen";
2
2
  import type { LocaleCompare } from "../../../helpers/locale.js";
3
3
  import type { Translate, TranslateMap } from "../../../helpers/translate.js";
4
4
  import { type ActivatableNameComponents } from "../activatableNameChunks.js";