@optolith/entity-descriptions 0.4.14 → 0.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
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.4.16](https://github.com/Optolith/entity-descriptions/compare/v0.4.15...v0.4.16) (2026-06-08)
6
+
7
+
8
+ ### Features
9
+
10
+ * render animist power skill check penalty ([ef7751a](https://github.com/Optolith/entity-descriptions/commit/ef7751ad9f9b995f979d973522c7f27e65ba545c))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * swap personality trait prerequisite texts ([abf4ff6](https://github.com/Optolith/entity-descriptions/commit/abf4ff6cbc8b8b09a0436741947ebf9b15cc9974))
16
+
17
+ ## [0.4.15](https://github.com/Optolith/entity-descriptions/compare/v0.4.14...v0.4.15) (2026-06-07)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * add sustained cost map interval ([f7c1b31](https://github.com/Optolith/entity-descriptions/commit/f7c1b31452befa0c898a6f21e4624c8084ea5df5))
23
+ * display full tradition name ([2d07eff](https://github.com/Optolith/entity-descriptions/commit/2d07eff1d4b6a5b72ce705e24969e4a384a1d486))
24
+ * ignore rating in prerequisite when zero ([b375702](https://github.com/Optolith/entity-descriptions/commit/b375702ea644fc21b92bb64b811e204aeda7370c))
25
+ * render ap value from explicit select options ([df2178f](https://github.com/Optolith/entity-descriptions/commit/df2178fe131ed9e9d3427ee092a46b2448b09650))
26
+
5
27
  ## [0.4.14](https://github.com/Optolith/entity-descriptions/compare/v0.4.13...v0.4.14) (2026-06-07)
6
28
 
7
29
 
@@ -47,7 +47,7 @@ export type BaseActivatableTranslation = {
47
47
  * Get a JSON representation of the rules text for a special ability.
48
48
  */
49
49
  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" | "InstrumentEnchantment" | "KarmaSpecialAbility" | "Krallenkettenzauber" | "Kristallkugelzauber" | "LiturgicalStyleSpecialAbility" | "LycantropicGift" | "MagicalSign" | "MagicalSpecialAbility" | "MagicalTradition" | "MagicStyleSpecialAbility" | "OrbEnchantment" | "PactGift" | "ProtectiveWardingCircleSpecialAbility" | "RingEnchantment" | "Sermon" | "SexSpecialAbility" | "SickleRitual" | "SikaryanDrainSpecialAbility" | "SkillStyleSpecialAbility" | "SpellSwordEnchantment" | "StaffEnchantment" | "ToyEnchantment" | "Trinkhornzauber" | "VampiricGift" | "Vision" | "WandEnchantment" | "WeaponEnchantment", {
50
- getInstanceById: GetInstanceById<"Publication" | "Subject" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "TradeSecret" | "Aspect" | "Property" | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Weapon" | "Patron">;
50
+ getInstanceById: GetInstanceById<"Publication" | "Subject" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "TradeSecret" | "Aspect" | "Property" | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Weapon" | "Patron" | "PersonalityTrait">;
51
51
  getAllInstances: GetAllInstances<"Script">;
52
52
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
53
53
  getAllResolvedSelectOptions: GetAllResolvedSelectOptions;
@@ -705,8 +705,11 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator(({
705
705
  : entityName === "MagicalTradition"
706
706
  ? renderAdditionalSpecialRules(translation.name, translation.nameOfSpellcasters, undefined, undefined, undefined, entry.primary).run(env)
707
707
  : [];
708
+ const makeTraditionName = entityName === "BlessedTradition" || entityName === "MagicalTradition"
709
+ ? name => translate("Tradition ({$tradition})", { tradition: name })
710
+ : name => name;
708
711
  return {
709
- title: (translation.name_in_library ?? translation.name) +
712
+ title: makeTraditionName(translation.name_in_library ?? translation.name) +
710
713
  (baseEntry.levels !== undefined ? ` I–${romanize(baseEntry.levels)}` : ""),
711
714
  subtitle: mapNullable(baseEntry.usage_type, usageType => {
712
715
  switch (usageType.kind) {
@@ -5,6 +5,6 @@ import type { GetResolvedSelectOptionById } from "./partial/prerequisites/single
5
5
  * Get a JSON representation of the rules text for an elixir.
6
6
  */
7
7
  export declare const getElixirEntityDescription: import("../creator.js").EntityDescriptionCreator<"Elixir", {
8
- getInstanceById: GetInstanceById<"Publication" | "DerivedCharacteristic" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Aspect" | "Property">;
8
+ getInstanceById: GetInstanceById<"Publication" | "DerivedCharacteristic" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Aspect" | "Property" | "PersonalityTrait">;
9
9
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
10
10
  }, import("../index.js").EntityDescription>;
@@ -6,6 +6,35 @@ import { romanize } from "@optolith/helpers/roman";
6
6
  import { assertExhaustive } from "@optolith/helpers/typeSafety";
7
7
  import { evaluateMathOperation } from "./mathOperation.js";
8
8
  import { MISSING_VALUE } from "./unknown.js";
9
+ const typographicSign = (num) => (num > 0 ? "" : "−") + Math.abs(num).toFixed();
10
+ const renderFixedSelectOptionsAdventurePointsValue = (locale, getAllSelectOptions, getNameForSelectOptionId, getApValueForSelectOption, applyNegative) => {
11
+ const entriesMap = Map.groupBy(getAllSelectOptions().map((item) => [
12
+ getApValueForSelectOption(item, item.id),
13
+ item,
14
+ ]), item => item[0])
15
+ .entries()
16
+ .toArray();
17
+ if (entriesMap.every((entry) => typeof entry[0] === "number" && entry[1].length === 1)) {
18
+ const entriesSortedByApValue = entriesMap.toSorted(on(item => item[0], numAsc));
19
+ return entriesSortedByApValue
20
+ .map(entry => locale.translateMap(entry[1][0][1].content.translations)?.name ?? MISSING_VALUE)
21
+ .join("/")
22
+ .concat(`: ${locale.translate("{$value} Adventure Points", { value: entriesSortedByApValue.map(entry => typographicSign(applyNegative(entry[0]))).join("/") })}`);
23
+ }
24
+ else {
25
+ return entriesMap
26
+ .map(([apValue, items]) => [
27
+ apValue,
28
+ locale.join(items
29
+ .map(item => getNameForSelectOptionId(item[1].id))
30
+ .filter(name => name !== undefined)
31
+ .toSorted(locale.compare), "conjunction"),
32
+ ])
33
+ .toSorted(on(item => item[1], locale.compare))
34
+ .map(([apValue, itemNames]) => `${itemNames}: ${locale.translate("{$value} Adventure Points", { value: typeof apValue === "number" ? applyNegative(apValue) : apValue })}`)
35
+ .join("; ");
36
+ }
37
+ };
9
38
  const renderSelectOptionsAdventurePointsValue = (locale, derivedLabel, getAllSelectOptions, getNameForSelectOptionId, config, applyNegative) => {
10
39
  if (config === undefined) {
11
40
  return MISSING_VALUE;
@@ -19,23 +48,8 @@ const renderSelectOptionsAdventurePointsValue = (locale, derivedLabel, getAllSel
19
48
  })}`;
20
49
  }
21
50
  case "Fixed":
22
- return Map.groupBy(getAllSelectOptions().map((item) => [
23
- config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))?.ap_value ??
24
- config.Fixed.default,
25
- item,
26
- ]), item => item[0])
27
- .entries()
28
- .map(([apValue, items]) => [
29
- apValue,
30
- locale.join(items
31
- .map(item => getNameForSelectOptionId(item[1].id))
32
- .filter(name => name !== undefined)
33
- .toSorted(locale.compare), "conjunction"),
34
- ])
35
- .toArray()
36
- .toSorted(on(item => item[1], locale.compare))
37
- .map(([apValue, itemNames]) => `${itemNames}: ${locale.translate("{$value} Adventure Points", { value: applyNegative(apValue) })}`)
38
- .join("; ");
51
+ return renderFixedSelectOptionsAdventurePointsValue(locale, getAllSelectOptions, getNameForSelectOptionId, item => config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))?.ap_value ??
52
+ config.Fixed.default, applyNegative);
39
53
  default:
40
54
  return assertExhaustive(config);
41
55
  }
@@ -89,7 +103,25 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
89
103
  case "DerivedFromSelection": {
90
104
  const derivedSelectOptions = entry.select_options?.derived;
91
105
  if (derivedSelectOptions === undefined) {
92
- return MISSING_VALUE;
106
+ return renderFixedSelectOptionsAdventurePointsValue(locale, getAllSelectOptions, getNameForSelectOptionId, item => {
107
+ switch (item.content.ap_value?.kind) {
108
+ case "Fixed":
109
+ return item.content.ap_value.Fixed;
110
+ case "DependingOnActiveInstances":
111
+ switch (item.content.ap_value.DependingOnActiveInstances.kind) {
112
+ case "Threshold":
113
+ return item.content.ap_value.DependingOnActiveInstances.Threshold.normal;
114
+ case "Expression":
115
+ return MISSING_VALUE;
116
+ default:
117
+ return assertExhaustive(item.content.ap_value.DependingOnActiveInstances);
118
+ }
119
+ case undefined:
120
+ return MISSING_VALUE;
121
+ default:
122
+ return assertExhaustive(item.content.ap_value);
123
+ }
124
+ }, applyNegative);
93
125
  }
94
126
  switch (derivedSelectOptions.kind) {
95
127
  case "Blessings":
@@ -13,11 +13,11 @@ export declare const printPublicationPrerequisites: (getInstanceById: GetInstanc
13
13
  /**
14
14
  * Print plain general prerequisites as a string.
15
15
  */
16
- export declare const printPlainGeneralPrerequisites: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, value: PlainGeneralPrerequisites) => string;
16
+ export declare const printPlainGeneralPrerequisites: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement" | "PersonalityTrait">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, value: PlainGeneralPrerequisites) => string;
17
17
  /**
18
18
  * Print general prerequisites as a string.
19
19
  */
20
- export declare const printGeneralPrerequisites: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, value: GeneralPrerequisites, printPreviousLevelPrerequisites?: {
20
+ export declare const printGeneralPrerequisites: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement" | "PersonalityTrait">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, value: GeneralPrerequisites, printPreviousLevelPrerequisites?: {
21
21
  id: ActivatableIdentifier;
22
22
  levels: number;
23
23
  }, trailingText?: string) => string;
@@ -28,7 +28,7 @@ export declare const printProfessionPrerequisites: (getInstanceById: GetInstance
28
28
  /**
29
29
  * Print advantage disadvantage prerequisites as a string.
30
30
  */
31
- export declare const printAdvantageDisadvantagePrerequisites: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, value: AdvantageDisadvantagePrerequisites, name: string, type: "Advantage" | "Disadvantage") => string;
31
+ export declare const printAdvantageDisadvantagePrerequisites: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement" | "PersonalityTrait">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, value: AdvantageDisadvantagePrerequisites, name: string, type: "Advantage" | "Disadvantage") => string;
32
32
  /**
33
33
  * Print arcane tradition prerequisites as a string.
34
34
  */
@@ -14,7 +14,7 @@ export declare const printPublicationPrerequisiteGroup: (getInstanceById: GetIns
14
14
  /**
15
15
  * Print the translation of a general prerequisite group.
16
16
  */
17
- export declare const printGeneralPrerequisiteGroup: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, prerequisite: GeneralPrerequisiteGroup) => PrerequisitePart | undefined;
17
+ export declare const printGeneralPrerequisiteGroup: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement" | "PersonalityTrait">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, prerequisite: GeneralPrerequisiteGroup) => PrerequisitePart | undefined;
18
18
  /**
19
19
  * Print the translation of a profession prerequisite group.
20
20
  */
@@ -22,7 +22,7 @@ export declare const printProfessionPrerequisiteGroup: (getInstanceById: GetInst
22
22
  /**
23
23
  * Print the translation of an advantage/disadvantage prerequisite group.
24
24
  */
25
- export declare const printAdvantageDisadvantagePrerequisiteGroup: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, prerequisite: AdvantageDisadvantagePrerequisiteGroup, name: string, type: "Advantage" | "Disadvantage") => PrerequisitePart | undefined;
25
+ export declare const printAdvantageDisadvantagePrerequisiteGroup: (getInstanceById: GetInstanceById<"Race" | "Culture" | "PactCategory" | "PactDomain" | "SocialStatus" | "State" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Property" | "Aspect" | "Enhancement" | "PersonalityTrait">, getResolvedSelectOptionById: GetResolvedSelectOptionById, locale: LocaleEnvironment, prerequisite: AdvantageDisadvantagePrerequisiteGroup, name: string, type: "Advantage" | "Disadvantage") => PrerequisitePart | undefined;
26
26
  /**
27
27
  * Print the translation of an arcane tradition prerequisite group.
28
28
  */
@@ -87,6 +87,8 @@ export const printGeneralPrerequisiteGroup = (getInstanceById, getResolvedSelect
87
87
  return printTextPrerequisite(locale, prerequisite.Text);
88
88
  case "SexualCharacteristic":
89
89
  return printSexualCharacteristicPrerequisite(locale, prerequisite.SexualCharacteristic);
90
+ case "PersonalityTrait":
91
+ return printPersonalityTraitPrerequisite(getInstanceById, locale, prerequisite.PersonalityTrait);
90
92
  default:
91
93
  return assertExhaustive(prerequisite);
92
94
  }
@@ -136,6 +138,7 @@ export const printAdvantageDisadvantagePrerequisiteGroup = (getInstanceById, get
136
138
  case "Enhancement":
137
139
  case "Text":
138
140
  case "SexualCharacteristic":
141
+ case "PersonalityTrait":
139
142
  return printGeneralPrerequisiteGroup(getInstanceById, getResolvedSelectOptionById, locale, prerequisite);
140
143
  default:
141
144
  return assertExhaustive(prerequisite);
@@ -17,10 +17,10 @@ export const printPersonalityTraitPrerequisite = (getInstanceById, locale, prere
17
17
  })})`;
18
18
  return {
19
19
  value: prerequisite.active
20
- ? locale.translate("cannot be chosen at the same time as {$trait}", {
20
+ ? locale.translate("must have {$trait}", {
21
21
  trait: name,
22
22
  })
23
- : locale.translate("must have {$trait}", {
23
+ : locale.translate("cannot be chosen at the same time as {$trait}", {
24
24
  trait: name,
25
25
  }),
26
26
  sentenceType: undefined,
@@ -8,8 +8,9 @@ export const printRatedPrerequisite = (getInstanceById, locale, prerequisite) =>
8
8
  if (prerequisite.display_option !== undefined) {
9
9
  return printDisplayOption(locale.translateMap, prerequisite.display_option);
10
10
  }
11
+ const name = attributedCustomName(locale.translateMap, getInstanceById, "prerequisite", (t) => t.abbreviation ?? t.name, prerequisite.id) ?? MISSING_VALUE;
11
12
  return {
12
- value: `${attributedCustomName(locale.translateMap, getInstanceById, "prerequisite", (t) => t.abbreviation ?? t.name, prerequisite.id) ?? MISSING_VALUE} ${prerequisite.value.toFixed()}`,
13
+ value: prerequisite.value > 0 ? `${name} ${prerequisite.value.toFixed()}` : name,
13
14
  sentenceType: undefined,
14
15
  isMeta: false,
15
16
  };
@@ -1,7 +1,8 @@
1
1
  import { Reader } from "@elyukai/utils/reader";
2
2
  import type { CheckResultBasedModifier, DurationUnitValue, ElvenMagicalSongPermanentCost, FirstPersonMagicalMelodyCost, NonModifiableOneTimeCostPerCountable, NonModifiableSustainedCost, OneTimeCost, OneTimeCostMap, ResponsiveText, ResponsiveTextOptional, ResponsiveTextReplace, SkillModificationLevel_ID, SustainedCost } from "@optolith/database-schema/gen";
3
- import { type LocaleMap } from "../../../../helpers/translate.js";
3
+ import { type LocaleMap, type Translate } from "../../../../helpers/translate.js";
4
4
  import { type StdEnv, type StdReader } from "../../reader.js";
5
+ import { type ResponsiveTextSize } from "../../responsiveText.js";
5
6
  /**
6
7
  * Returns the text for the modifiable one-time cost of an activatable skill.
7
8
  */
@@ -51,11 +52,11 @@ export declare const renderOneTimeCost: (value: OneTimeCost) => StdReader<string
51
52
  * Returns the text for the non-modifiable cost of a sustained activatable skill.
52
53
  */
53
54
  export declare const renderNonModifiableSustainedCost: (value: NonModifiableSustainedCost) => Reader<{
54
- translate: import("../../../../helpers/translate.js").Translate;
55
+ translate: Translate;
55
56
  energyUnit: import("../../units/energy.js").EnergyUnit;
56
57
  } & {
57
- translate: import("../../../../helpers/translate.js").Translate;
58
- responsiveTextSize: import("../../responsiveText.js").ResponsiveTextSize;
58
+ translate: Translate;
59
+ responsiveTextSize: ResponsiveTextSize;
59
60
  } & StdEnv<"t" | "rts" | "tm" | "eu", never, never, never>, string>;
60
61
  /**
61
62
  * Returns the text for the cost of a sustained activatable skill.
@@ -2,10 +2,11 @@ import { identity } from "@elyukai/utils/function";
2
2
  import { Reader } from "@elyukai/utils/reader";
3
3
  import { mapNullable } from "@optolith/helpers/nullable";
4
4
  import { assertExhaustive } from "@optolith/helpers/typeSafety";
5
+ import { responsiveTranslate, } from "../../../../helpers/translate.js";
5
6
  import { renderResponsiveMap } from "../../map.js";
6
7
  import { formatEnergyFnR, formatEnergyR, getInstanceByIdFnR, modifiableBySpeedR, responsiveLocaleJoinR, responsiveR, responsiveTextR, responsiveTranslateR, translateMapR, translateR, } from "../../reader.js";
7
- import { appendNoteIfNeeded, replaceTextIfNeeded } from "../../responsiveText.js";
8
- import { formatCombinedTimeSpanR } from "../../units/timeSpan.js";
8
+ import { appendNoteIfNeeded, replaceTextIfNeeded, } from "../../responsiveText.js";
9
+ import { formatCombinedTimeSpan, formatCombinedTimeSpanR } from "../../units/timeSpan.js";
9
10
  import { MISSING_VALUE } from "../../unknown.js";
10
11
  import { appendCheckResultModifier } from "./checkResultBased.js";
11
12
  import { wrapIfMinimum } from "./isMinimumMaximum.js";
@@ -57,12 +58,18 @@ export const renderModifiableOneTimeCost = (value) => deriveModifiableCost(value
57
58
  }
58
59
  return responsiveTextR(translation.additional).map(additional => `${text} + ${additional}`);
59
60
  })));
60
- const appendIntervalToCost = (interval, baseCost) => interval === undefined
61
+ const appendIntervalToCostR = (interval, baseCost) => interval === undefined
61
62
  ? Reader.of(baseCost)
62
63
  : formatCombinedTimeSpanR(interval, true).then(formattedInterval => responsiveTranslateR("{$cost} per {$interval}", "{$cost}/{$interval}", {
63
64
  cost: baseCost,
64
65
  interval: formattedInterval,
65
66
  }));
67
+ const appendIntervalToCost = (translate, responsiveTextSize, interval, baseCost) => interval === undefined
68
+ ? baseCost
69
+ : responsiveTranslate(translate, responsiveTextSize, "{$cost} per {$interval}", "{$cost}/{$interval}", {
70
+ cost: baseCost,
71
+ interval: formatCombinedTimeSpan(translate, responsiveTextSize, interval, true),
72
+ });
66
73
  /**
67
74
  * Returns the text for a non-modifiable one-time cost of an activatable skill.
68
75
  */
@@ -70,7 +77,7 @@ export const renderNonModifiableOneTimeCost = (value, shouldAppendNonModifiableS
70
77
  .thenW(base => appendFamiliarsTrickLPCostIfNeeded(value.lp_value, base))
71
78
  .thenW(base => appendPerCountableToCostIfNeeded(value.per, base))
72
79
  .then(base => appendPermanentCostIfNeeded(value.permanent_value, base))
73
- .then(base => appendIntervalToCost(value.interval, base))
80
+ .then(base => appendIntervalToCostR(value.interval, base))
74
81
  .then(base => appendElvenPermanentCostIfNeeded(value.permanent, base))
75
82
  .then(base => wrapIfMinimum(value.is_minimum, base))
76
83
  .then(base => appendNoteIfNeeded(value.translations, base))
@@ -126,7 +133,7 @@ export const renderOneTimeCostMap = (value) => Reader.asks(({ translate }) => tr
126
133
  : undefined))
127
134
  .then(text => appendNonModifiableSuffix(ModifiableParameter.Cost, text)));
128
135
  const renderSustainedCostMap = (value) => formatEnergyFnR
129
- .thenW(formatEnergy => renderResponsiveMap(value, option => option.value, formatEnergy))
136
+ .thenW(formatEnergy => Reader.ask().thenW(({ translate, responsiveTextSize }) => renderResponsiveMap(value, option => option.value, singleValue => appendIntervalToCost(translate, responsiveTextSize, value.interval, formatEnergy(singleValue)))))
130
137
  .then(text => appendNonModifiableSuffix(ModifiableParameter.Cost, text));
131
138
  /**
132
139
  * Returns the text for the cost of a one-time activatable skill.
@@ -147,7 +154,7 @@ export const renderOneTimeCost = (value) => {
147
154
  };
148
155
  const buildSustainedCost = (activationCost, intervalCost, interval) => {
149
156
  const activationCostWithLabel = responsiveR(() => translateR("activation").map(label => `${activationCost} (${label})`), () => Reader.of(activationCost)).thenW(identity);
150
- const intervalCostWithLabel = appendIntervalToCost(interval, intervalCost);
157
+ const intervalCostWithLabel = appendIntervalToCostR(interval, intervalCost);
151
158
  return activationCostWithLabel.map2(intervalCostWithLabel, (activationStr, intervalStr) => `${activationStr} + ${intervalStr}`);
152
159
  };
153
160
  const renderModifiableSustainedCost = (value) => deriveModifiableCost(value.initial_modification_level).thenW(cost => cost === undefined
@@ -12,6 +12,15 @@ export type TimeSpanUnit = "Seconds" | "Minutes" | "Hours" | "Days" | "Weeks" |
12
12
  export declare const formatTimeSpan: (translate: Translate, responsiveTextSize: ResponsiveTextSize, unit: {
13
13
  kind: TimeSpanUnit;
14
14
  } | TimeSpanUnit, value: number | string, interval?: boolean) => string;
15
+ /**
16
+ * Returns the text for a time span unit.
17
+ */
18
+ export declare const formatCombinedTimeSpan: (translate: Translate, responsiveTextSize: ResponsiveTextSize, object: {
19
+ unit: {
20
+ kind: TimeSpanUnit;
21
+ };
22
+ value: number | string;
23
+ }, interval?: boolean) => string;
15
24
  /**
16
25
  * Returns the text for a time span unit.
17
26
  */
@@ -28,6 +28,10 @@ export const formatTimeSpan = (translate, responsiveTextSize, unit, value, inter
28
28
  style: interval ? "interval" : "default",
29
29
  }));
30
30
  };
31
+ /**
32
+ * Returns the text for a time span unit.
33
+ */
34
+ export const formatCombinedTimeSpan = (translate, responsiveTextSize, object, interval = false) => formatTimeSpan(translate, responsiveTextSize, object.unit, object.value, interval);
31
35
  /**
32
36
  * Returns the text for a time span unit.
33
37
  */
@@ -6,7 +6,7 @@ import type { GetResolvedSelectOptionById } from "./partial/prerequisites/single
6
6
  * Get a JSON representation of the rules text for a poison.
7
7
  */
8
8
  export declare const getPoisonEntityDescription: import("../creator.js").EntityDescriptionCreator<"Poison", {
9
- getInstanceById: GetInstanceById<"Publication" | "DerivedCharacteristic" | "Disease" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Aspect" | "Property">;
9
+ getInstanceById: GetInstanceById<"Publication" | "DerivedCharacteristic" | "Disease" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Aspect" | "Property" | "PersonalityTrait">;
10
10
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
11
11
  idMap: IdMap;
12
12
  }, import("../index.js").EntityDescription>;
@@ -68,6 +68,7 @@ export declare const getFamiliarsTrickEntityDescription: import("../creator.js")
68
68
  */
69
69
  export declare const getAnimistPowerEntityDescription: import("../creator.js").EntityDescriptionCreator<"AnimistPower", {
70
70
  getInstanceById: GetInstanceById<"Publication" | "Attribute" | "SkillModificationLevel" | "TargetCategory" | "Property" | "MagicalTradition" | "DerivedCharacteristic" | "AnimistPower" | "Tribe">;
71
+ idMap: IdMap;
71
72
  }, import("../index.js").EntityDescription>;
72
73
  /**
73
74
  * Get a JSON representation of the rules text for a Goede ritual.
@@ -581,7 +581,7 @@ const renderAnimistPowerImprovementCost = (improvementCost) => {
581
581
  /**
582
582
  * Get a JSON representation of the rules text for a animist power.
583
583
  */
584
- export const getAnimistPowerEntityDescription = createEntityDescriptionCreator(({ getInstanceById }, locale, { content: entry }) => {
584
+ export const getAnimistPowerEntityDescription = createEntityDescriptionCreator(({ getInstanceById, idMap }, locale, { content: entry }) => {
585
585
  const { translate, translateMap } = locale;
586
586
  const translation = translateMap(entry.translations);
587
587
  if (translation === undefined) {
@@ -645,7 +645,7 @@ export const getAnimistPowerEntityDescription = createEntityDescriptionCreator((
645
645
  {
646
646
  type: "definitionList",
647
647
  items: [
648
- renderSkillCheck(entry.check).run(env),
648
+ renderSkillCheckWithPenalty(entry.check, entry.check_penalty, idMap).run(env),
649
649
  renderEffect(mergedEffect).run(env),
650
650
  combineGeneratedTextWithStaticTranslation(translate("AE Cost"), cost, translation.cost),
651
651
  combineGeneratedTextWithStaticTranslation(translate("Duration"), duration, translation.duration),