@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
@@ -1,6 +1,7 @@
1
1
  import { Reader } from "@elyukai/utils/reader";
2
2
  import type { ChildEntityMap, EntityMap, FastSkillModificationLevelConfig, ResponsiveText, ResponsiveTextOptional, SkillModificationLevel, SlowSkillModificationLevelConfig } from "@optolith/database-schema/gen";
3
3
  import type { IdArgsVariant } from "tsondb/schema/gen";
4
+ import type { StdReader } from "../../env.js";
4
5
  import type { GetAllChildInstancesForParent, GetAllInstances, GetInstanceById } from "../../helpers/getTypes.js";
5
6
  import type { LocaleCompare, LocaleJoin, LocaleJoinType } from "../../helpers/locale.js";
6
7
  import type { Format, LocaleMap, Translate, TranslateMap, TranslationKeyMatchingParamsOfKey, TranslationParamsInArray, Translations } from "../../helpers/translate.js";
@@ -99,7 +100,7 @@ export declare const attributedNameFromInstanceR: (instance: {
99
100
  translations: LocaleMap<{
100
101
  name: string;
101
102
  }>;
102
- } | undefined, context: string, ...args: IdArgsVariant) => Reader<{
103
+ } | undefined, context: string, ...args: IdArgsVariant<EntityMap, keyof EntityMap>) => Reader<{
103
104
  translateMap: TranslateMap;
104
105
  }, string | undefined>;
105
106
  /**
@@ -204,6 +205,13 @@ export declare const responsiveTranslateR: <K extends keyof Translations, K2 ext
204
205
  translate: Translate;
205
206
  responsiveTextSize: ResponsiveTextSize;
206
207
  }, string>;
208
+ /**
209
+ * Creates a responsive value from a translation key that has a full and a compressed version.
210
+ */
211
+ export declare const responsiveTranslateSplitR: <K extends keyof Translations, K2 extends TranslationKeyMatchingParamsOfKey<K>>(fullKey: K, compressedKey: K2, ...rest: [...TranslationParamsInArray<K>, ...TranslationParamsInArray<K2>]) => Reader<{
212
+ translate: Translate;
213
+ responsiveTextSize: ResponsiveTextSize;
214
+ }, string>;
207
215
  /**
208
216
  * Creates a responsive value from a responsive text.
209
217
  */
