@optolith/entity-descriptions 0.4.26 → 0.5.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 (29) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/lib/entities/activatable.d.ts +2 -2
  3. package/lib/entities/activatable.js +17 -21
  4. package/lib/entities/equipment.d.ts +2 -2
  5. package/lib/entities/equipment.js +7 -7
  6. package/lib/entities/liturgicalChant.js +7 -3
  7. package/lib/entities/partial/enhancements.js +2 -2
  8. package/lib/entities/partial/map.d.ts +25 -36
  9. package/lib/entities/partial/map.js +16 -15
  10. package/lib/entities/partial/prerequisites/prerequisiteGroups.js +9 -2
  11. package/lib/entities/partial/prerequisites/single/enhancement.d.ts +7 -3
  12. package/lib/entities/partial/prerequisites/single/enhancement.js +19 -25
  13. package/lib/entities/partial/rated/activatable/castingTime.d.ts +5 -5
  14. package/lib/entities/partial/rated/activatable/castingTime.js +6 -3
  15. package/lib/entities/partial/rated/activatable/cost.d.ts +20 -14
  16. package/lib/entities/partial/rated/activatable/cost.js +62 -27
  17. package/lib/entities/partial/rated/activatable/duration.d.ts +3 -3
  18. package/lib/entities/partial/rated/activatable/index.d.ts +6 -6
  19. package/lib/entities/partial/rated/activatable/index.js +4 -4
  20. package/lib/entities/partial/reader.d.ts +10 -0
  21. package/lib/entities/partial/reader.js +5 -1
  22. package/lib/entities/partial/units/timeSpan.d.ts +23 -15
  23. package/lib/entities/partial/units/timeSpan.js +17 -3
  24. package/lib/entities/poison.js +9 -9
  25. package/lib/entities/spell.js +34 -18
  26. package/lib/helpers/enums.d.ts +2 -1
  27. package/lib/helpers/enums.js +1 -3
  28. package/lib/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +11 -10
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [0.5.1](https://github.com/Optolith/entity-descriptions/compare/v0.5.0...v0.5.1) (2026-09-08)
6
+ ## [0.5.0](https://github.com/Optolith/entity-descriptions/compare/v0.4.26...v0.5.0) (2026-09-08)
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * **deps:** upgrade to schema 0.54.0 and other newer dependencies
11
+
12
+ ### build
13
+
14
+ * **deps:** upgrade to schema 0.54.0 and other newer dependencies ([5ebe2df](https://github.com/Optolith/entity-descriptions/commit/5ebe2df9e468da66f792dd6e40e414037f395579))
15
+
16
+ ### Features
17
+
18
+ * attributed names for enhancement prerequisites ([0e8600a](https://github.com/Optolith/entity-descriptions/commit/0e8600ac4999c4c7f83b6c719d8955dcc7f87512))
19
+
20
+ ### Bug Fixes
21
+
22
+ * do not display enchantment parent in parent description ([7e3d5da](https://github.com/Optolith/entity-descriptions/commit/7e3d5daf565eb1729b6f6a31229cd7bfa826bf0d)), closes [#26](https://github.com/Optolith/entity-descriptions/issues/26)
23
+ * indentation for multi-paragraph enhancements ([206f13c](https://github.com/Optolith/entity-descriptions/commit/206f13cc5730148e98c5263cc891a53c10391e3f))
24
+ * sort liturgical chant traditions alphabetically ([2255cd4](https://github.com/Optolith/entity-descriptions/commit/2255cd44f8df474ce4b17b253ead84c4f2c10217)), closes [#35](https://github.com/Optolith/entity-descriptions/issues/35)
5
25
  ## [0.4.26](https://github.com/Optolith/entity-descriptions/compare/v0.4.25...v0.4.26) (2026-06-21)
6
26
 
7
27
 
@@ -1,4 +1,4 @@
1
- import type { ActivatableIdentifier, AdvancedSpecialAbility, AdvantageDisadvantagePrerequisites, AdventurePointsValue, ApplicableCombatTechniques, Aspect_ID, CombatSpecialAbilityUsageType, DaggerRitualCost, EnchantmentCost, Errata, GeneralPrerequisites, MagicalSignCost, Penalty, PropertyDeclaration, PublicationRefs, RatedIdentifier, SelectOptions, SpecialRule, Volume } from "@optolith/database-schema/gen";
1
+ import type { ActivatableIdentifier, AdvancedSpecialAbility, AdvantageDisadvantagePrerequisites, AdventurePointsValue, ApplicableCombatTechniques, Aspect_ID, DaggerRitualCost, EnchantmentCost, Errata, GeneralPrerequisites, MagicalSignCost, Penalty, PropertyDeclaration, PublicationRefs, RatedIdentifier, SelectOptions, SpecialRule, Volume } from "@optolith/database-schema/gen";
2
2
  import { Case } from "tsondb/schema/gen";
3
3
  import type { GetAllInstances, GetInstanceById } from "../helpers/getTypes.js";
4
4
  import type { GetAllResolvedNewSkillApplications, GetAllResolvedSelectOptions, GetAllResolvedSkillUses } from "../index.js";
@@ -12,7 +12,7 @@ export type BaseActivatable = {
12
12
  levels?: number;
13
13
  maximum?: number;
14
14
  select_options?: SelectOptions;
15
- usage_type?: CombatSpecialAbilityUsageType;
15
+ usage_type?: Case<"Passive" | "Active" | "BasicManeuver" | "SpecialManeuver">;
16
16
  advanced?: AdvancedSpecialAbility<AdvancedIdentifierSpecialAbility>[];
17
17
  penalty?: Penalty;
18
18
  combat_techniques?: ApplicableCombatTechniques;
@@ -18,6 +18,7 @@ import { attributedName } from "./partial/markdown.js";
18
18
  import { additionFormatter } from "./partial/mathOperation.js";
19
19
  import { printAdvantageDisadvantagePrerequisites, printGeneralPrerequisites, } from "./partial/prerequisites/index.js";
20
20
  import { printActivatableName, } from "./partial/prerequisites/single/activatable.js";
21
+ import { renderStandaloneCostMap } from "./partial/rated/activatable/cost.js";
21
22
  import { parensIf } from "./partial/rated/activatable/parensIf.js";
22
23
  import { attributedNameR, localeSortR, translateR, } from "./partial/reader.js";
23
24
  import { getResponsiveText, getResponsiveTextOptional, ResponsiveTextSize, } from "./partial/responsiveText.js";
@@ -274,7 +275,7 @@ const renderVolumeValue = (translate, translateMap, getAllResolvedSelectOptions,
274
275
  levels: volume.ByLevel.list.map((_, index) => romanize(index + 1)).join("/"),
275
276
  });
276
277
  case "Map":
277
- return renderResponsiveMap(volume.Map, option => option.points, values => translate("{$points} points", { points: values })).run({
278
+ return renderResponsiveMap(volume.Map, option => Reader.of(option.points), values => translateR("{$points} points", { points: values })).run({
278
279
  translate,
279
280
  translateMap,
280
281
  responsiveTextSize,
@@ -297,7 +298,7 @@ const renderVolumeValue = (translate, translateMap, getAllResolvedSelectOptions,
297
298
  return assertExhaustive(volume);
298
299
  }
299
300
  };
300
- const renderArcaneEnergyCost = (translate, translateMap, localeJoin, responsiveTextSize, levels, cost) => {
301
+ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, responsiveTextSize, levels, cost) => {
301
302
  switch (cost.kind) {
302
303
  case "Fixed": {
303
304
  const translationWrapper = cost.Fixed.is_permanent === true
@@ -310,7 +311,7 @@ const renderArcaneEnergyCost = (translate, translateMap, localeJoin, responsiveT
310
311
  ? str => str
311
312
  : str => translate("{$cost} per {$interval}", {
312
313
  cost: str,
313
- interval: formatTimeSpan(translate, responsiveTextSize, interval.unit, interval.value),
314
+ interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, interval.unit, interval.value),
314
315
  });
315
316
  const wrapInPerLevel = (() => {
316
317
  if (cost.Fixed.per_level === undefined) {
@@ -352,7 +353,7 @@ const renderArcaneEnergyCost = (translate, translateMap, localeJoin, responsiveT
352
353
  case "Interval":
353
354
  return translate("{$cost} per {$interval}", {
354
355
  cost: translate("{$value} AE", { value: cost.Interval.value }),
355
- interval: formatTimeSpan(translate, responsiveTextSize, cost.Interval.interval.unit, cost.Interval.interval.value),
356
+ interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.Interval.interval.unit, cost.Interval.interval.value),
356
357
  });
357
358
  case "ActivationAndHalfInterval":
358
359
  return additionFormatter(translate("{$value} AE", {
@@ -361,7 +362,7 @@ const renderArcaneEnergyCost = (translate, translateMap, localeJoin, responsiveT
361
362
  cost: translate("{$value} AE", {
362
363
  value: Math.round(cost.ActivationAndHalfInterval.value / 2),
363
364
  }),
364
- interval: formatTimeSpan(translate, responsiveTextSize, cost.ActivationAndHalfInterval.interval.unit, cost.ActivationAndHalfInterval.interval.value),
365
+ interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.ActivationAndHalfInterval.interval.unit, cost.ActivationAndHalfInterval.interval.value),
365
366
  }));
366
367
  case "Indefinite":
367
368
  return ((mapNullable(translateMap(cost.Indefinite.translations)?.description, description => getResponsiveText(description, responsiveTextSize)) ?? MISSING_VALUE) +
@@ -372,18 +373,11 @@ const renderArcaneEnergyCost = (translate, translateMap, localeJoin, responsiveT
372
373
  mapNullableDefault(translateMap(option.translations)?.note, note => parensIf(getResponsiveTextOptional(note, responsiveTextSize)), "")), "disjunction"),
373
374
  });
374
375
  case "Map":
375
- return renderResponsiveMap(cost.Map, option => option.value, values => translate("{$value} AE", { value: values }), cost.Map.options.every(option => option.permanent_value !== undefined)
376
- ? {
377
- surround: values => translate(", {$value} of which are permanent", {
378
- value: values,
379
- }),
380
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
381
- getAdditionalValue: option => option.permanent_value,
382
- }
383
- : undefined).run({
376
+ return renderStandaloneCostMap(cost.Map).run({
384
377
  translate,
385
378
  translateMap,
386
379
  responsiveTextSize,
380
+ energyUnit: "ArcaneEnergy",
387
381
  });
388
382
  case "Variable":
389
383
  return translate("Variable");
@@ -423,7 +417,7 @@ const renderBindingCost = (translate, translateMap, localeCompare, getAllResolve
423
417
  }),
424
418
  });
425
419
  case "Map":
426
- return renderResponsiveMap(cost.Map, option => option.permanent_value, values => translate("{$value} permanent AE", { value: values })).run({
420
+ return renderResponsiveMap(cost.Map, option => Reader.of(option.permanentValue), values => translateR("{$value} permanent AE", { value: values })).run({
427
421
  translate,
428
422
  translateMap,
429
423
  responsiveTextSize,
@@ -451,7 +445,7 @@ const renderLifePointsCost = (translate, cost) => cost === undefined
451
445
  ? ""
452
446
  : // eslint-disable-next-line no-irregular-whitespace
453
447
  ` (+ ${translate("{$value} LP", { value: cost.Fixed.value })})`;
454
- const renderCost = (translate, translateMap, localeJoin, localeCompare, getAllResolvedSelectOptions, responsiveTextSize, levels, cost) => {
448
+ const renderCost = (translate, translateMap, format, localeJoin, localeCompare, getAllResolvedSelectOptions, responsiveTextSize, levels, cost) => {
455
449
  if (typeof cost === "number") {
456
450
  return {
457
451
  label: translate("AE Cost"),
@@ -466,9 +460,9 @@ const renderCost = (translate, translateMap, localeJoin, localeCompare, getAllRe
466
460
  label: translate("AE Cost"),
467
461
  value: cost.kind === "ArcaneEnergyCost"
468
462
  ? "ae_cost" in cost.ArcaneEnergyCost
469
- ? renderArcaneEnergyCost(translate, translateMap, localeJoin, responsiveTextSize, levels, cost.ArcaneEnergyCost.ae_cost) + renderLifePointsCost(translate, cost.ArcaneEnergyCost.lp_cost)
470
- : renderArcaneEnergyCost(translate, translateMap, localeJoin, responsiveTextSize, levels, cost.ArcaneEnergyCost)
471
- : renderArcaneEnergyCost(translate, translateMap, localeJoin, responsiveTextSize, levels, cost),
463
+ ? renderArcaneEnergyCost(translate, translateMap, format, localeJoin, responsiveTextSize, levels, cost.ArcaneEnergyCost.ae_cost) + renderLifePointsCost(translate, cost.ArcaneEnergyCost.lp_cost)
464
+ : renderArcaneEnergyCost(translate, translateMap, format, localeJoin, responsiveTextSize, levels, cost.ArcaneEnergyCost)
465
+ : renderArcaneEnergyCost(translate, translateMap, format, localeJoin, responsiveTextSize, levels, cost),
472
466
  };
473
467
  case "BindingCost":
474
468
  return {
@@ -686,7 +680,7 @@ const renderAdditionalSpecialRules = (traditionName, nameOfPeopleWithThisTraditi
686
680
  * Get a JSON representation of the rules text for a special ability.
687
681
  */
688
682
  export const getActivatableEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getAllInstances, getResolvedSelectOptionById, getAllResolvedSelectOptions }, locale, { entity: entityName, content: entry, id }) => {
689
- const { translate, translateMap } = locale;
683
+ const { translate, translateMap, format } = locale;
690
684
  const translation = translateMap(entry.translations);
691
685
  const responsiveTextSize = ResponsiveTextSize.Full;
692
686
  if (translation === undefined) {
@@ -713,6 +707,8 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator(({
713
707
  (baseEntry.levels !== undefined ? ` I–${romanize(baseEntry.levels)}` : ""),
714
708
  subtitle: mapNullable(baseEntry.usage_type, usageType => {
715
709
  switch (usageType.kind) {
710
+ case "Active":
711
+ return translate("Active");
716
712
  case "Passive":
717
713
  return translate("Passive");
718
714
  case "BasicManeuver":
@@ -789,7 +785,7 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator(({
789
785
  label: translate("Volume"),
790
786
  value: renderVolumeValue(translate, translateMap, () => getAllResolvedSelectOptions(wrappedId), responsiveTextSize, volume),
791
787
  })),
792
- mapNullable(baseEntry.cost, cost => renderCost(translate, translateMap, locale.join, locale.compare, () => getAllResolvedSelectOptions(wrappedId), responsiveTextSize, baseEntry.levels, cost)),
788
+ mapNullable(baseEntry.cost, cost => renderCost(translate, translateMap, format, locale.join, locale.compare, () => getAllResolvedSelectOptions(wrappedId), responsiveTextSize, baseEntry.levels, cost)),
793
789
  mapNullable(baseEntry.property, property => ({
794
790
  label: translate("Property"),
795
791
  value: renderPropertyValue(getInstanceById, translate, translateMap, property),
@@ -2,7 +2,7 @@ import type { AmmunitionishIdentifier, EquipmentIdentifier, GenMeleeWeapon, GenR
2
2
  import { type TaggedEntity } from "../creator.js";
3
3
  import type { GetInstanceById } from "../helpers/getTypes.js";
4
4
  import type { LocaleJoin } from "../helpers/locale.js";
5
- import type { Translate, TranslateMap } from "../helpers/translate.js";
5
+ import type { Format, Translate, TranslateMap } from "../helpers/translate.js";
6
6
  import type { IdMap, LabeledEntityDescriptionSection, RawEntityDescriptionSectionContent } from "../index.js";
7
7
  /**
8
8
  * Get the name of an equipment item.
@@ -15,7 +15,7 @@ export declare const renderMeleeWeapon: <Damage>(translate: Translate, translate
15
15
  /**
16
16
  * Render combat values of a ranged weapon.
17
17
  */
18
- export declare const renderRangedWeapon: <Damage>(translate: Translate, translateMap: TranslateMap, getInstanceById: GetInstanceById<"RangedCombatTechnique" | AmmunitionishIdentifier["kind"]>, measurements: Required<LocaleMeasurementAdjustments>, renderDamage: (damage: Damage) => string, rangedCombatTechniqueId: string, use: GenRangedWeapon<Damage>) => LabeledEntityDescriptionSection<RawEntityDescriptionSectionContent>;
18
+ export declare const renderRangedWeapon: <Damage>(translate: Translate, translateMap: TranslateMap, format: Format, getInstanceById: GetInstanceById<"RangedCombatTechnique" | AmmunitionishIdentifier["kind"]>, measurements: Required<LocaleMeasurementAdjustments>, renderDamage: (damage: Damage) => string, rangedCombatTechniqueId: string, use: GenRangedWeapon<Damage>) => LabeledEntityDescriptionSection<RawEntityDescriptionSectionContent>;
19
19
  /**
20
20
  * Get a JSON representation of the rules text for equipment.
21
21
  */
@@ -162,12 +162,12 @@ export const renderMeleeWeapon = (translate, translateMap, localeJoin, getInstan
162
162
  },
163
163
  };
164
164
  };
165
- const renderReloadTime = (translate, reloadTime) => isNotEmpty(reloadTime)
165
+ const renderReloadTime = (translate, translateMap, format, reloadTime) => isNotEmpty(reloadTime)
166
166
  ? reloadTime.length > 1
167
167
  ? translate("{$value} actions", {
168
168
  value: reloadTime.map(time => time.value).join("/"),
169
169
  })
170
- : formatTimeSpan(translate, ResponsiveTextSize.Full, "Actions", reloadTime[0].value)
170
+ : formatTimeSpan(translate, translateMap, format, ResponsiveTextSize.Full, "Actions", reloadTime[0].value)
171
171
  : MISSING_VALUE;
172
172
  const renderRangeBrackets = (rangeBrackets) => `${rangeBrackets.close.toFixed()}/${rangeBrackets.medium.toFixed()}/${rangeBrackets.far.toFixed()}`;
173
173
  const renderAmmunition = (translateMap, getInstanceById, ammunition) => ammunition === undefined
@@ -177,7 +177,7 @@ const renderMeleeDamage = (translate) => (damage) => renderDiceAndFlat(translate
177
177
  /**
178
178
  * Render combat values of a ranged weapon.
179
179
  */
180
- export const renderRangedWeapon = (translate, translateMap, getInstanceById, measurements, renderDamage, rangedCombatTechniqueId, use) => {
180
+ export const renderRangedWeapon = (translate, translateMap, format, getInstanceById, measurements, renderDamage, rangedCombatTechniqueId, use) => {
181
181
  const combatTechnique = getInstanceById("RangedCombatTechnique", rangedCombatTechniqueId);
182
182
  return {
183
183
  type: "labeled",
@@ -193,7 +193,7 @@ export const renderRangedWeapon = (translate, translateMap, getInstanceById, mea
193
193
  },
194
194
  {
195
195
  label: translate("Reload Time"),
196
- value: renderReloadTime(translate, use.reload_time),
196
+ value: renderReloadTime(translate, translateMap, format, use.reload_time),
197
197
  },
198
198
  {
199
199
  label: translate("Range Brackets"),
@@ -624,7 +624,7 @@ const renderBookRules = (translate, rules) => {
624
624
  * Get a JSON representation of the rules text for equipment.
625
625
  */
626
626
  export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ getInstanceById, idMap }, locale, entry) => {
627
- const { translate, translateMap } = locale;
627
+ const { translate, translateMap, format } = locale;
628
628
  if (entry.entity === "Book") {
629
629
  const translation = translateMap(entry.content.translations);
630
630
  if (translation === undefined) {
@@ -729,7 +729,7 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
729
729
  ? Object.entries(combatValues.values.melee_uses ?? {}).map(([combatTechniqueId, use]) => renderMeleeWeapon(translate, translateMap, locale.join, getInstanceById, locale.measurementAdjustments, renderMeleeDamage(translate), combatTechniqueId, use))
730
730
  : []),
731
731
  ...(combatValues?.type === "Weapon"
732
- ? Object.entries(combatValues.values.ranged_uses ?? {}).map(([combatTechniqueId, use]) => renderRangedWeapon(translate, translateMap, getInstanceById, locale.measurementAdjustments, renderRangedDamage(translate), combatTechniqueId, use))
732
+ ? Object.entries(combatValues.values.ranged_uses ?? {}).map(([combatTechniqueId, use]) => renderRangedWeapon(translate, translateMap, format, getInstanceById, locale.measurementAdjustments, renderRangedDamage(translate), combatTechniqueId, use))
733
733
  : []),
734
734
  {
735
735
  type: "definitionList",
@@ -742,7 +742,7 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator(({ g
742
742
  label: translate("Burning Time"),
743
743
  value: burningTime.kind === "Unlimited"
744
744
  ? translate("unlimited")
745
- : formatTimeSpan(translate, ResponsiveTextSize.Full, burningTime.Limited.unit, burningTime.Limited.value),
745
+ : formatTimeSpan(translate, translateMap, format, ResponsiveTextSize.Full, burningTime.Limited.unit, burningTime.Limited.value),
746
746
  })),
747
747
  mapNullable(baseItemTranslation?.color, color => ({
748
748
  label: translate("Color"),
@@ -84,6 +84,7 @@ const getTextForTraditions = (deps, values) => {
84
84
  }
85
85
  })
86
86
  .filter(isNotNullish)
87
+ .sort(deps.localeCompare)
87
88
  .join(", ");
88
89
  return {
89
90
  label: deps.translate("Traditions"),
@@ -94,7 +95,7 @@ const getTextForTraditions = (deps, values) => {
94
95
  * Get a JSON representation of the rules text for a blessing.
95
96
  */
96
97
  export const getBlessingEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getAllInstances }, locale, { id, content: entry }) => {
97
- const { translate, translateMap } = locale;
98
+ const { translate, translateMap, format } = locale;
98
99
  const translation = translateMap(entry.translations);
99
100
  if (translation === undefined) {
100
101
  return undefined;
@@ -102,6 +103,7 @@ export const getBlessingEntityDescription = createEntityDescriptionCreator(({ ge
102
103
  const env = {
103
104
  translate,
104
105
  translateMap,
106
+ format,
105
107
  localeCompare: locale.compare,
106
108
  getInstanceById,
107
109
  getAllInstances,
@@ -135,7 +137,7 @@ export const getBlessingEntityDescription = createEntityDescriptionCreator(({ ge
135
137
  * Get a JSON representation of the rules text for a liturgical chant.
136
138
  */
137
139
  export const getLiturgicalChantEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getChildInstancesForInstanceId, idMap }, locale, { content: entry, entity, id }, options) => {
138
- const { translate, translateMap, compare: localeCompare, join: localeJoin } = locale;
140
+ const { translate, translateMap, format, compare: localeCompare, join: localeJoin } = locale;
139
141
  const translation = translateMap(entry.translations);
140
142
  if (translation === undefined) {
141
143
  return undefined;
@@ -143,6 +145,7 @@ export const getLiturgicalChantEntityDescription = createEntityDescriptionCreato
143
145
  const env = {
144
146
  translate,
145
147
  translateMap,
148
+ format,
146
149
  getInstanceById,
147
150
  getChildInstancesForInstanceId,
148
151
  localeJoin: locale.join,
@@ -198,7 +201,7 @@ export const getLiturgicalChantEntityDescription = createEntityDescriptionCreato
198
201
  * Get a JSON representation of the rules text for a ceremony.
199
202
  */
200
203
  export const getCeremonyEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getChildInstancesForInstanceId, idMap }, locale, { content: entry, entity, id }, options) => {
201
- const { translate, translateMap, compare: localeCompare, join: localeJoin } = locale;
204
+ const { translate, translateMap, format, compare: localeCompare, join: localeJoin } = locale;
202
205
  const translation = translateMap(entry.translations);
203
206
  if (translation === undefined) {
204
207
  return undefined;
@@ -206,6 +209,7 @@ export const getCeremonyEntityDescription = createEntityDescriptionCreator(({ ge
206
209
  const env = {
207
210
  translate,
208
211
  translateMap,
212
+ format,
209
213
  getInstanceById,
210
214
  getChildInstancesForInstanceId,
211
215
  localeJoin: locale.join,
@@ -20,7 +20,7 @@ const getEnhancementAPValue = (adventurePoints, parentImprovementCost) => {
20
20
  };
21
21
  const renderEnhancement = (enhancement, parentImprovementCost) => translateMapR(enhancement.content.translations).thenW(translation => Reader.ask().map(env => translation === undefined
22
22
  ? undefined
23
- : `- ^[${translation.name}](entity: "Enhancement") (${env.translate("SR {$value}", { value: enhancement.content.skill_rating })}, ${env.translate("{$value} AP", { value: getEnhancementAPValue(enhancement.content.adventurePoints, parentImprovementCost) })}): ${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)}`}`));
23
+ : `- ^[${translation.name}](entity: "Enhancement") (${env.translate("SR {$value}", { value: enhancement.content.skill_rating })}, ${env.translate("{$value} AP", { value: getEnhancementAPValue(enhancement.content.adventurePoints, parentImprovementCost) })}): ${translation.effect.split("\n").join("\n ")}${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)}`}`));
24
24
  /**
25
25
  * Render the enhancements section for an entity description, if applicable.
26
26
  */
@@ -33,7 +33,7 @@ export const renderEnhancements = (parentId, parentImprovementCost) => getChildI
33
33
  }
34
34
  return translateR("Enhancements").map((label) => ({
35
35
  type: "labeled",
36
- label,
36
+ label: `${label}:`,
37
37
  value: {
38
38
  type: "plain",
39
39
  text: nonNullishDescriptions.join("\n"),
@@ -1,51 +1,40 @@
1
- import type { MapStyle, ResponsiveTextOptional } from "@optolith/database-schema/gen";
2
- import type { LocaleMap, Translate, TranslateMap } from "../../helpers/translate.js";
3
- import { type StdReader } from "./reader.js";
1
+ import { Reader } from "@elyukai/utils/reader";
2
+ import type { ParameterMapStyle, ResponsiveTextOptional } from "@optolith/database-schema/gen";
3
+ import type { LocaleMap } from "../../helpers/translate.js";
4
+ import { type StdEnv, type StdReader } from "./reader.js";
4
5
  /**
5
6
  * Renders a map of options to a string.
6
7
  */
7
- export declare const renderMap: <M extends {
8
- options: MO[];
9
- style?: MapStyle;
10
- translations?: LocaleMap<MT>;
11
- }, MOT = M extends {
8
+ export declare const renderMap: <V, T, OT, VEnv, SVEnv>(map: {
12
9
  options: {
13
- translations?: LocaleMap<infer MOT_>;
10
+ value: V;
11
+ translations?: LocaleMap<OT>;
14
12
  }[];
15
- } ? MOT_ : never, MT = M extends {
16
- translations?: LocaleMap<infer MT_>;
17
- } ? MT_ : never, MO extends {
18
- translations?: LocaleMap<MOT>;
19
- } = M extends {
20
- options: (infer MO_ & {
21
- translations?: LocaleMap<MOT>;
22
- })[];
23
- } ? MO_ : never>(translate: Translate, translateMap: TranslateMap, map: M, getValue: (option: MO) => string | number, surroundValues: ((values: string) => string) | undefined, getLabel: (optionTranslation: MOT) => string, getListPrefix: (translation: MT) => string | undefined, getListSuffix: (translation: MT) => string | undefined, getReplacement: (translation: MT) => string | undefined, ...moreToAppend: ({
24
- surround: (values: string) => string;
25
- getAdditionalValue: (option: MO, optionTranslation: MOT) => string | number;
26
- } | undefined)[]) => string;
13
+ style: ParameterMapStyle;
14
+ translations?: LocaleMap<T>;
15
+ }, getValue: (option: V) => Reader<VEnv, string | number>, surroundValues: ((values: string) => Reader<SVEnv, string>) | undefined, getLabel: (optionTranslation: OT) => string, getListPrefix: (translation: T) => string | undefined, getListSuffix: (translation: T) => string | undefined, getReplacement: (translation: T) => string | undefined, ...moreToAppend: ({
16
+ surround: (values: string) => StdReader<string, "t">;
17
+ getAdditionalValue: (option: V, optionTranslation: OT) => string | number;
18
+ } | undefined)[]) => Reader<StdEnv<"t" | "tm"> & VEnv & SVEnv, string>;
27
19
  /**
28
20
  * Renders a map of options to a string.
29
21
  */
30
- export declare const renderResponsiveMap: <M extends {
31
- options: MO[];
32
- style?: MapStyle;
22
+ export declare const renderResponsiveMap: <T, VEnv, SVEnv>(map: {
23
+ options: {
24
+ value: T;
25
+ translations?: LocaleMap<{
26
+ label: ResponsiveTextOptional;
27
+ }>;
28
+ }[];
29
+ style: ParameterMapStyle;
33
30
  translations?: LocaleMap<{
34
- list_prepend?: ResponsiveTextOptional;
35
31
  listPrefix?: ResponsiveTextOptional;
36
- list_append?: ResponsiveTextOptional;
37
32
  listSuffix?: ResponsiveTextOptional;
38
33
  replacement?: ResponsiveTextOptional;
39
34
  }>;
40
- }, MO extends {
41
- translations?: LocaleMap<{
42
- label: ResponsiveTextOptional;
43
- }>;
44
- } = M extends {
45
- options: (infer MO_)[];
46
- } ? MO_ : never>(map: M, getValue: (option: MO) => string | number, surroundValues: ((values: string) => string) | undefined, ...moreToAppend: ({
47
- surround: (values: string) => string;
48
- getAdditionalValue: (option: MO, optionTranslation: {
35
+ }, getValue: (option: T) => Reader<VEnv, string | number>, surroundValues: ((values: string) => Reader<SVEnv, string>) | undefined, ...moreToAppend: ({
36
+ surround: (values: string) => StdReader<string, "t">;
37
+ getAdditionalValue: (option: T, optionTranslation: {
49
38
  label: ResponsiveTextOptional;
50
39
  }) => string | number;
51
- } | undefined)[]) => StdReader<string, "t" | "tm" | "rts">;
40
+ } | undefined)[]) => Reader<StdEnv<"t" | "tm" | "rts"> & VEnv & SVEnv, string>;
@@ -6,32 +6,32 @@ import { MISSING_VALUE } from "./unknown.js";
6
6
  /**
7
7
  * Renders a map of options to a string.
8
8
  */
9
- export const renderMap = (translate, translateMap, map, getValue, surroundValues = values => values, getLabel, getListPrefix, getListSuffix, getReplacement, ...moreToAppend) => {
10
- const translation = translateMap(map.translations);
9
+ export const renderMap = (map, getValue, surroundValues = values => Reader.of(values), getLabel, getListPrefix, getListSuffix, getReplacement, ...moreToAppend) => Reader.asks(env => {
10
+ const translation = env.translateMap(map.translations);
11
11
  const replacement = mapNullable(translation, t => getReplacement(t));
12
12
  if (isNotNullish(replacement)) {
13
13
  return replacement;
14
14
  }
15
- if (map.style?.kind === "Verbose") {
15
+ if (map.style.kind === "Verbose") {
16
16
  return map.options
17
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
18
+ const value = getValue(option.value).run(env);
19
+ const optionTranslation = env.translateMap(option.translations);
20
+ return `${surroundValues(typeof value === "number" ? value.toFixed() : value).run(env)} ${env.translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${optionTranslation === undefined ? MISSING_VALUE : getLabel(optionTranslation)}${mapNullableDefault(mapNullable(translation, getListSuffix), listSuffix => ` ${listSuffix}`, "")}${moreToAppend
21
21
  .filter(isNotNullish)
22
22
  .map(({ surround, getAdditionalValue }) => {
23
23
  const additionalValue = optionTranslation === undefined
24
24
  ? MISSING_VALUE
25
- : getAdditionalValue(option, optionTranslation);
26
- return surround(typeof additionalValue === "number" ? additionalValue.toFixed() : additionalValue);
25
+ : getAdditionalValue(option.value, optionTranslation);
26
+ return surround(typeof additionalValue === "number" ? additionalValue.toFixed() : additionalValue).run(env);
27
27
  })
28
28
  .join("")}`;
29
29
  })
30
30
  .join(", ");
31
31
  }
32
- return `${surroundValues(map.options.map(option => getValue(option)).join("/"))} ${translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${map.options
32
+ return `${surroundValues(map.options.map(option => getValue(option.value).run(env)).join("/")).run(env)} ${env.translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${map.options
33
33
  .map(option => {
34
- const optionTranslation = translateMap(option.translations);
34
+ const optionTranslation = env.translateMap(option.translations);
35
35
  if (optionTranslation === undefined) {
36
36
  return MISSING_VALUE;
37
37
  }
@@ -41,16 +41,17 @@ export const renderMap = (translate, translateMap, map, getValue, surroundValues
41
41
  .filter(isNotNullish)
42
42
  .map(({ surround, getAdditionalValue }) => surround(map.options
43
43
  .map(option => {
44
- const optionTranslation = translateMap(option.translations);
44
+ const optionTranslation = env.translateMap(option.translations);
45
45
  if (optionTranslation === undefined) {
46
46
  return MISSING_VALUE;
47
47
  }
48
- return getAdditionalValue(option, optionTranslation);
48
+ return getAdditionalValue(option.value, optionTranslation);
49
49
  })
50
- .join("/")))
50
+ .join("/")).run(env))
51
51
  .join("")}`;
52
- };
52
+ });
53
53
  /**
54
54
  * Renders a map of options to a string.
55
55
  */
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));
56
+ export const renderResponsiveMap = (map, getValue, surroundValues, ...moreToAppend) => Reader.asks(env => renderMap(map, getValue, surroundValues, translation => getResponsiveTextOptional(translation.label, env.responsiveTextSize) ??
57
+ translation.label.full, translation => mapNullable(translation.listPrefix, text => getResponsiveTextOptional(text, env.responsiveTextSize)), translation => mapNullable(translation.listSuffix, text => getResponsiveTextOptional(text, env.responsiveTextSize)), translation => mapNullable(translation.replacement, text => getResponsiveTextOptional(text, env.responsiveTextSize)), ...moreToAppend).run(env));
@@ -82,7 +82,10 @@ export const printGeneralPrerequisiteGroup = (getInstanceById, getResolvedSelect
82
82
  case "RatedSum":
83
83
  return printRatedSumPrerequisite(getInstanceById, locale, prerequisite.RatedSum);
84
84
  case "Enhancement":
85
- return printEnhancementPrerequisite(getInstanceById, locale, prerequisite.Enhancement);
85
+ return printEnhancementPrerequisite(prerequisite.Enhancement).run({
86
+ ...locale,
87
+ getInstanceById,
88
+ });
86
89
  case "Text":
87
90
  return printTextPrerequisite(locale, prerequisite.Text);
88
91
  case "SexualCharacteristic":
@@ -262,7 +265,11 @@ export const printEnhancementPrerequisiteGroup = (getInstanceById, locale, prere
262
265
  case "Rated":
263
266
  return printRatedPrerequisite(getInstanceById, locale, prerequisite.Rated);
264
267
  case "Enhancement":
265
- return printEnhancementPrerequisite(getInstanceById, locale, prerequisite.Enhancement);
268
+ return printEnhancementPrerequisite(prerequisite.Enhancement).run({
269
+ ...locale,
270
+ getInstanceById,
271
+ hideParent: true,
272
+ });
266
273
  default:
267
274
  return assertExhaustive(prerequisite);
268
275
  }
@@ -1,8 +1,12 @@
1
+ import { Reader } from "@elyukai/utils/reader";
1
2
  import type { EnhancementPrerequisite } from "@optolith/database-schema/gen";
2
- import type { GetInstanceById } from "../../../../helpers/getTypes.js";
3
- import type { LocaleEnvironment } from "../../../../helpers/locale.js";
3
+ import { type StdEnv } from "../../reader.js";
4
4
  import type { PrerequisitePart } from "../part.js";
5
+ type EnvExtra = {
6
+ hideParent?: true;
7
+ };
5
8
  /**
6
9
  * Get the translation of an external enhancement prerequisite.
7
10
  */
8
- export declare const printEnhancementPrerequisite: (getInstanceById: GetInstanceById<"Spell" | "Ritual" | "LiturgicalChant" | "Ceremony" | "Enhancement">, locale: Pick<LocaleEnvironment, "translate" | "translateMap">, prerequisite: EnhancementPrerequisite) => PrerequisitePart | undefined;
11
+ export declare const printEnhancementPrerequisite: (prerequisite: EnhancementPrerequisite) => Reader<StdEnv<"t" | "tm" | "ibi", "Enhancement" | "Spell" | "Ritual" | "LiturgicalChant" | "Ceremony"> & EnvExtra, PrerequisitePart | undefined>;
12
+ export {};
@@ -1,41 +1,35 @@
1
+ import { Reader } from "@elyukai/utils/reader";
1
2
  import { assertExhaustive } from "@optolith/helpers/typeSafety";
3
+ import { fromUniformCase } from "tsondb/schema/gen";
4
+ import { attributedNameFromInstanceR, getInstanceByIdR, translateR, } from "../../reader.js";
2
5
  import { MISSING_VALUE } from "../../unknown.js";
3
- const printLabel = (locale, skillId) => {
6
+ const printLabel = (skillId) => {
4
7
  switch (skillId.kind) {
5
8
  case "Spell":
6
9
  case "Ritual":
7
- return locale.translate("spell enhancement");
10
+ return translateR("spell enhancement");
8
11
  case "LiturgicalChant":
9
12
  case "Ceremony":
10
- return locale.translate("liturgical enhancement");
13
+ return translateR("liturgical enhancement");
11
14
  default:
12
15
  return assertExhaustive(skillId);
13
16
  }
14
17
  };
15
- const getSkill = (getInstanceById, parentId) => {
16
- switch (parentId.kind) {
17
- case "Spell":
18
- return getInstanceById("Spell", parentId.Spell);
19
- case "Ritual":
20
- return getInstanceById("Ritual", parentId.Ritual);
21
- case "LiturgicalChant":
22
- return getInstanceById("LiturgicalChant", parentId.LiturgicalChant);
23
- case "Ceremony":
24
- return getInstanceById("Ceremony", parentId.Ceremony);
25
- default:
26
- return assertExhaustive(parentId);
27
- }
28
- };
29
18
  /**
30
19
  * Get the translation of an external enhancement prerequisite.
31
20
  */
32
- export const printEnhancementPrerequisite = (getInstanceById, locale, prerequisite) => {
33
- const enhancement = getInstanceById("Enhancement", prerequisite.id);
34
- const skill = enhancement && getSkill(getInstanceById, enhancement.parent);
35
- return {
36
- label: `${enhancement ? printLabel(locale, enhancement.parent) : MISSING_VALUE} `,
37
- value: `*${locale.translateMap(enhancement?.translations)?.name ?? MISSING_VALUE}* ${locale.translate("for")} ${locale.translateMap(skill?.translations)?.name ?? MISSING_VALUE}`,
21
+ export const printEnhancementPrerequisite = (prerequisite) => getInstanceByIdR("Enhancement", prerequisite.id).thenW(enhancement => enhancement === undefined
22
+ ? Reader.of(undefined)
23
+ : getInstanceByIdR(enhancement.parent).thenW(skill => printLabel(enhancement.parent).thenW(label => Reader.asks((env) => env.hideParent ?? false).thenW(hideParent => attributedNameFromInstanceR(enhancement, "prerequisite", "Enhancement", prerequisite.id)
24
+ .map(name => name ?? MISSING_VALUE)
25
+ .thenW(name => hideParent
26
+ ? Reader.of(name)
27
+ : attributedNameFromInstanceR(skill, "prerequisite", enhancement.parent.kind, fromUniformCase(enhancement.parent))
28
+ .map(skillName => skillName ?? MISSING_VALUE)
29
+ .thenW(skillName => translateR("for").map(forText => `${name} ${forText} ${skillName}`)))
30
+ .map((value) => ({
31
+ label: `${label} `,
32
+ value,
38
33
  sentenceType: undefined,
39
34
  isMeta: false,
40
- };
41
- };
35
+ }))))));
@@ -3,20 +3,20 @@ import { type StdReader } from "../../reader.js";
3
3
  /**
4
4
  * Renders the text for a non-modifiable casting time of a fast activatable skill.
5
5
  */
6
- export declare const renderFastSkillNonModifiableCastingTime: (value: FastSkillNonModifiableCastingTime) => StdReader<string, "t" | "rts">;
6
+ export declare const renderFastSkillNonModifiableCastingTime: (value: FastSkillNonModifiableCastingTime) => StdReader<string, "t" | "tm" | "f" | "rts">;
7
7
  /**
8
8
  * Get the text for a non-modifiable casting time of a slow activatable skill.
9
9
  */
10
- export declare const renderSlowSkillNonModifiableCastingTime: (value: SlowSkillNonModifiableCastingTime) => StdReader<string, "t" | "rts">;
10
+ export declare const renderSlowSkillNonModifiableCastingTime: (value: SlowSkillNonModifiableCastingTime) => StdReader<string, "t" | "tm" | "f" | "rts">;
11
11
  /**
12
12
  * Translate casting time.
13
13
  */
14
- export declare const renderCastingTime: <NonModifiable extends object>(renderNonModifiableCastingTime: (value: NonModifiable) => StdReader<string, "t" | "rts" | "s" | "nms">, value: CastingTime<NonModifiable>) => StdReader<string, "t" | "rts" | "s" | "nms" | "ibi", "SkillModificationLevel">;
14
+ export declare const renderCastingTime: <NonModifiable extends object>(renderNonModifiableCastingTime: (value: NonModifiable) => StdReader<string, "t" | "tm" | "f" | "rts" | "s" | "nms">, value: CastingTime<NonModifiable>) => StdReader<string, "t" | "tm" | "f" | "rts" | "s" | "nms" | "ibi", "SkillModificationLevel">;
15
15
  /**
16
16
  * Get the text for the casting time of a fast activatable skill.
17
17
  */
18
- export declare const renderFastCastingTime: (value: FastCastingTime) => StdReader<string, "t" | "rts" | "nms" | "ibi", "SkillModificationLevel">;
18
+ export declare const renderFastCastingTime: (value: FastCastingTime) => StdReader<string, "t" | "tm" | "f" | "rts" | "nms" | "ibi", "SkillModificationLevel">;
19
19
  /**
20
20
  * Get the text for the casting time of a slow activatable skill.
21
21
  */
22
- export declare const renderSlowCastingTime: (value: SlowCastingTime) => StdReader<string, "t" | "rts" | "nms" | "ibi", "SkillModificationLevel">;
22
+ export declare const renderSlowCastingTime: (value: SlowCastingTime) => StdReader<string, "t" | "tm" | "f" | "rts" | "nms" | "ibi", "SkillModificationLevel">;