@optolith/entity-descriptions 0.8.0 → 0.9.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
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.9.0](https://github.com/Optolith/entity-descriptions/compare/v0.8.0...v0.9.0) (2026-09-11)
6
+
7
+ ### ⚠ BREAKING CHANGES
8
+
9
+ * **deps:** upgrade to schema 0.58.0
10
+
11
+ ### build
12
+
13
+ * **deps:** upgrade to schema 0.58.0 ([fe93cf7](https://github.com/Optolith/entity-descriptions/commit/fe93cf72768cf397a671bd4e883b1bd18288697c))
14
+
15
+ ### Features
16
+
17
+ * use nameInAPValue for animal shape text generation ([d1c8017](https://github.com/Optolith/entity-descriptions/commit/d1c801791532bdcf1004c67d1e8ae5cb53ae4f07))
18
+
19
+ ### Bug Fixes
20
+
21
+ * render brew for cauldron enchantments ([866703a](https://github.com/Optolith/entity-descriptions/commit/866703a4500a9d6e47a59cf78919aa9ebcbca05a))
22
+ * render translation replacement if available ([6f890fd](https://github.com/Optolith/entity-descriptions/commit/6f890fd9885c070934c32db5dc5b745a86b46ac8))
23
+ * time interval display when value is 1 ([ada5cc7](https://github.com/Optolith/entity-descriptions/commit/ada5cc768286ced1a26bacf18a4c16a3d289aa40))
5
24
  ## [0.8.0](https://github.com/Optolith/entity-descriptions/compare/v0.7.2...v0.8.0) (2026-09-10)
6
25
 
7
26
  ### ⚠ BREAKING CHANGES
@@ -1,4 +1,4 @@
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";
1
+ import type { ActivatableIdentifier, AdvancedSpecialAbility, AdvantageDisadvantagePrerequisites, 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
3
  import type { GetAllInstances, GetInstanceById } from "../helpers/getTypes.js";
4
4
  import type { GetAllResolvedNewSkillApplications, GetAllResolvedSelectOptions, GetAllResolvedSkillUses } from "../index.js";
@@ -17,6 +17,7 @@ export type BaseActivatable = {
17
17
  penalty?: Penalty;
18
18
  combat_techniques?: ApplicableCombatTechniques;
19
19
  volume?: Volume;
20
+ brew?: Brew_ID;
20
21
  cost?: EnchantmentCost | DaggerRitualCost | MagicalSignCost | number;
21
22
  property?: PropertyDeclaration;
22
23
  aspect?: Aspect_ID;
@@ -47,7 +48,7 @@ export type BaseActivatableTranslation = {
47
48
  * Get a JSON representation of the rules text for a special ability.
48
49
  */
49
50
  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", {
50
- getInstanceById: GetInstanceById<"Publication" | "Subject" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "TradeSecret" | "Aspect" | "Property" | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Weapon" | "Patron" | "PersonalityTrait" | "Blessing">;
51
+ getInstanceById: GetInstanceById<"Publication" | "Subject" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "TradeSecret" | "Aspect" | "Property" | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Weapon" | "Patron" | "PersonalityTrait" | "Blessing" | "Brew">;
51
52
  getAllInstances: GetAllInstances<"Script">;
52
53
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
53
54
  getAllResolvedSelectOptions: GetAllResolvedSelectOptions;
@@ -21,7 +21,7 @@ import { printAdvantageDisadvantagePrerequisites, printGeneralPrerequisites, } f
21
21
  import { printActivatableName, } from "./partial/prerequisites/single/activatable.js";
22
22
  import { renderStandaloneCostMap } from "./partial/rated/activatable/cost.js";
23
23
  import { parensIf } from "./partial/rated/activatable/parensIf.js";
24
- import { attributedNameR, localeSortR, translateR, } from "./partial/reader.js";
24
+ import { attributedNameR, localeSortR, nameR, translateR, } from "./partial/reader.js";
25
25
  import { getResponsiveText, getResponsiveTextOptional, ResponsiveTextSize, } from "./partial/responsiveText.js";
26
26
  import { formatTimeSpan } from "./partial/units/timeSpan.js";
27
27
  import { MISSING_VALUE } from "./partial/unknown.js";
@@ -305,6 +305,7 @@ const renderVolumeValue = (translate, translateMap, getAllResolvedSelectOptions,
305
305
  return assertExhaustive(volume);
306
306
  }
307
307
  };
308
+ const renderBrew = (brew) => nameR("Brew", brew).map(name => name ?? MISSING_VALUE);
308
309
  const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, responsiveTextSize, levels, cost) => {
309
310
  switch (cost.kind) {
310
311
  case "Fixed": {
@@ -318,7 +319,7 @@ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, res
318
319
  ? str => str
319
320
  : str => translate("{$cost} per {$interval}", {
320
321
  cost: str,
321
- interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, interval.unit, interval.value),
322
+ interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, interval.unit, interval.value, true),
322
323
  });
323
324
  const wrapInPerLevel = (() => {
324
325
  if (cost.Fixed.per_level === undefined) {
@@ -360,7 +361,7 @@ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, res
360
361
  case "Interval":
361
362
  return translate("{$cost} per {$interval}", {
362
363
  cost: translate("{$value} AE", { value: cost.Interval.value }),
363
- interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.Interval.interval.unit, cost.Interval.interval.value),
364
+ interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.Interval.interval.unit, cost.Interval.interval.value, true),
364
365
  });
365
366
  case "ActivationAndHalfInterval":
366
367
  return additionFormatter(translate("{$value} AE", {
@@ -369,7 +370,7 @@ const renderArcaneEnergyCost = (translate, translateMap, format, localeJoin, res
369
370
  cost: translate("{$value} AE", {
370
371
  value: Math.round(cost.ActivationAndHalfInterval.value / 2),
371
372
  }),
372
- interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.ActivationAndHalfInterval.interval.unit, cost.ActivationAndHalfInterval.interval.value),
373
+ interval: formatTimeSpan(translate, translateMap, format, responsiveTextSize, cost.ActivationAndHalfInterval.interval.unit, cost.ActivationAndHalfInterval.interval.value, true),
373
374
  }));
374
375
  case "Indefinite":
375
376
  return ((mapNullable(translateMap(cost.Indefinite.translations)?.description, description => getResponsiveText(description, responsiveTextSize)) ?? MISSING_VALUE) +
@@ -846,6 +847,10 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator(({
846
847
  label: translate("Volume"),
847
848
  value: renderVolumeValue(translate, translateMap, () => getAllResolvedSelectOptions(wrappedId), responsiveTextSize, volume),
848
849
  })),
850
+ mapNullable(baseEntry.brew, brew => ({
851
+ label: translate("Brew"),
852
+ value: renderBrew(brew).run(env),
853
+ })),
849
854
  mapNullable(baseEntry.cost, cost => renderCost(translate, translateMap, format, locale.join, locale.compare, () => getAllResolvedSelectOptions(wrappedId), responsiveTextSize, baseEntry.levels, cost)),
850
855
  mapNullable(baseEntry.property, property => ({
851
856
  label: translate("Property"),
@@ -161,6 +161,8 @@ const getAtomicEquipmentCost = (entry) => {
161
161
  return "Various";
162
162
  case "DependingOnPurchaseOrSale":
163
163
  return entry.content.cost.DependingOnPurchaseOrSale.purchase;
164
+ case "Range":
165
+ return [entry.content.cost.Range.from, entry.content.cost.Range.to];
164
166
  default:
165
167
  return assertExhaustive(entry.content.cost);
166
168
  }
@@ -101,6 +101,10 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
101
101
  }
102
102
  }
103
103
  case "DerivedFromSelection": {
104
+ const derivedTranslation = translateMap(value.DerivedFromSelection.translations);
105
+ if (derivedTranslation?.replacement !== undefined) {
106
+ return derivedTranslation.replacement;
107
+ }
104
108
  const derivedSelectOptions = entry.select_options?.derived;
105
109
  if (derivedSelectOptions === undefined) {
106
110
  return renderFixedSelectOptionsAdventurePointsValue(locale, getAllSelectOptions, getNameForSelectOptionId, item => {
@@ -144,7 +148,7 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
144
148
  return translate("{$values} adventure points for a {$sized} animal shape", {
145
149
  values: sizes.map(size => applyNegative(size.content.ap_value)).join("/"),
146
150
  sized: sizes
147
- .map(size => translateMap(size.content.translations)?.name ?? MISSING_VALUE)
151
+ .map(size => translateMap(size.content.translations)?.nameInAPValue ?? MISSING_VALUE)
148
152
  .join("/"),
149
153
  });
150
154
  }
@@ -69,7 +69,7 @@ const renderAddiction = (translate, translateMap, format, getInstanceById, addic
69
69
  default:
70
70
  return assertExhaustive(withdrawalPrevention.interval);
71
71
  }
72
- })()),
72
+ })(), true),
73
73
  })),
74
74
  ]
75
75
  .filter(isNotNullish)
@@ -222,6 +222,11 @@ const renderCost = (translate, translateMap, cost) => {
222
222
  })} (${translate("sale")})`;
223
223
  case "Indefinite":
224
224
  return translateMap(cost.Indefinite.translations)?.description ?? MISSING_VALUE;
225
+ case "Range":
226
+ return translate(".input {$from :number} .input {$to :number} {{{$from}–{$to} silverthalers}}", {
227
+ from: cost.Range.from,
228
+ to: cost.Range.to,
229
+ });
225
230
  default:
226
231
  return assertExhaustive(cost);
227
232
  }