@@ -266,4 +274,20 @@ export declare const mapNullableR: <T, U, R>(defaultValue: U, fn: (value: NonNul
266
274
  * Use `Reader` instances directly in a template string.
267
275
  */
268
276
  export declare const sequence: <E>(string: TemplateStringsArray, ...values: (Reader<E, string> | string)[]) => Reader<E, string>;
277
+ /**
278
+ * Formats a number according to the locale’s rules, returns a string as-is.
279
+ */
280
+ export declare const formatNumber: (value: number) => StdReader<string, "fn">;
281
+ /**
282
+ * Formats an AP value in an appreviated form.
283
+ */
284
+ export declare const formatAPValue: (value: number) => StdReader<string, "t" | "fn">;
285
+ /**
286
+ * Formats a number according to the locale’s rules, returns a string as-is.
287
+ */
288
+ export declare const formatNumberOrString: (value: number | string) => StdReader<string, "fn">;
289
+ /**
290
+ * Formats a number in fixed-point notation, returns a string as-is.
291
+ */
292
+ export declare const fixedNumberOrString: (value: number | string, fractionDigits?: number) => string;
269
293
  export {};
@@ -135,6 +135,10 @@ export const responsiveThenR = (full, compressed) => Reader.asks(env => {
135
135
  * Creates a responsive value from a translation key that has a full and a compressed version.
136
136
  */
137
137
  export const responsiveTranslateR = (fullKey, compressedKey, ...rest) => Reader.asks(({ translate, responsiveTextSize }) => responsive(responsiveTextSize, () => translate(fullKey, ...rest), () => translate(compressedKey, ...rest)));
138
+ /**
139
+ * Creates a responsive value from a translation key that has a full and a compressed version.
140
+ */
141
+ export const responsiveTranslateSplitR = (fullKey, compressedKey, ...rest) => Reader.asks(({ translate, responsiveTextSize }) => responsive(responsiveTextSize, () => translate(fullKey, ...rest.slice(0, 1)), () => translate(compressedKey, ...rest.slice(1))));
138
142
  /**
139
143
  * Creates a responsive value from a responsive text.
140
144
  */
@@ -198,3 +202,19 @@ export const mapNullableR = (defaultValue, fn) => (value) => isNotNullish(value)
198
202
  * Use `Reader` instances directly in a template string.
199
203
  */
200
204
  export const sequence = (string, ...values) => Reader.traverse(values, value => (value instanceof Reader ? value : Reader.of(value))).map(resolvedValues => string.reduce((acc, str, i) => acc + str + (resolvedValues[i] ?? ""), ""));
205
+ /**
206
+ * Formats a number according to the locale’s rules, returns a string as-is.
207
+ */
208
+ export const formatNumber = (value) => Reader.asks(env => env.formatNumber(value));
209
+ /**
210
+ * Formats an AP value in an appreviated form.
211
+ */
212
+ export const formatAPValue = (value) => formatNumber(value).thenW(formattedValue => translateR("{$value} AP", { value: formattedValue }));
213
+ /**
214
+ * Formats a number according to the locale’s rules, returns a string as-is.
215
+ */
216
+ export const formatNumberOrString = (value) => Reader.asks(env => (typeof value === "number" ? env.formatNumber(value) : value));
217
+ /**
218
+ * Formats a number in fixed-point notation, returns a string as-is.
219
+ */
220
+ export const fixedNumberOrString = (value, fractionDigits) => typeof value === "number" ? value.toFixed(fractionDigits) : value;
@@ -1,3 +1,4 @@
1
+ import { fixedNumberOrString } from "../reader.js";
1
2
  const lengthUnitTranslationKeys = {
2
3
  LifePoints: "{$value} LP",
3
4
  ArcaneEnergy: "{$value} AE",
@@ -8,5 +9,5 @@ const lengthUnitTranslationKeys = {
8
9
  */
9
10
  export const formatEnergy = (translate, unit, value) => {
10
11
  const key = lengthUnitTranslationKeys[unit];
11
- return translate(key, { value });
12
+ return translate(key, { value: fixedNumberOrString(value) });
12
13
  };
@@ -1,4 +1,5 @@
1
1
  import { Reader } from "@elyukai/utils/reader";
2
+ import { fixedNumberOrString } from "../reader.js";
2
3
  import { responsive } from "../responsiveText.js";
3
4
  const lengthUnitTranslationKeys = {
4
5
  Steps: [".input {$value :number} {{{$value} yards}}", "{$value} yards", "{$value} yd"],
@@ -9,9 +10,11 @@ const lengthUnitTranslationKeys = {
9
10
  */
10
11
  export const formatLength = (translate, responsiveTextSize, unit, value) => {
11
12
  const [fullNumberKey, fullKey, compressedKey] = lengthUnitTranslationKeys[typeof unit === "string" ? unit : unit.kind];
12
- return responsive(responsiveTextSize, () => translate(typeof value === "number" ? fullNumberKey : fullKey, {
13
- value,
14
- }), () => translate(compressedKey, { value }));
13
+ return responsive(responsiveTextSize, () => translate(typeof value === "number" ? fullNumberKey : fullKey, typeof value === "number"
14
+ ? {
15
+ value,
16
+ }
17
+ : { value }), () => translate(compressedKey, { value: fixedNumberOrString(value) }));
15
18
  };
16
19
  /**
17
20
  * Returns the text for a length unit.
@@ -0,0 +1,55 @@
1
+ import { Reader } from "@elyukai/utils/reader";
2
+ import type { StdEnv } from "../../../env.js";
3
+ /**
4
+ * Returns the text for numeric silverthaler cost.
5
+ */
6
+ export declare const formatSilverthalers: (value: number) => Reader<StdEnv<"fn", never, never, never> & import("@elyukai/utils/nullable").AnyNonNullish & {
7
+ translate: import("../../../helpers/translate.js").Translate;
8
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
9
+ }, string>;
10
+ /**
11
+ * Returns the text for silverthaler cost.
12
+ */
13
+ export declare const formatArbitrarySilverthalers: (value: string | number) => Reader<StdEnv<"fn", never, never, never> & import("@elyukai/utils/nullable").AnyNonNullish & {
14
+ translate: import("../../../helpers/translate.js").Translate;
15
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
16
+ }, string>;
17
+ /**
18
+ * Returns the text for weight in the weight unit of the locale.
19
+ */
20
+ export declare const formatWeight: (value: number) => Reader<{
21
+ translate: import("../../../helpers/translate.js").Translate;
22
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
23
+ }, string>;
24
+ /**
25
+ * Adjusts a weight value according to the locale's measurement adjustments.
26
+ */
27
+ export declare const adjustWeight: (value: number) => Reader<StdEnv<"ma">, number>;
28
+ /**
29
+ * Returns the text for weight in the weight unit of the locale.
30
+ */
31
+ export declare const formatAdjustedWeight: (value: number) => Reader<StdEnv<"ma"> & {
32
+ translate: import("../../../helpers/translate.js").Translate;
33
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
34
+ }, string>;
35
+ /**
36
+ * Returns the text for arbitrary weight in the weight unit of the locale.
37
+ */
38
+ export declare const formatArbitraryWeight: (value: string | number) => Reader<{
39
+ translate: import("../../../helpers/translate.js").Translate;
40
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
41
+ }, string>;
42
+ /**
43
+ * Returns the text for adventure points.
44
+ */
45
+ export declare const formatAdventurePoints: (value: number) => Reader<StdEnv<"fn", never, never, never> & import("@elyukai/utils/nullable").AnyNonNullish & {
46
+ translate: import("../../../helpers/translate.js").Translate;
47
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
48
+ }, string>;
49
+ /**
50
+ * Returns the text for arbitrary adventure points.
51
+ */
52
+ export declare const formatArbitraryAdventurePoints: (value: string | number) => Reader<StdEnv<"fn", never, never, never> & import("@elyukai/utils/nullable").AnyNonNullish & {
53
+ translate: import("../../../helpers/translate.js").Translate;
54
+ responsiveTextSize: import("../responsiveText.js").ResponsiveTextSize;
55
+ }, string>;
@@ -0,0 +1,56 @@
1
+ import { Reader } from "@elyukai/utils/reader";
2
+ import { formatNumber, responsiveTranslateR, responsiveTranslateSplitR } from "../reader.js";
3
+ /**
4
+ * Returns the text for numeric silverthaler cost.
5
+ */
6
+ export const formatSilverthalers = (value) => formatNumber(value).thenW(valueStr => responsiveTranslateSplitR(".input {$value :number} {{{$value} silverthalers}}", "{$value} S", {
7
+ value,
8
+ }, {
9
+ value: valueStr,
10
+ }));
11
+ /**
12
+ * Returns the text for silverthaler cost.
13
+ */
14
+ export const formatArbitrarySilverthalers = (value) => typeof value === "number"
15
+ ? formatSilverthalers(value)
16
+ : responsiveTranslateR("{$value} silverthalers", "{$value} S", {
17
+ value,
18
+ });
19
+ /**
20
+ * Returns the text for weight in the weight unit of the locale.
21
+ */
22
+ export const formatWeight = (value) => responsiveTranslateR(".input {$value :number} {{{$value} pounds}}", ".input {$value :number} {{{$value} lbs}}", {
23
+ value,
24
+ });
25
+ /**
26
+ * Adjusts a weight value according to the locale's measurement adjustments.
27
+ */
28
+ export const adjustWeight = (value) => Reader.asks((env) => env.measurementAdjustments.stonesMultiplier * value);
29
+ /**
30
+ * Returns the text for weight in the weight unit of the locale.
31
+ */
32
+ export const formatAdjustedWeight = (value) => adjustWeight(value).thenW(formatWeight);
33
+ /**
34
+ * Returns the text for arbitrary weight in the weight unit of the locale.
35
+ */
36
+ export const formatArbitraryWeight = (value) => typeof value === "number"
37
+ ? formatWeight(value)
38
+ : responsiveTranslateR("{$value} pounds", "{$value} lbs", {
39
+ value,
40
+ });
41
+ /**
42
+ * Returns the text for adventure points.
43
+ */
44
+ export const formatAdventurePoints = (value) => formatNumber(value).thenW(valueStr => responsiveTranslateSplitR(".input {$value :number} {{{$value} Adventure Points}}", "{$value} AP", {
45
+ value,
46
+ }, {
47
+ value: valueStr,
48
+ }));
49
+ /**
50
+ * Returns the text for arbitrary adventure points.
51
+ */
52
+ export const formatArbitraryAdventurePoints = (value) => typeof value === "number"
53
+ ? formatAdventurePoints(value)
54
+ : responsiveTranslateR("{$value} Adventure Points", "{$value} AP", {
55
+ value,
56
+ });
@@ -35,13 +35,17 @@ export const formatTimeSpan = (translate, translateMap, format, responsiveTextSi
35
35
  }));
36
36
  }
37
37
  const [fullNumberKey, fullKey, compressedNumberKey, compressedKey] = timeSpanUnitTranslationKeys[typeof unit === "string" ? unit : unit.kind];
38
- return responsive(responsiveTextSize, () => translate(typeof value === "number" ? fullNumberKey : fullKey, {
39
- value,
40
- style: interval ? "interval" : "default",
41
- }), () => translate(typeof value === "number" ? compressedNumberKey : compressedKey, {
42
- value,
43
- style: interval ? "interval" : "default",
44
- }));
38
+ return responsive(responsiveTextSize, () => translate(typeof value === "number" ? fullNumberKey : fullKey, typeof value === "number"
39
+ ? {
40
+ value,
41
+ style: interval ? "interval" : "default",
42
+ }
43
+ : { value }), () => translate(typeof value === "number" ? compressedNumberKey : compressedKey, typeof value === "number"
44
+ ? {
45
+ value,
46
+ style: interval ? "interval" : "default",
47
+ }
48
+ : { value }));
45
49
  };
46
50
  /**
47
51
  * Returns the text for a time span unit.
@@ -6,3 +6,7 @@ export declare const MISSING_VALUE = "###MISSING###";
6
6
  * String to display when a translation that should be present produces an error.
7
7
  */
8
8
  export declare const UNHANDLED_VALUE = "###UNHANDLEDERROR###";
9
+ /**
10
+ * String to display when there is a logic error in text generation.
11
+ */
12
+ export declare const LOGIC_ERROR = "###LOGIC_ERROR###";
@@ -6,3 +6,7 @@ export const MISSING_VALUE = "###MISSING###";
6
6
  * String to display when a translation that should be present produces an error.
7
7
  */
8
8
  export const UNHANDLED_VALUE = "###UNHANDLEDERROR###";
9
+ /**
10
+ * String to display when there is a logic error in text generation.
11
+ */
12
+ export const LOGIC_ERROR = "###LOGIC_ERROR###";
@@ -4,4 +4,4 @@ import type { GetInstanceById } from "../helpers/getTypes.js";
4
4
  */
5
5
  export declare const getPersonalityTraitEntityDescription: import("../creator.js").EntityDescriptionCreator<"PersonalityTrait", {
6
6
  getInstanceById: GetInstanceById<"Publication" | "Race" | "Culture" | "PersonalityTrait">;
7
- }, import("../index.js").EntityDescription>;
7
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -9,4 +9,4 @@ export declare const getPoisonEntityDescription: import("../creator.js").EntityD
9
9
  getInstanceById: GetInstanceById<"Publication" | "DerivedCharacteristic" | "Disease" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Race" | "Culture" | "State" | "Enhancement" | "PactCategory" | "PactDomain" | "SocialStatus" | "Aspect" | "Property" | "PersonalityTrait" | "Blessing" | "Cantrip">;
10
10
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
11
11
  idMap: IdMap;
12
- }, import("../index.js").EntityDescription>;
12
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -379,7 +379,7 @@ export const getPoisonEntityDescription = createEntityDescriptionCreator(({ getI
379
379
  ? undefined
380
380
  : {
381
381
  label: `${translate("AP Value")} (${translate("Trade Secret")})`,
382
- value: translate("{$value} AP", { value: tradeSecret.apValue }) +
382
+ value: translate("{$value} AP", { value: tradeSecret.apValue.toFixed() }) +
383
383
  parensIf(mapNullable(tradeSecret.prerequisites, prerequisites => `${translate("Prerequisites")}: ${printPlainGeneralPrerequisites(prerequisites).run(env)}`)),
384
384
  },
385
385
  entry.source_type.kind === "AlchemicalPoison" ||
@@ -1,4 +1,5 @@
1
1
  import type { MagicalActionIdentifier, ProfessionSpecialAbilityIdentifier, RatedIdentifier } from "@optolith/database-schema/gen";
2
+ import type { StdEnv } from "../env.js";
2
3
  import type { GetAllChildInstancesForParent, GetAllInstances, GetInstanceById } from "../helpers/getTypes.js";
3
4
  import { type GetResolvedSelectOptionById } from "./partial/prerequisites/single/activatable.js";
4
5
  /**
@@ -9,4 +10,4 @@ export declare const getProfessionVersionEntityDescription: import("../creator.j
9
10
  getAllInstances: GetAllInstances<"SkillGroup">;
10
11
  getChildInstancesForInstanceId: GetAllChildInstancesForParent<"ProfessionPackage" | "ProfessionVariant">;
11
12
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
12
- }, import("../index.js").EntityDescription>;
13
+ } & StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -2,7 +2,7 @@ import { allSame } from "@elyukai/utils/array/filters";
2
2
  import { ensureNonEmpty, isEmpty, isNotEmpty, } from "@elyukai/utils/array/nonEmpty";
3
3
  import { deepEqual, equal } from "@elyukai/utils/equality";
4
4
  import { on } from "@elyukai/utils/function";
5
- import { isNotNullish, isNullish, mapNullable, nullableToArray, } from "@elyukai/utils/nullable";
5
+ import { isNotNullish, isNullish, mapNullable } from "@elyukai/utils/nullable";
6
6
  import { omitKeys } from "@elyukai/utils/object";
7
7
  import { compareNumber } from "@elyukai/utils/ordering";
8
8
  import { Reader } from "@elyukai/utils/reader";
@@ -13,16 +13,13 @@ import { combineNameComponents, getNameComponents, renderActivatableNameComponen
13
13
  import { renderCommonnessRatedAdvantagesOrDisadvantages, renderValueWithPossibleTranslation, } from "./partial/commonnessRatedAdvantagesAndDisadvantages.js";
14
14
  import { attributedCustomName, attributedInstance, attributedName, attributedNameFromInstance, } from "./partial/markdown.js";
15
15
  import { printProfessionPrerequisites, printProfessionVariantPrerequisites, } from "./partial/prerequisites/index.js";
16
+ import { renderBaseCombatTechniquesForAdventurePointsOption, renderVariantCombatTechniquesForAdventurePointsOption, } from "./partial/profession/options/combatTechniquesForAdventurePoints.js";
17
+ import { renderBaseNumberOfSpellworksTotalingAdventurePoints, renderVariantNumberOfSpellworksTotalingAdventurePoints, } from "./partial/profession/options/numberOfSpellworksTotalingAdventurePoints.js";
18
+ import { prepareProfessionPackages, } from "./partial/profession/packages.js";
19
+ import { insteadOfR, renderVariantOption, renderVariantOptionPaths, } from "./partial/profession/variants.js";
16
20
  import { parensIf } from "./partial/rated/activatable/parensIf.js";
17
- import { attributedCustomNameR, attributedNameR, customNameR, formatR, getChildInstancesForInstanceIdFnR, getChildInstancesForInstanceIdR, getInstanceByIdR, localeCompareR, localeJoinR, localeSortR, strictNameR, translateMapFnR, translateR, translationR, } from "./partial/reader.js";
21
+ import { attributedCustomNameR, attributedNameR, customNameR, fixedNumberOrString, formatAPValue, formatR, getChildInstancesForInstanceIdFnR, getChildInstancesForInstanceIdR, getInstanceByIdR, localeCompareR, localeJoinR, localeSortR, strictNameR, translateMapFnR, translateR, translationR, } from "./partial/reader.js";
18
22
  import { MISSING_VALUE, UNHANDLED_VALUE } from "./partial/unknown.js";
19
- const prepareProfessionPackages = (professionId) => Reader.asks(({ getInstanceById, getChildInstancesForInstanceId }) => getChildInstancesForInstanceId("ProfessionPackage", professionId)
20
- .map((professionPackage) => ({
21
- ...professionPackage,
22
- experienceLevel: getInstanceById("ExperienceLevel", professionPackage.content.experience_level),
23
- }))
24
- .filter((professionPackage) => professionPackage.experienceLevel !== undefined)
25
- .toSorted(on(item => item.experienceLevel.adventure_points, compareNumber)));
26
23
  const renderNumericListAcrossPackages = (packages, selector, equalityFn, renderText, defaultValue) => packages
27
24
  .reduce((accR, pkg, pkgIndex) => accR.map2(selector(pkg), (acc, selected) => selected.reduce((filledAcc, [values, number]) => {
28
25
  const existing = filledAcc.findIndex(([value]) => equalityFn(value, values));
@@ -48,7 +45,6 @@ const renderNumericListAcrossPackages = (packages, selector, equalityFn, renderT
48
45
  .thenW(list => localeSortR(list))
49
46
  .map(list => ensureNonEmpty(list)?.join(", "));
50
47
  const renderSinglePackageValue = (professionPackages, selector, equalityFn, renderText) => Reader.traverse(professionPackages, selector).thenW(selected => (allSame(selected, equalityFn) ? renderText([selected[0]]) : renderText(selected)));
51
- const insteadOfR = (replacement, base) => translateR("{$replacement} instead of {$base}", { replacement, base });
52
48
  /**
53
49
  * If the selected values of the base and the variant are equal, only renders the value once. Otherwise, renders both values as the variant value replacing the base value.
54
50
  */
@@ -59,23 +55,14 @@ const plainOrInsteadOfR = (base, variant, selector, equality, render) => {
59
55
  ? render(baseValue)
60
56
  : render(variantValue).thenW(replacement => render(baseValue).thenW(baseRendered => insteadOfR(replacement, baseRendered)));
61
57
  };
62
- const renderVariantOptionPaths = (base, variant, paths) => {
63
- switch (variant.kind) {
64
- case "Remove":
65
- return base === undefined ? Reader.of(MISSING_VALUE) : paths.remove(base);
66
- case "Override":
67
- return base === undefined ? paths.add(variant.Override) : paths.update(base, variant.Override);
68
- default:
69
- return assertExhaustive(variant);
70
- }
71
- };
72
58
  const renderVariantLanguagesScriptsOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
73
- remove: base => translateR("no Languages and Literacy totaling {$apValue} AP", { apValue: base.ap_value }),
74
- add: override => translateR("Languages and Literacy totaling {$apValue} AP", { apValue: override.ap_value }),
75
- update: (base, override) => translateR("{$replacement} instead of {$base}", {
76
- replacement: override.ap_value,
77
- base: base.ap_value,
78
- }).then(apValue => translateR("Languages and Literacy totaling {$apValue} AP", {
59
+ remove: base => translateR("no Languages and Literacy totaling {$apValue} AP", {
60
+ apValue: base.ap_value.toFixed(),
61
+ }),
62
+ add: override => translateR("Languages and Literacy totaling {$apValue} AP", {
63
+ apValue: override.ap_value.toFixed(),
64
+ }),
65
+ update: (base, override) => insteadOfR(override.ap_value, base.ap_value).then(apValue => translateR("Languages and Literacy totaling {$apValue} AP", {
79
66
  apValue,
80
67
  })),
81
68
  });
@@ -101,17 +88,17 @@ const renderSkillSpecializationOption = (option) => Reader.asks(({ format, trans
101
88
  const renderCombatTechniquesOption = (option) => isNotEmpty(option.fixed)
102
89
  ? (() => {
103
90
  const [first, ...others] = option.fixed;
104
- const firstTextR = translateR(".input {$count :number} {{{$count} of the following combat techniques {$rating}}}", { count: first.number, rating: first.rating_modifier + 6 });
91
+ const firstTextR = translateR(".input {$count :number} {{{$count} of the following combat techniques {$rating}}}", { count: first.number, rating: (first.rating_modifier + 6).toFixed() });
105
92
  const fixedTextR = others.reduce((accTextR, other) => accTextR.then(accText => translateR(".input {$count :number} {{{$previous}, {$count} others {$rating}}}", {
106
93
  count: other.number,
107
94
  previous: accText,
108
- rating: other.rating_modifier + 6,
95
+ rating: (other.rating_modifier + 6).toFixed(),
109
96
  })), firstTextR);
110
97
  const completeTextR = fixedTextR.then(fixedText => option.rest_rating_modifier === undefined
111
98
  ? Reader.of(fixedText)
112
99
  : translateR("{$previous}, the others {$rating}", {
113
100
  previous: fixedText,
114
- rating: option.rest_rating_modifier + 6,
101
+ rating: (option.rest_rating_modifier + 6).toFixed(),
115
102
  }));
116
103
  const listR = Reader.traverse(option.options, id => attributedNameR("profession", id).map(name => name ?? MISSING_VALUE))
117
104
  .thenW(list => localeSortR(list))
@@ -134,13 +121,13 @@ const renderSkillsOption = (professionPackages, skillGroup) => allSame(professio
134
121
  ? pkg.content.options.skills
135
122
  : undefined), apValue => professionPackages[0].content.options?.skills?.specific === undefined
136
123
  ? translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
137
- apValue,
124
+ apValue: fixedNumberOrString(apValue),
138
125
  skillsOfGroup: skillGroup.nameOfSkillsOfGroup,
139
126
  })
140
127
  : Reader.traverse(professionPackages[0].content.options.skills.specific, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE))
141
128
  .thenW(localeSortR)
142
- .thenW(list => translateR("maximum SR per skill: {$maxSR}", { maxSR: 8 }).then(maxSRText => translateR("{$apValue} AP to distribute among the following skills: {$list}", {
143
- apValue,
129
+ .thenW(list => translateR("maximum SR per skill: {$maxSR}", { maxSR: "8" }).then(maxSRText => translateR("{$apValue} AP to distribute among the following skills: {$list}", {
130
+ apValue: fixedNumberOrString(apValue),
144
131
  list: list.join(", ") + parensIf(maxSRText),
145
132
  })))) ?? Reader.of(undefined))
146
133
  : Reader.traverse(professionPackages, pkg => {
@@ -149,13 +136,13 @@ const renderSkillsOption = (professionPackages, skillGroup) => allSame(professio
149
136
  ? Reader.of("—")
150
137
  : skillsOption.specific === undefined
151
138
  ? translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
152
- apValue: skillsOption.ap_value,
139
+ apValue: skillsOption.ap_value.toFixed(),
153
140
  skillsOfGroup: skillGroup.nameOfSkillsOfGroup,
154
141
  })
155
142
  : Reader.traverse(skillsOption.specific, id => attributedNameR("profession", "Skill", id).map(name => name ?? MISSING_VALUE))
156
143
  .thenW(localeSortR)
157
- .thenW(list => translateR("maximum SR per skill: {$maxSR}", { maxSR: 8 }).then(maxSRText => translateR("{$apValue} AP to distribute among the following skills: {$list}", {
158
- apValue: skillsOption.ap_value,
144
+ .thenW(list => translateR("maximum SR per skill: {$maxSR}", { maxSR: "8" }).then(maxSRText => translateR("{$apValue} AP to distribute among the following skills: {$list}", {
145
+ apValue: skillsOption.ap_value.toFixed(),
159
146
  list: list.join(", ") + parensIf(maxSRText),
160
147
  })));
161
148
  }).map(list => list.join(" / "));
@@ -175,10 +162,7 @@ const renderVariantCantripsOption = (baseOptions, variantOptions) => renderVaria
175
162
  }).then(baseCount => translateR(".input {$count :number} {{{$count} cantrips}}", {
176
163
  count: override.number,
177
164
  })
178
- .then(replacementCount => translateR("{$replacement} instead of {$base}", {
179
- replacement: replacementCount,
180
- base: baseCount,
181
- }).then(count => translateR("{$count} from the following list", { count })))
165
+ .then(replacementCount => insteadOfR(replacementCount, baseCount).then(count => translateR("{$count} from the following list", { count })))
182
166
  .map(text => `${text}: ${list}`)))
183
167
  : renderCantripList(override.options)
184
168
  .thenW(list => translateR(".input {$count :number} {{{$count} cantrips}}", {
@@ -192,10 +176,7 @@ const renderVariantCantripsOption = (baseOptions, variantOptions) => renderVaria
192
176
  })
193
177
  .then(count => translateR("{$count} from the following list", { count }))
194
178
  .map(text => `${text}: ${list}`))
195
- .then(baseText => translateR("{$replacement} instead of {$base}", {
196
- replacement: replacementText,
197
- base: baseText,
198
- }))),
179
+ .then(baseText => insteadOfR(replacementText, baseText))),
199
180
  });
200
181
  const renderSingleCantripsOption = (option) => renderVariantCantripsOption(undefined, { kind: "Override", Override: option });
201
182
  const renderCantripsOption = (professionPackages) => {
@@ -212,7 +193,7 @@ const renderCantripsOption = (professionPackages) => {
212
193
  }
213
194
  };
214
195
  const renderLiturgiesOption = (professionPackages) => mapNullable(getTotalingAPValues(professionPackages, pkg => pkg.content.options?.liturgies), apValue => translateR("Liturgies totaling {$apValue} AP", {
215
- apValue,
196
+ apValue: fixedNumberOrString(apValue),
216
197
  })) ?? Reader.of(undefined);
217
198
  const renderVariantSkillSpecializationOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
218
199
  remove: (base) => {
@@ -260,8 +241,8 @@ const renderVariantCombatTechniquesOption = (baseOptions, variantOptions) => ren
260
241
  update: (base, override) => renderCombatTechniquesOption(override).then(overrideText => renderCombatTechniquesOption(base).then(baseText => insteadOfR(overrideText, baseText))),
261
242
  });
262
243
  const renderVariantCursesOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
263
- remove: base => translateR("no Curses totaling {$apValue} AP", { apValue: base.ap_value }),
264
- add: override => translateR("Curses totaling {$apValue} AP", { apValue: override.ap_value }),
244
+ remove: base => translateR("no Curses totaling {$apValue} AP", { apValue: base.ap_value.toFixed() }),
245
+ add: override => translateR("Curses totaling {$apValue} AP", { apValue: override.ap_value.toFixed() }),
265
246
  update: (base, override) => insteadOfR(override.ap_value, base.ap_value).then(apValue => translateR("Curses totaling {$apValue} AP", {
266
247
  apValue,
267
248
  })),
@@ -273,17 +254,17 @@ const renderVariantSkillsOption = (baseOptions, variantOptions) => renderVariant
273
254
  skillsOfGroup: nameOfSkillsOfGroup,
274
255
  })),
275
256
  add: override => nameOfSkillsOfGroupR(override.group).thenW(nameOfSkillsOfGroup => translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
276
- apValue: override.ap_value,
257
+ apValue: override.ap_value.toFixed(),
277
258
  skillsOfGroup: nameOfSkillsOfGroup,
278
259
  })),
279
260
  update: (base, override) => plainOrInsteadOfR(base, override, b => b.ap_value, equal, b => Reader.of(b)).thenW(apValue => plainOrInsteadOfR(base, override, b => b.group, equal, nameOfSkillsOfGroupR).thenW(nameOfSkillsOfGroup => translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
280
- apValue,
281
- skillsOfGroup: nameOfSkillsOfGroup,
261
+ apValue: fixedNumberOrString(apValue),
262
+ skillsOfGroup: fixedNumberOrString(nameOfSkillsOfGroup),
282
263
  }))),
