@optolith/entity-descriptions 0.13.2 → 0.15.0

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 (74) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/lib/creator.d.ts +4 -11
  3. package/lib/creator.js +58 -2
  4. package/lib/entities/activatable.d.ts +5 -2
  5. package/lib/entities/activatable.js +29 -15
  6. package/lib/entities/alternativeRule.d.ts +1 -1
  7. package/lib/entities/attribute.d.ts +1 -3
  8. package/lib/entities/combatTechnique.d.ts +2 -2
  9. package/lib/entities/condition.d.ts +2 -6
  10. package/lib/entities/culture.d.ts +2 -1
  11. package/lib/entities/culture.js +3 -3
  12. package/lib/entities/curriculum.d.ts +1 -1
  13. package/lib/entities/curriculum.js +1 -1
  14. package/lib/entities/derivedCharacteristic.d.ts +1 -1
  15. package/lib/entities/disease.d.ts +1 -1
  16. package/lib/entities/elixir.d.ts +1 -1
  17. package/lib/entities/elixir.js +1 -1
  18. package/lib/entities/equipment.d.ts +4 -6
  19. package/lib/entities/equipment.js +517 -213
  20. package/lib/entities/equipmentPackage.d.ts +1 -1
  21. package/lib/entities/experienceLevel.d.ts +1 -3
  22. package/lib/entities/focusRule.d.ts +1 -1
  23. package/lib/entities/influence.d.ts +1 -1
  24. package/lib/entities/liturgicalChant.d.ts +3 -3
  25. package/lib/entities/optionalRule.d.ts +1 -3
  26. package/lib/entities/partial/adventurePointsValue.js +8 -7
  27. package/lib/entities/partial/dice.js +2 -2
  28. package/lib/entities/partial/enhancements.js +2 -2
  29. package/lib/entities/partial/herbary.js +1 -1
  30. package/lib/entities/partial/prerequisites/single/personalityTrait.js +1 -1
  31. package/lib/entities/partial/prerequisites/single/ratedSum.js +1 -1
  32. package/lib/entities/partial/profession/options/combatTechniquesForAdventurePoints.d.ts +16 -0
  33. package/lib/entities/partial/profession/options/combatTechniquesForAdventurePoints.js +43 -0
  34. package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.d.ts +16 -0
  35. package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.js +39 -0
  36. package/lib/entities/partial/profession/packages.d.ts +67 -0
  37. package/lib/entities/partial/profession/packages.js +84 -0
  38. package/lib/entities/partial/profession/variants.d.ts +21 -0
  39. package/lib/entities/partial/profession/variants.js +31 -0
  40. package/lib/entities/partial/rated/activatable/cost.js +11 -9
  41. package/lib/entities/partial/rated/activatable/effect.js +2 -2
  42. package/lib/entities/partial/reader.d.ts +25 -1
  43. package/lib/entities/partial/reader.js +20 -0
  44. package/lib/entities/partial/units/energy.js +2 -1
  45. package/lib/entities/partial/units/length.js +6 -3
  46. package/lib/entities/partial/units/simple.d.ts +55 -0
  47. package/lib/entities/partial/units/simple.js +56 -0
  48. package/lib/entities/partial/units/timeSpan.js +11 -7
  49. package/lib/entities/partial/unknown.d.ts +4 -0
  50. package/lib/entities/partial/unknown.js +4 -0
  51. package/lib/entities/personalityTrait.d.ts +1 -1
  52. package/lib/entities/poison.d.ts +1 -1
  53. package/lib/entities/poison.js +1 -1
  54. package/lib/entities/profession.d.ts +2 -1
  55. package/lib/entities/profession.js +44 -62
  56. package/lib/entities/race.d.ts +2 -2
  57. package/lib/entities/sexPractice.d.ts +1 -3
  58. package/lib/entities/skill.d.ts +1 -1
  59. package/lib/entities/spell.d.ts +17 -16
  60. package/lib/entities/state.d.ts +1 -3
  61. package/lib/entities/tradeSecret.d.ts +4 -0
  62. package/lib/entities/tradeSecret.js +77 -0
  63. package/lib/env.d.ts +9 -2
  64. package/lib/helpers/locale.d.ts +4 -0
  65. package/lib/index.d.ts +320 -293
  66. package/lib/index.js +9 -1
  67. package/lib/references/index.d.ts +2 -3
  68. package/lib/references/index.js +11 -16
  69. package/lib/references/page.d.ts +4 -2
  70. package/lib/references/page.js +6 -4
  71. package/lib/references/pageRange.d.ts +3 -3
  72. package/lib/references/pageRange.js +6 -4
  73. package/lib/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
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.15.0](https://github.com/Optolith/entity-descriptions/compare/v0.14.0...v0.15.0) (2026-09-26)
6
+
7
+ ### ⚠ BREAKING CHANGES
8
+
9
+ * tabular descriptions for most equipment
10
+
11
+ ### Features
12
+
13
+ * add trade secret entity description ([6379f7d](https://github.com/Optolith/entity-descriptions/commit/6379f7d5fb111ad5960db15b16a17e9d3600bf71))
14
+ * tabular descriptions for most equipment ([3c53e81](https://github.com/Optolith/entity-descriptions/commit/3c53e81d8ca55b47c16ebd4727aa34531f42f965))
15
+ ## [0.14.0](https://github.com/Optolith/entity-descriptions/compare/v0.13.2...v0.14.0) (2026-09-24)
16
+
17
+ ### ⚠ BREAKING CHANGES
18
+
19
+ * **deps:** upgrade to schema 0.65.0
20
+
21
+ ### build
22
+
23
+ * **deps:** upgrade to schema 0.65.0 ([cb94c96](https://github.com/Optolith/entity-descriptions/commit/cb94c96df8dc3f1d4f5476db729df2ed85ce6b1d))
24
+
25
+ ### Bug Fixes
26
+
27
+ * missing adventure points label for AP value by level ([9aabd43](https://github.com/Optolith/entity-descriptions/commit/9aabd437d587e7b559c18a730249ea3c66bb8387))
5
28
  ## [0.13.2](https://github.com/Optolith/entity-descriptions/compare/v0.13.1...v0.13.2) (2026-09-16)
6
29
 
7
30
  ### Features
package/lib/creator.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import { type AnyNonNullish } from "@elyukai/utils/nullable";
1
2
  import type { EntityMap } from "@optolith/database-schema/gen";
2
- import type { GetInstanceById } from "./helpers/getTypes.js";
3
+ import type { StdEnv } from "./env.js";
3
4
  import type { LocaleEnvironment } from "./helpers/locale.js";
4
5
  import type { EntityDescription, RawEntityDescription } from "./index.js";
5
6
  import { type PublicationOptions } from "./references/publicationOptions.js";
@@ -17,19 +18,11 @@ export type TaggedEntity<ES extends keyof EntityMap> = {
17
18
  * Creates a function that creates the JSON representation of the rules text for
18
19
  * a library entry.
19
20
  */
20
- export declare const createEntityDescriptionCreator: <ES extends keyof EntityMap, A extends {
21
- getInstanceById: GetInstanceById<"Publication">;
22
- } = {
23
- getInstanceById: GetInstanceById<"Publication">;
24
- }>(fn: EntityDescriptionCreator<ES, A, RawEntityDescription>) => EntityDescriptionCreator<ES, A>;
21
+ export declare const createEntityDescriptionCreator: <ES extends keyof EntityMap, A = AnyNonNullish, Cols extends string = never>(fn: EntityDescriptionCreator<ES, A, RawEntityDescription<Cols, A>>) => EntityDescriptionCreator<ES, A & StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">>;
25
22
  /**
26
23
  * A function that creates the JSON representation of the rules text for a
27
24
  * library entry.
28
25
  */
29
- export type EntityDescriptionCreator<ES extends keyof EntityMap = keyof EntityMap, A extends {
30
- getInstanceById: GetInstanceById<"Publication">;
31
- } = {
32
- getInstanceById: GetInstanceById<"Publication">;
33
- }, R = EntityDescription> = (databaseAccessors: A, locale: LocaleEnvironment, entry: TaggedEntity<ES>, options: {
26
+ export type EntityDescriptionCreator<ES extends keyof EntityMap = keyof EntityMap, A = AnyNonNullish, R = EntityDescription<string>> = (databaseAccessors: A, locale: LocaleEnvironment, entry: TaggedEntity<ES>, options: {
34
27
  publications: PublicationOptions;
35
28
  }) => R | undefined;
package/lib/creator.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ensureNonEmpty } from "@elyukai/utils/array/nonEmpty";
1
2
  import { isNotNullish } from "@elyukai/utils/nullable";
2
3
  import { assertExhaustive } from "@elyukai/utils/typeSafety";
3
4
  import { getReferencesTranslation } from "./references/index.js";
@@ -50,25 +51,80 @@ const mapRawSection = (section) => {
50
51
  return assertExhaustive(section);
51
52
  }
52
53
  };
54
+ const mapRawTabular = (raw, env, options) => ({
55
+ ...raw,
56
+ category: raw.category === undefined
57
+ ? undefined
58
+ : {
59
+ label: raw.category.label.run(env),
60
+ value: raw.category.value.run(env),
61
+ },
62
+ labels: Object.fromEntries(Object.entries(raw.labels).map(([col, label]) => [col, label.run(env)])),
63
+ values: Object.fromEntries(Object.entries(raw.values).map(([col, value]) => [col, value.run(env)])),
64
+ additionalInformation: raw.additionalInformation === undefined
65
+ ? undefined
66
+ : raw.additionalInformation
67
+ .map(info => {
68
+ if (info === undefined) {
69
+ return undefined;
70
+ }
71
+ const { label: labelR, id, value: valueR } = info;
72
+ const value = valueR.run(env);
73
+ if (value === undefined) {
74
+ return undefined;
75
+ }
76
+ return {
77
+ label: labelR.run(env),
78
+ id,
79
+ value,
80
+ };
81
+ })
82
+ .filter(isNotNullish),
83
+ errata: raw.errata?.map(({ date, description }) => ({
84
+ date: env.formatDate(new Date(date)),
85
+ description: description.trim(),
86
+ })),
87
+ references: raw.references === undefined
88
+ ? undefined
89
+ : getReferencesTranslation(options.publications, raw.references).run(env),
90
+ });
53
91
  /**
54
92
  * Creates a function that creates the JSON representation of the rules text for
55
93
  * a library entry.
56
94
  */
57
95
  export const createEntityDescriptionCreator = (fn) => (databaseAccessors, locale, entry, options) => {
58
96
  const rawEntry = fn(databaseAccessors, locale, entry, options);
97
+ if (Array.isArray(rawEntry)) {
98
+ const results = rawEntry
99
+ .filter(e => isEntryFromIncludedPublication({ src: e.references }, databaseAccessors.getInstanceById, locale.translateMap, options.publications))
100
+ .map(e => mapRawTabular(e, databaseAccessors, options));
101
+ return ensureNonEmpty(results);
102
+ }
59
103
  if (rawEntry === undefined ||
60
104
  !isEntryFromIncludedPublication({ src: rawEntry.references }, databaseAccessors.getInstanceById, locale.translateMap, options.publications)) {
61
105
  return undefined;
62
106
  }
107
+ if (rawEntry.type === "tabular") {
108
+ return mapRawTabular(rawEntry, databaseAccessors, options);
109
+ }
63
110
  return {
64
111
  ...rawEntry,
112
+ category: rawEntry.category === undefined
113
+ ? undefined
114
+ : {
115
+ label: rawEntry.category.label.run(databaseAccessors),
116
+ value: rawEntry.category.value.run(databaseAccessors),
117
+ },
65
118
  body: rawEntry.body.filter(isNotNullish).map(mapRawSection),
66
119
  errata: rawEntry.errata?.map(({ date, description }) => ({
67
- date: locale.formatDate(date),
120
+ date: locale.formatDate(new Date(date)),
68
121
  description: description.trim(),
69
122
  })),
70
123
  references: rawEntry.references === undefined
71
124
  ? undefined
72
- : getReferencesTranslation(databaseAccessors.getInstanceById, options.publications, locale, rawEntry.references),
125
+ : getReferencesTranslation(options.publications, rawEntry.references).run({
126
+ ...databaseAccessors,
127
+ ...locale,
128
+ }),
73
129
  };
74
130
  };
@@ -1,6 +1,8 @@
1
1
  import type { ActivatableIdentifier, ActivatableNameBuilderRules, AdvancedSpecialAbility, AdvantageDisadvantagePrerequisites, AdvantageDisadvantageSubtype, AdventurePointsValue, ApplicableCombatTechniques, Aspect_ID, Brew_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
+ import type { StdEnv } from "../env.js";
3
4
  import type { GetAllInstances, GetInstanceById } from "../helpers/getTypes.js";
5
+ import type { TranslateMap } from "../helpers/translate.js";
4
6
  import type { GetAllResolvedNewSkillApplications, GetAllResolvedSelectOptions, GetAllResolvedSkillUses } from "../index.js";
5
7
  import { type GetResolvedSelectOptionById } from "./partial/prerequisites/single/activatable.js";
6
8
  type AdvancedSpecialAbilityKind = "AdvancedSkillSpecialAbility" | "AdvancedCombatSpecialAbility" | "AdvancedMagicalSpecialAbility" | "AdvancedKarmaSpecialAbility";
@@ -51,11 +53,12 @@ export type BaseActivatableTranslation = {
51
53
  * Get a JSON representation of the rules text for a special ability.
52
54
  */
53
55
  export declare const getActivatableEntityDescription: import("../creator.js").EntityDescriptionCreator<"Advantage" | "Disadvantage" | "AdvancedCombatSpecialAbility" | "AdvancedKarmaSpecialAbility" | "AdvancedMagicalSpecialAbility" | "AdvancedSkillSpecialAbility" | "AncestorGlyph" | "ArcaneOrbEnchantment" | "AttireEnchantment" | "Beutelzauber" | "BlessedTradition" | "BowlEnchantment" | "BrawlingSpecialAbility" | "CauldronEnchantment" | "CeremonialItemSpecialAbility" | "ChronicleEnchantment" | "CombatSpecialAbility" | "CombatStyleSpecialAbility" | "CommandSpecialAbility" | "DaggerRitual" | "FamiliarSpecialAbility" | "FatePointSexSpecialAbility" | "FatePointSpecialAbility" | "FoolsHatEnchantment" | "GeneralSpecialAbility" | "Haubenzauber" | "Hauerkettenzauber" | "InstrumentEnchantment" | "KarmaSpecialAbility" | "Keulenzauber" | "Krallenkettenzauber" | "Kristallkugelzauber" | "LiturgicalStyleSpecialAbility" | "LycantropicGift" | "MagicalSign" | "MagicalSpecialAbility" | "MagicalTradition" | "MagicStyleSpecialAbility" | "OrbEnchantment" | "PactGift" | "ProtectiveWardingCircleSpecialAbility" | "RingEnchantment" | "Schweinetrommelzauber" | "Sermon" | "SexSpecialAbility" | "SickleRitual" | "SikaryanDrainSpecialAbility" | "SkillStyleSpecialAbility" | "SpellSwordEnchantment" | "StaffEnchantment" | "ToyEnchantment" | "Trinkhornzauber" | "VampiricGift" | "Vision" | "WandEnchantment" | "WeaponEnchantment", {
54
- getInstanceById: GetInstanceById<"Publication" | "Subject" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "TradeSecret" | "Aspect" | "Property" | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Weapon" | "Patron" | "PersonalityTrait" | "Brew" | "Blessing" | "Cantrip" | "FocusRule">;
56
+ getInstanceById: GetInstanceById<"Publication" | "Subject" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "TradeSecret" | "Aspect" | "Property" | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Weapon" | "Patron" | "PersonalityTrait" | "Brew" | "Blessing" | "Cantrip" | "FocusRule" | "CeremonialItemSpecialAbilityGroup">;
55
57
  getAllInstances: GetAllInstances<"Script">;
56
58
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
57
59
  getAllResolvedSelectOptions: GetAllResolvedSelectOptions;
58
60
  getAllResolvedNewSkillApplications: GetAllResolvedNewSkillApplications;
59
61
  getAllResolvedSkillUses: GetAllResolvedSkillUses;
60
- }, import("../index.js").EntityDescription>;
62
+ translateMap: TranslateMap;
63
+ } & StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
61
64
  export {};
@@ -22,7 +22,7 @@ import { getDerivedFocusRuleBadgeFromPrerequisites, printAdvantageDisadvantagePr
22
22
  import { printActivatableName, } from "./partial/prerequisites/single/activatable.js";
23
23
  import { renderStandaloneCostMap } from "./partial/rated/activatable/cost.js";
24
24
  import { parensIf } from "./partial/rated/activatable/parensIf.js";
25
- import { attributedNameR, localeSortR, nameR, translateR } from "./partial/reader.js";
25
+ import { attributedNameR, customNameR, localeSortR, nameR, translateR } from "./partial/reader.js";
26
26
  import { getResponsiveText, getResponsiveTextOptional, ResponsiveTextSize, } from "./partial/responsiveText.js";
27
27
  import { formatTimeSpan } from "./partial/units/timeSpan.js";
28
28
  import { MISSING_VALUE } from "./partial/unknown.js";
@@ -44,6 +44,10 @@ const renderAdvantageDisadvantageSubtype = (subtype) => {
44
44
  return translateR("Magical Rank");
45
45
  case "MagicalTitle":
46
46
  return translateR("Magical Title");
47
+ case "BlessedRank":
48
+ return translateR("Blessed Title");
49
+ case "BlessedTitle":
50
+ return translateR("Blessed Title");
47
51
  default:
48
52
  return assertExhaustive(subtype);
49
53
  }
@@ -311,7 +315,9 @@ const renderVolumeValue = (translate, translateMap, getAllResolvedSelectOptions,
311
315
  levels: volume.ByLevel.list.map((_, index) => romanize(index + 1)).join("/"),
312
316
  });
313
317
  case "Map":
314
- return renderParameterMap(volume.Map, option => Reader.of(option.points), values => translateR("{$points} points", { points: values })).run({
318
+ return renderParameterMap(volume.Map, option => Reader.of(option.points), values => translateR("{$points} points", {
319
+ points: typeof values === "number" ? values.toFixed() : values,
320
+ })).run({
315
321
  translate,
316
322
  translateMap,
317
323
  responsiveTextSize,
@@ -342,7 +348,7 @@ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, res
342
348
  ? value => translate(".input {$value :number} {{{$value} permanent AE}}", {
343
349
  value,
344
350
  })
345
- : value => translate("{$value} AE", { value });
351
+ : value => translate("{$value} AE", { value: value.toFixed() });
346
352
  const { interval } = cost.Fixed;
347
353
  const wrapInInterval = interval === undefined
348
354
  ? str => str
@@ -370,40 +376,40 @@ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, res
370
376
  return wrapInPerLevel(value => wrapInInterval(translationWrapper(value))) + noteInParens;
371
377
  }
372
378
  case "Constant":
373
- return (translate("{$value} AE", { value: cost.Constant.value }) +
379
+ return (translate("{$value} AE", { value: cost.Constant.value.toFixed() }) +
374
380
  (cost.Constant.permanent_value === undefined
375
381
  ? ""
376
382
  : translate(", {$value} of which are permanent", {
377
- value: cost.Constant.permanent_value,
383
+ value: cost.Constant.permanent_value.toFixed(),
378
384
  })));
379
385
  case "PerCountable": {
380
386
  const translation = translateMap(cost.PerCountable.translations);
381
387
  return ((cost.PerCountable.base_value === undefined
382
388
  ? ""
383
- : `${translate("{$value} AE", { value: cost.PerCountable.base_value })} + `) +
389
+ : `${translate("{$value} AE", { value: cost.PerCountable.base_value.toFixed() })} + `) +
384
390
  translate("{$cost} per {$countable}", {
385
- cost: translate("{$value} AE", { value: cost.PerCountable.value }),
391
+ cost: translate("{$value} AE", { value: cost.PerCountable.value.toFixed() }),
386
392
  countable: getResponsiveText(translation?.per, responsiveTextSize),
387
393
  }) +
388
394
  parensIf(mapNullable(translation?.note, note => getResponsiveTextOptional(note, responsiveTextSize))));
389
395
  }
390
396
  case "Interval":
391
397
  return translate("{$cost} per {$interval}", {
392
- cost: translate("{$value} AE", { value: cost.Interval.value }),
398
+ cost: translate("{$value} AE", { value: cost.Interval.value.toFixed() }),
393
399
  interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.Interval.interval.unit, cost.Interval.interval.value, true),
394
400
  });
395
401
  case "ActivationAndHalfInterval":
396
402
  return additionFormatter(translate("{$value} AE", {
397
- value: cost.ActivationAndHalfInterval.value,
403
+ value: cost.ActivationAndHalfInterval.value.toFixed(),
398
404
  }) + parensIf(translate("activation")), translate("{$cost} per {$interval}", {
399
405
  cost: translate("{$value} AE", {
400
- value: Math.round(cost.ActivationAndHalfInterval.value / 2),
406
+ value: Math.round(cost.ActivationAndHalfInterval.value / 2).toFixed(),
401
407
  }),
402
408
  interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.ActivationAndHalfInterval.interval.unit, cost.ActivationAndHalfInterval.interval.value, true),
403
409
  }));
404
410
  case "Indefinite":
405
411
  return ((mapNullable(translateMap(cost.Indefinite.translations)?.description, description => getResponsiveText(description, responsiveTextSize)) ?? MISSING_VALUE) +
406
- mapNullableDefault(cost.Indefinite.modifier, modifier => ` + ${translate("{$value} AE", { value: modifier.value })}`, ""));
412
+ mapNullableDefault(cost.Indefinite.modifier, modifier => ` + ${translate("{$value} AE", { value: modifier.value.toFixed() })}`, ""));
407
413
  case "Disjunction":
408
414
  return translate("{$value} AE", {
409
415
  value: localeJoin(cost.Disjunction.options.map(option => option.value.toFixed() +
@@ -430,7 +436,7 @@ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, res
430
436
  case "Verbose":
431
437
  return cost.ByLevel.levels
432
438
  .map((level, index) => translate("{$cost} for level {$level}", {
433
- cost: translate("{$value} AE", { value: level.value }),
439
+ cost: translate("{$value} AE", { value: level.value.toFixed() }),
434
440
  level: romanize(index + 1),
435
441
  }))
436
442
  .join("; ");
@@ -454,7 +460,9 @@ const renderBindingCost = (translate, translateMap, localeCompare, getAllResolve
454
460
  }),
455
461
  });
456
462
  case "Map":
457
- return renderParameterMap(cost.Map, option => Reader.of(option.permanentValue), values => translateR("{$value} permanent AE", { value: values })).run({
463
+ return renderParameterMap(cost.Map, option => Reader.of(option.permanentValue), values => translateR("{$value} permanent AE", {
464
+ value: typeof values === "number" ? values.toFixed() : values,
465
+ })).run({
458
466
  translate,
459
467
  translateMap,
460
468
  responsiveTextSize,
@@ -481,12 +489,12 @@ const renderBindingCost = (translate, translateMap, localeCompare, getAllResolve
481
489
  const renderLifePointsCost = (translate, cost) => cost === undefined
482
490
  ? ""
483
491
  : // eslint-disable-next-line no-irregular-whitespace
484
- ` (+ ${translate("{$value} LP", { value: cost.Fixed.value })})`;
492
+ ` (+ ${translate("{$value} LP", { value: cost.Fixed.value.toFixed() })})`;
485
493
  const renderCost = (translate, translateMap, format, localeJoin, localeCompare, getAllResolvedSelectOptions, responsiveTextSize, levels, cost) => {
486
494
  if (typeof cost === "number") {
487
495
  return {
488
496
  label: translate("AE Cost"),
489
- value: translate("{$value} AE", { value: cost }),
497
+ value: translate("{$value} AE", { value: cost.toFixed() }),
490
498
  };
491
499
  }
492
500
  switch (cost.kind) {
@@ -823,6 +831,12 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator(({
823
831
  }
824
832
  : name => `${name} I–${romanize(levels)}`;
825
833
  return {
834
+ category: entityName === "CeremonialItemSpecialAbility" && entry.associatedItem !== undefined
835
+ ? {
836
+ label: attributedNameR("category", "CeremonialItemSpecialAbilityGroup", entry.associatedItem).map(name => name ?? MISSING_VALUE),
837
+ value: customNameR(t => t.nameForSorting, "CeremonialItemSpecialAbilityGroup", entry.associatedItem).map(name => name ?? MISSING_VALUE),
838
+ }
839
+ : undefined,
826
840
  title: addLevels(makeTraditionName(translation.name_in_library ?? translation.name + parensIf(translation.additionalName))),
827
841
  subtitle: mapNullable(baseEntry.subtype, subtype => renderAdvantageDisadvantageSubtype(subtype).run(env)) ??
828
842
  mapNullable(baseEntry.usage_type, usageType => renderCombatSpecialAbilityType(usageType).run(env)),
@@ -4,4 +4,4 @@ import type { GetInstanceById } from "../helpers/getTypes.js";
4
4
  */
5
5
  export declare const getAlternativeRuleEntityDescription: import("../creator.js").EntityDescriptionCreator<"AlternativeRule", {
6
6
  getInstanceById: GetInstanceById<"Publication" | "PlayerType">;
7
- }, import("../index.js").EntityDescription>;
7
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -1,6 +1,4 @@
1
1
  /**
2
2
  * Get a JSON representation of the rules text for an attribute.
3
3
  */
4
- export declare const getAttributeEntityDescription: import("../creator.js").EntityDescriptionCreator<"Attribute", {
5
- getInstanceById: import("../helpers/getTypes.js").GetInstanceById<"Publication">;
6
- }, import("../index.js").EntityDescription>;
4
+ export declare const getAttributeEntityDescription: import("../creator.js").EntityDescriptionCreator<"Attribute", import("@elyukai/utils/nullable").AnyNonNullish & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -4,10 +4,10 @@ import type { GetInstanceById } from "../helpers/getTypes.js";
4
4
  */
5
5
  export declare const getCloseCombatTechniqueEntityDescription: import("../creator.js").EntityDescriptionCreator<"CloseCombatTechnique", {
6
6
  getInstanceById: GetInstanceById<"Publication" | "Attribute">;
7
- }, import("../index.js").EntityDescription>;
7
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
8
8
  /**
9
9
  * Get a JSON representation of the rules text for a ranged combat technique.
10
10
  */
11
11
  export declare const getRangedCombatTechniqueEntityDescription: import("../creator.js").EntityDescriptionCreator<"RangedCombatTechnique", {
12
12
  getInstanceById: GetInstanceById<"Publication" | "Attribute">;
13
- }, import("../index.js").EntityDescription>;
13
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -1,12 +1,8 @@
1
1
  /**
2
2
  * Get a JSON representation of the rules text for a condition.
3
3
  */
4
- export declare const getConditionEntityDescription: import("../creator.js").EntityDescriptionCreator<"Condition", {
5
- getInstanceById: import("../helpers/getTypes.js").GetInstanceById<"Publication">;
6
- }, import("../index.js").EntityDescription>;
4
+ export declare const getConditionEntityDescription: import("../creator.js").EntityDescriptionCreator<"Condition", import("@elyukai/utils/nullable").AnyNonNullish & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
7
5
  /**
8
6
  * Get a JSON representation of the rules text for a meta condition.
9
7
  */
10
- export declare const getMetaConditionEntityDescription: import("../creator.js").EntityDescriptionCreator<"MetaCondition", {
11
- getInstanceById: import("../helpers/getTypes.js").GetInstanceById<"Publication">;
12
- }, import("../index.js").EntityDescription>;
8
+ export declare const getMetaConditionEntityDescription: import("../creator.js").EntityDescriptionCreator<"MetaCondition", import("@elyukai/utils/nullable").AnyNonNullish & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -1,3 +1,4 @@
1
+ import type { StdEnv } from "../env.js";
1
2
  import type { GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js";
2
3
  import type { GetResolvedSelectOptionById } from "./partial/prerequisites/single/activatable.js";
3
4
  /**
@@ -7,4 +8,4 @@ export declare const getCultureEntityDescription: import("../creator.js").Entity
7
8
  getInstanceById: GetInstanceById<"Publication" | "Advantage" | "Disadvantage" | "Skill" | "Language" | "LanguageSpecialization" | "Script" | "SocialStatus" | "ProfessionVariant" | "MagicalTradition" | "BlessedTradition">;
8
9
  getChildInstancesForInstanceId: GetAllChildInstancesForParent<"ProfessionVersion">;
9
10
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
10
- }, import("../index.js").EntityDescription>;
11
+ } & StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -248,14 +248,14 @@ export const getCultureEntityDescription = createEntityDescriptionCreator(({ get
248
248
  if (allSame(processedScripts, on(([, apValue]) => apValue, equal))) {
249
249
  return (localeJoin(processedScripts.map(([name]) => name).toSorted(localeCompare), "disjunction") +
250
250
  parensIf(translate("{$value} AP", {
251
- value: processedScripts[0][1],
251
+ value: processedScripts[0][1].toFixed(),
252
252
  })));
253
253
  }
254
254
  else {
255
255
  return localeJoin(processedScripts
256
256
  .map(([name, apValue]) => name +
257
257
  parensIf(translate("{$value} AP", {
258
- value: apValue,
258
+ value: apValue.toFixed(),
259
259
  })))
260
260
  .toSorted(localeCompare), "disjunction");
261
261
  }
@@ -306,7 +306,7 @@ export const getCultureEntityDescription = createEntityDescriptionCreator(({ get
306
306
  type: "labeled",
307
307
  label: translate("Cultural Package {$cultureName}", {
308
308
  cultureName: translation.name,
309
- }) + parensIf(translate("{$value} AP", { value: culturalPackageApValue })),
309
+ }) + parensIf(translate("{$value} AP", { value: culturalPackageApValue.toFixed() })),
310
310
  value: {
311
311
  type: "plain",
312
312
  text: culturePackageText,
@@ -8,4 +8,4 @@ export declare const getCurriculumEntityDescription: import("../creator.js").Ent
8
8
  getAllInstances: GetAllInstances<"Profession">;
9
9
  getChildInstancesForInstanceId: GetAllChildInstancesForParent<"LessonPackage" | "ProfessionVersion" | "ProfessionPackage">;
10
10
  idMap: IdMap;
11
- }, import("../index.js").EntityDescription>;
11
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -183,7 +183,7 @@ const renderAbilityAdjustment = (translate, translateMap, getInstanceById, baseP
183
183
  const printedBaseValue = mapNullable(renderPrintedAbilityAdjustmentBaseValue(abilityAdjustment), value => value + defaultValue);
184
184
  return translate("{$replacement} instead of {$base}", {
185
185
  base: printedBaseValue === basePoints
186
- ? basePoints
186
+ ? basePoints.toFixed()
187
187
  : `<ins>${basePoints.toFixed()}</ins><del>${printedBaseValue?.toFixed() ?? "n/a"}</del>`,
188
188
  replacement: `${renderAbilityAdjustmentName(translateMap, getInstanceById, abilityAdjustment)} ${(basePoints + renderAbilityAdjustmentModifierValue(abilityAdjustment)).toFixed()}`,
189
189
  });
@@ -4,4 +4,4 @@ import type { GetInstanceById } from "../helpers/getTypes.js";
4
4
  */
5
5
  export declare const getDerivedCharacteristicEntityDescription: import("../creator.js").EntityDescriptionCreator<"DerivedCharacteristic", {
6
6
  getInstanceById: GetInstanceById<"Publication" | "Attribute" | "DerivedCharacteristic">;
7
- }, import("../index.js").EntityDescription>;
7
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -6,4 +6,4 @@ import type { IdMap } from "../index.js";
6
6
  export declare const getDiseaseEntityDescription: import("../creator.js").EntityDescriptionCreator<"Disease" | "AnimalDisease", {
7
7
  getInstanceById: GetInstanceById<"Publication" | "AnimalType" | "DerivedCharacteristic">;
8
8
  idMap: IdMap;
9
- }, import("../index.js").EntityDescription>;
9
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -7,4 +7,4 @@ import type { GetResolvedSelectOptionById } from "./partial/prerequisites/single
7
7
  export declare const getElixirEntityDescription: import("../creator.js").EntityDescriptionCreator<"Elixir", {
8
8
  getInstanceById: GetInstanceById<"Publication" | "DerivedCharacteristic" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Aspect" | "Property" | "PersonalityTrait" | "Blessing" | "Cantrip">;
9
9
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
10
- }, import("../index.js").EntityDescription>;
10
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -54,7 +54,7 @@ export const getElixirEntityDescription = createEntityDescriptionCreator(({ getI
54
54
  {
55
55
  label: `${translate("AP Value")} (${translate("Trade Secret")})`,
56
56
  value: translate("{$value} AP", {
57
- value: entry.trade_secret.ap_value,
57
+ value: entry.trade_secret.ap_value.toFixed(),
58
58
  }) +
59
59
  parensIf(mapNullable(entry.trade_secret.prerequisites, prerequisites => `${translate("Prerequisites")}: ${printPlainGeneralPrerequisites(prerequisites).run(env)}`)),
60
60
  },
@@ -1,9 +1,10 @@
1
1
  import type { AmmunitionishIdentifier, EquipmentIdentifier, GenMeleeWeapon, GenRangedWeapon, LocaleMeasurementAdjustments } from "@optolith/database-schema/gen";
2
2
  import { type TaggedEntity } from "../creator.js";
3
+ import type { StdEnv } from "../env.js";
3
4
  import type { GetInstanceById } from "../helpers/getTypes.js";
4
5
  import type { LocaleJoin } from "../helpers/locale.js";
5
- import type { Format, Translate, TranslateMap } from "../helpers/translate.js";
6
- import type { IdMap, LabeledEntityDescriptionSection, RawEntityDescriptionSectionContent } from "../index.js";
6
+ import { type Format, type Translate, type TranslateMap } from "../helpers/translate.js";
7
+ import { type LabeledEntityDescriptionSection, type RawEntityDescriptionSectionContent } from "../index.js";
7
8
  /**
8
9
  * Get the name of an equipment item.
9
10
  */
@@ -19,7 +20,4 @@ export declare const renderRangedWeapon: <Damage>(translate: Translate, translat
19
20
  /**
20
21
  * Get a JSON representation of the rules text for equipment.
21
22
  */
22
- export declare const getEquipmentEntityDescription: import("../creator.js").EntityDescriptionCreator<"Weapon" | "Ammunition" | "Armor" | "Animal" | "AnimalCare" | "BandageOrRemedy" | "Book" | "CeremonialItem" | "Clothes" | "ClothingPackage" | "Container" | "EquipmentOfBlessedOnes" | "GemOrPreciousStone" | "IlluminationLightSource" | "IlluminationRefillOrSupply" | "Jewelry" | "Laboratory" | "Liebesspielzeug" | "LuxuryGood" | "MagicalArtifact" | "MusicalInstrument" | "Newspaper" | "OrienteeringAid" | "RopeOrChain" | "Stationery" | "ThievesTool" | "ToolOfTheTrade" | "TravelGearOrTool" | "Vehicle" | "WeaponAccessory" | "WorkingSupernaturalCreature", {
23
- getInstanceById: GetInstanceById<"Publication" | "Attribute" | "Reach" | "SocialStatus" | "CloseCombatTechnique" | "RangedCombatTechnique" | "MagicalTradition" | "BlessedTradition" | "DerivedCharacteristic" | AmmunitionishIdentifier["kind"] | "Race" | "Culture" | "Profession" | "Skill">;
24
- idMap: IdMap;
25
- }, import("../index.js").EntityDescription>;
23
+ export declare const getEquipmentEntityDescription: import("../creator.js").EntityDescriptionCreator<"Weapon" | "Ammunition" | "Armor" | "Animal" | "AnimalCare" | "BandageOrRemedy" | "Book" | "CeremonialItem" | "Clothes" | "ClothingPackage" | "Container" | "EquipmentOfBlessedOnes" | "GemOrPreciousStone" | "IlluminationLightSource" | "IlluminationRefillOrSupply" | "Jewelry" | "Laboratory" | "Liebesspielzeug" | "LuxuryGood" | "MagicalArtifact" | "MusicalInstrument" | "Newspaper" | "OrienteeringAid" | "RopeOrChain" | "Stationery" | "ThievesTool" | "ToolOfTheTrade" | "TravelGearOrTool" | "Vehicle" | "WeaponAccessory" | "WorkingSupernaturalCreature", StdEnv<"f" | "fn" | "t" | "tm" | "lj" | "lc" | "rts" | "ibi" | "ma" | "idm", "BlessedTradition" | "MagicalTradition" | "Publication" | "Race" | "Culture" | "SocialStatus" | "Attribute" | "Skill" | "CloseCombatTechnique" | "RangedCombatTechnique" | "Weapon" | "Profession" | "Ammunition" | "DerivedCharacteristic" | "Reach"> & StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;