283
264
  });
284
265
  const renderVariantLiturgiesOption = (baseOptions, variantOptions) => renderVariantOptionPaths(baseOptions, variantOptions, {
285
- remove: base => translateR("no Liturgies totaling {$apValue} AP", { apValue: base.ap_value }),
286
- add: override => translateR("Liturgies totaling {$apValue} AP", { apValue: override.ap_value }),
266
+ remove: base => translateR("no Liturgies totaling {$apValue} AP", { apValue: base.ap_value.toFixed() }),
267
+ add: override => translateR("Liturgies totaling {$apValue} AP", { apValue: override.ap_value.toFixed() }),
287
268
  update: (base, override) => insteadOfR(override.ap_value, base.ap_value).then(apValue => translateR("Liturgies totaling {$apValue} AP", {
288
269
  apValue,
289
270
  })),
@@ -416,6 +397,9 @@ const renderCombatTechniques = (professionPackages) => Reader.sequence([
416
397
  professionPackages.some(pkg => pkg.content.options?.combat_techniques !== undefined)
417
398
  ? renderSinglePackageValue(professionPackages, pkg => Reader.of(pkg.content.options?.combat_techniques), deepEqual, options => Reader.traverse(options, option => option === undefined ? Reader.of("—") : renderCombatTechniquesOption(option)).map(renderedOptions => renderedOptions.join(" / ")))
418
399
  : Reader.of(undefined),
400
+ professionPackages.some(pkg => pkg.content.options?.combatTechniquesForAdventurePoints !== undefined)
401
+ ? renderBaseCombatTechniquesForAdventurePointsOption(professionPackages)
402
+ : Reader.of(undefined),
419
403
  ]).map(list => ensureNonEmpty(list.filter(isNotNullish))?.join(", ") ?? "—");
420
404
  const renderSkills = (professionPackages) => Reader.asks(({ getAllInstances }) => getAllInstances("SkillGroup")).thenW(skillGroups => Reader.traverse(skillGroups.toSorted(on(group => group.content.position, compareNumber)), ({ id: groupId, content: group }) => translationR(group).thenW(groupTranslation => Reader.sequence([
421
405
  renderNumericListAcrossPackages(professionPackages, pkg => Reader.asks(({ getInstanceById }) => pkg.content.skills
@@ -444,6 +428,9 @@ const renderSpellworkName = (spellworkIds) => Reader.traverse(spellworkIds, spel
444
428
  const renderSpellworks = (professionPackages) => Reader.sequence([
445
429
  renderCantripsOption(professionPackages),
446
430
  renderNumericListAcrossPackages(professionPackages, (pkg) => Reader.of(pkg.content.spells?.map(ct => [ct.id, ct.rating_modifier]) ?? []), deepEqual, renderSpellworkName, undefined),
431
+ professionPackages.some(pkg => pkg.content.options?.spellworks !== undefined)
432
+ ? renderBaseNumberOfSpellworksTotalingAdventurePoints(professionPackages)
433
+ : Reader.of(undefined),
447
434
  ]).map(list => ensureNonEmpty(list.filter(isNotNullish))?.join("; "));
448
435
  const retrieveBlessedTraditionIdentifierFromPrerequisiteGroup = (prerequisite) => prerequisite.kind === "Activatable" && prerequisite.Activatable.id.kind === "BlessedTradition"
449
436
  ? [prerequisite.Activatable.id.BlessedTradition]
@@ -533,17 +520,9 @@ const renderRatedVariantChanges = (baseList, variantList, renderInstance) => Rea
533
520
  return renderInstance(id).map(name => `${name} ${rating_modifier.toFixed()}`);
534
521
  }
535
522
  const baseValue = baseList?.find(item => deepEqual(item.id, id))?.rating_modifier ?? 0;
536
- return renderInstance(id).thenW(name => translateR("{$replacement} instead of {$base}", {
537
- replacement: `${name} ${(baseValue + rating_modifier).toFixed()}`,
538
- base: baseValue,
539
- }));
523
+ return renderInstance(id).thenW(name => insteadOfR(`${name} ${(baseValue + rating_modifier).toFixed()}`, baseValue));
540
524
  }).thenW(localeSortR);
541
- const renderVariantOption = (base, variant, key, render) => (variant.options?.[key] === undefined
542
- ? Reader.of(undefined)
543
- : render(base.options?.[key], variant.options[key])).map(text => nullableToArray(text));
544
- const renderProfessionVariantLabel = (base, variant, translations) => translateR("{$value} AP", {
545
- value: base.ap_value + (variant.ap_value ?? 0),
546
- }).thenW(apValueText => localeSortR(translations.map(({ id, content }) => `^[${content.name.default}](entity: "ProfessionVariant", instance: "${id}")`))
525
+ const renderProfessionVariantLabel = (base, variant, translations) => formatAPValue(base.ap_value + (variant.ap_value ?? 0)).thenW(apValueText => localeSortR(translations.map(({ id, content }) => `^[${content.name.default}](entity: "ProfessionVariant", instance: "${id}")`))
547
526
  .thenW(list => localeJoinR(list, "conjunction"))
548
527
  .map(names => names + parensIf(apValueText)));
549
528
  const renderProfessionVariantText = (base, variant, translation) => translation.full_text !== undefined
@@ -557,9 +536,11 @@ const renderProfessionVariantText = (base, variant, translation) => translation.
557
536
  renderVariantOption(base, variant, "skill_specialization", renderVariantSkillSpecializationOption),
558
537
  renderVariantOption(base, variant, "languages_scripts", renderVariantLanguagesScriptsOption),
559
538
  renderVariantOption(base, variant, "combat_techniques", renderVariantCombatTechniquesOption),
539
+ renderVariantOption(base, variant, "combatTechniquesForAdventurePoints", renderVariantCombatTechniquesForAdventurePointsOption),
560
540
  renderVariantOption(base, variant, "cantrips", renderVariantCantripsOption),
561
541
  renderVariantOption(base, variant, "curses", renderVariantCursesOption),
562
542
  renderVariantOption(base, variant, "skills", renderVariantSkillsOption),
543
+ renderVariantOption(base, variant, "spellworks", renderVariantNumberOfSpellworksTotalingAdventurePoints),
563
544
  renderVariantOption(base, variant, "liturgies", renderVariantLiturgiesOption),
564
545
  variant.special_abilities === undefined
565
546
  ? Reader.of([])
@@ -635,10 +616,11 @@ const renderProfessionVariants = (professionPackages) => professionPackages.leng
635
616
  * Get a JSON representation of the rules text for a profession version.
636
617
  */
637
618
  export const getProfessionVersionEntityDescription = createEntityDescriptionCreator(({ getInstanceById, getAllInstances, getChildInstancesForInstanceId, getResolvedSelectOptionById, }, locale, { id, content: entry }) => {
638
- const { format, translate, translateMap, compare: localeCompare, join: localeJoin } = locale;
619
+ const { format, formatNumber, translate, translateMap, compare: localeCompare, join: localeJoin, } = locale;
639
620
  const translation = translateMap(entry.translations);
640
621
  const env = {
641
622
  format,
623
+ formatNumber,
642
624
  translate,
643
625
  translateMap,
644
626
  localeCompare,
@@ -1,5 +1,5 @@
1
1
  import type { ActivatableIdentifier, AutomaticAdvantageDisadvantage } from "@optolith/database-schema/gen";
2
- import type { StdReader } from "../env.js";
2
+ import type { StdEnv, StdReader } from "../env.js";
3
3
  import type { CountInstances, GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js";
4
4
  import { type GetResolvedSelectOptionById } from "./partial/prerequisites/single/activatable.js";
5
5
  /**
@@ -14,4 +14,4 @@ export declare const getRaceEntityDescription: import("../creator.js").EntityDes
14
14
  countInstances: CountInstances<"Attribute">;
15
15
  getChildInstancesForInstanceId: GetAllChildInstancesForParent<"RaceVariant">;
16
16
  getResolvedSelectOptionById: GetResolvedSelectOptionById;
17
- }, import("../index.js").EntityDescription>;
17
+ } & 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 a sex practice.
3
3
  */
4
- export declare const getSexPracticeEntityDescription: import("../creator.js").EntityDescriptionCreator<"SexPractice", {
5
- getInstanceById: import("../helpers/getTypes.js").GetInstanceById<"Publication">;
6
- }, import("../index.js").EntityDescription>;
4
+ export declare const getSexPracticeEntityDescription: import("../creator.js").EntityDescriptionCreator<"SexPractice", import("@elyukai/utils/nullable").AnyNonNullish & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
@@ -10,4 +10,4 @@ export declare const getSkillEntityDescription: import("../creator.js").EntityDe
10
10
  getChildInstancesForInstanceId: GetAllChildInstancesForParent<"SkillApplication">;
11
11
  getAllResolvedNewSkillApplications: GetAllResolvedNewSkillApplications;
12
12
  getAllResolvedSkillUses: GetAllResolvedSkillUses;
13
- }, import("../index.js").EntityDescription>;
13
+ } & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;