@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.
- package/CHANGELOG.md +23 -0
- package/lib/creator.d.ts +4 -11
- package/lib/creator.js +58 -2
- package/lib/entities/activatable.d.ts +5 -2
- package/lib/entities/activatable.js +29 -15
- package/lib/entities/alternativeRule.d.ts +1 -1
- package/lib/entities/attribute.d.ts +1 -3
- package/lib/entities/combatTechnique.d.ts +2 -2
- package/lib/entities/condition.d.ts +2 -6
- package/lib/entities/culture.d.ts +2 -1
- package/lib/entities/culture.js +3 -3
- package/lib/entities/curriculum.d.ts +1 -1
- package/lib/entities/curriculum.js +1 -1
- package/lib/entities/derivedCharacteristic.d.ts +1 -1
- package/lib/entities/disease.d.ts +1 -1
- package/lib/entities/elixir.d.ts +1 -1
- package/lib/entities/elixir.js +1 -1
- package/lib/entities/equipment.d.ts +4 -6
- package/lib/entities/equipment.js +517 -213
- package/lib/entities/equipmentPackage.d.ts +1 -1
- package/lib/entities/experienceLevel.d.ts +1 -3
- package/lib/entities/focusRule.d.ts +1 -1
- package/lib/entities/influence.d.ts +1 -1
- package/lib/entities/liturgicalChant.d.ts +3 -3
- package/lib/entities/optionalRule.d.ts +1 -3
- package/lib/entities/partial/adventurePointsValue.js +8 -7
- package/lib/entities/partial/dice.js +2 -2
- package/lib/entities/partial/enhancements.js +2 -2
- package/lib/entities/partial/herbary.js +1 -1
- package/lib/entities/partial/prerequisites/single/personalityTrait.js +1 -1
- package/lib/entities/partial/prerequisites/single/ratedSum.js +1 -1
- package/lib/entities/partial/profession/options/combatTechniquesForAdventurePoints.d.ts +16 -0
- package/lib/entities/partial/profession/options/combatTechniquesForAdventurePoints.js +43 -0
- package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.d.ts +16 -0
- package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.js +39 -0
- package/lib/entities/partial/profession/packages.d.ts +67 -0
- package/lib/entities/partial/profession/packages.js +84 -0
- package/lib/entities/partial/profession/variants.d.ts +21 -0
- package/lib/entities/partial/profession/variants.js +31 -0
- package/lib/entities/partial/rated/activatable/cost.js +11 -9
- package/lib/entities/partial/rated/activatable/effect.js +2 -2
- package/lib/entities/partial/reader.d.ts +25 -1
- package/lib/entities/partial/reader.js +20 -0
- package/lib/entities/partial/units/energy.js +2 -1
- package/lib/entities/partial/units/length.js +6 -3
- package/lib/entities/partial/units/simple.d.ts +55 -0
- package/lib/entities/partial/units/simple.js +56 -0
- package/lib/entities/partial/units/timeSpan.js +11 -7
- package/lib/entities/partial/unknown.d.ts +4 -0
- package/lib/entities/partial/unknown.js +4 -0
- package/lib/entities/personalityTrait.d.ts +1 -1
- package/lib/entities/poison.d.ts +1 -1
- package/lib/entities/poison.js +1 -1
- package/lib/entities/profession.d.ts +2 -1
- package/lib/entities/profession.js +44 -62
- package/lib/entities/race.d.ts +2 -2
- package/lib/entities/sexPractice.d.ts +1 -3
- package/lib/entities/skill.d.ts +1 -1
- package/lib/entities/spell.d.ts +17 -16
- package/lib/entities/state.d.ts +1 -3
- package/lib/entities/tradeSecret.d.ts +4 -0
- package/lib/entities/tradeSecret.js +77 -0
- package/lib/env.d.ts +9 -2
- package/lib/helpers/locale.d.ts +4 -0
- package/lib/index.d.ts +320 -293
- package/lib/index.js +9 -1
- package/lib/references/index.d.ts +2 -3
- package/lib/references/index.js +11 -16
- package/lib/references/page.d.ts +4 -2
- package/lib/references/page.js +6 -4
- package/lib/references/pageRange.d.ts +3 -3
- package/lib/references/pageRange.js +6 -4
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -5,4 +5,4 @@ import type { GetInstanceById } from "../helpers/getTypes.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const getEquipmentPackageEntityDescription: import("../creator.js").EntityDescriptionCreator<"EquipmentPackage", {
|
|
7
7
|
getInstanceById: GetInstanceById<"Publication" | EquipmentIdentifier["kind"] | "SocialStatus">;
|
|
8
|
-
}, import("../index.js").EntityDescription
|
|
8
|
+
} & 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 experience level.
|
|
3
3
|
*/
|
|
4
|
-
export declare const getExperienceLevelEntityDescription: import("../creator.js").EntityDescriptionCreator<"ExperienceLevel",
|
|
5
|
-
getInstanceById: import("../helpers/getTypes.js").GetInstanceById<"Publication">;
|
|
6
|
-
}, import("../index.js").EntityDescription>;
|
|
4
|
+
export declare const getExperienceLevelEntityDescription: import("../creator.js").EntityDescriptionCreator<"ExperienceLevel", import("@elyukai/utils/nullable").AnyNonNullish & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
|
|
@@ -4,4 +4,4 @@ import type { GetInstanceById } from "../helpers/getTypes.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const getFocusRuleEntityDescription: import("../creator.js").EntityDescriptionCreator<"FocusRule", {
|
|
6
6
|
getInstanceById: GetInstanceById<"Publication" | "Subject">;
|
|
7
|
-
}, import("../index.js").EntityDescription
|
|
7
|
+
} & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
|
|
@@ -8,4 +8,4 @@ export declare const getInfluenceEntityDescription: import("../creator.js").Enti
|
|
|
8
8
|
getInstanceById: GetInstanceById<"Publication" | "Influence" | "Race" | ActivatableIdentifier["kind"] | "Aspect">;
|
|
9
9
|
getResolvedSelectOptionById: GetResolvedSelectOptionById;
|
|
10
10
|
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"ProfessionVersion">;
|
|
11
|
-
}, import("../index.js").EntityDescription
|
|
11
|
+
} & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
|
|
@@ -7,7 +7,7 @@ import type { IdMap } from "../index.js";
|
|
|
7
7
|
export declare const getBlessingEntityDescription: import("../creator.js").EntityDescriptionCreator<"Blessing", {
|
|
8
8
|
getInstanceById: GetInstanceById<"Publication" | "TargetCategory">;
|
|
9
9
|
getAllInstances: GetAllInstances<"BlessedTradition">;
|
|
10
|
-
}, import("../index.js").EntityDescription
|
|
10
|
+
} & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
|
|
11
11
|
/**
|
|
12
12
|
* Get a JSON representation of the rules text for a liturgical chant.
|
|
13
13
|
*/
|
|
@@ -15,7 +15,7 @@ export declare const getLiturgicalChantEntityDescription: import("../creator.js"
|
|
|
15
15
|
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "SkillModificationLevel" | "TargetCategory" | "Aspect" | "BlessedTradition" | "DerivedCharacteristic" | RatedIdentifier["kind"] | "Enhancement" | "FocusRule">;
|
|
16
16
|
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"Enhancement">;
|
|
17
17
|
idMap: IdMap;
|
|
18
|
-
}, import("../index.js").EntityDescription
|
|
18
|
+
} & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
|
|
19
19
|
/**
|
|
20
20
|
* Get a JSON representation of the rules text for a ceremony.
|
|
21
21
|
*/
|
|
@@ -23,4 +23,4 @@ export declare const getCeremonyEntityDescription: import("../creator.js").Entit
|
|
|
23
23
|
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "SkillModificationLevel" | "TargetCategory" | "Aspect" | "BlessedTradition" | "DerivedCharacteristic" | RatedIdentifier["kind"] | "Enhancement" | "FocusRule">;
|
|
24
24
|
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"Enhancement">;
|
|
25
25
|
idMap: IdMap;
|
|
26
|
-
}, import("../index.js").EntityDescription
|
|
26
|
+
} & 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 optional rule.
|
|
3
3
|
*/
|
|
4
|
-
export declare const getOptionalRuleEntityDescription: import("../creator.js").EntityDescriptionCreator<"OptionalRule",
|
|
5
|
-
getInstanceById: import("../helpers/getTypes.js").GetInstanceById<"Publication">;
|
|
6
|
-
}, import("../index.js").EntityDescription>;
|
|
4
|
+
export declare const getOptionalRuleEntityDescription: import("../creator.js").EntityDescriptionCreator<"OptionalRule", import("@elyukai/utils/nullable").AnyNonNullish & import("../env.js").StdEnv<"fd" | "t" | "tm" | "ibi", "Publication">, import("../index.js").EntityDescription<string>>;
|
|
@@ -35,7 +35,7 @@ const renderFixedSelectOptionsAdventurePointsValue = (locale, getAllSelectOption
|
|
|
35
35
|
.toSorted(locale.compare), "conjunction"),
|
|
36
36
|
])
|
|
37
37
|
.toSorted(on(item => item[1], locale.compare))
|
|
38
|
-
.map(([apValue, itemNames]) => `${itemNames}: ${locale.translate("{$value} Adventure Points", { value: typeof apValue === "number" ? applyNegative(apValue) : apValue })}`)
|
|
38
|
+
.map(([apValue, itemNames]) => `${itemNames}: ${locale.translate("{$value} Adventure Points", { value: typeof apValue === "number" ? applyNegative(apValue).toFixed() : apValue })}`)
|
|
39
39
|
.join("; ");
|
|
40
40
|
}
|
|
41
41
|
};
|
|
@@ -88,12 +88,13 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
|
|
|
88
88
|
if (entry.levels !== value.ByLevel.list.length) {
|
|
89
89
|
return MISSING_VALUE;
|
|
90
90
|
}
|
|
91
|
-
const
|
|
91
|
+
const levelsPrefix = translate("Level {$level}", {
|
|
92
92
|
level: Array.from({ length: entry.levels }, (_, index) => romanize(index + 1)).join("/"),
|
|
93
|
-
})
|
|
93
|
+
});
|
|
94
|
+
const mainValues = value.ByLevel.list.map(applyNegative).join("/");
|
|
94
95
|
const { additionalBySizeCategory } = value.ByLevel;
|
|
95
96
|
if (additionalBySizeCategory === undefined) {
|
|
96
|
-
return
|
|
97
|
+
return translate("{$value} Adventure Points", { value: mainValues });
|
|
97
98
|
}
|
|
98
99
|
else {
|
|
99
100
|
const sizeCategories = ["tiny", "small", "medium", "large", "huge"];
|
|
@@ -101,7 +102,7 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
|
|
|
101
102
|
.map(sizeCategory => applyNegative(additionalBySizeCategory[sizeCategory]))
|
|
102
103
|
.join("/");
|
|
103
104
|
const labels = sizeCategories.map(sizeCategory => translate(sizeCategory)).join("/");
|
|
104
|
-
return `${
|
|
105
|
+
return `${levelsPrefix}: ${mainValues} + ${translate("{$values} AP for size category {$labels} (per level)", { values, labels })}`;
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
case "DerivedFromSelection": {
|
|
@@ -281,13 +282,13 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
|
|
|
281
282
|
const labels = Array.from({ length: entry.maximum ?? 3 }, (_, index) => `${(index + 1).toFixed()}.`).join("/");
|
|
282
283
|
if (entry.maximum !== undefined) {
|
|
283
284
|
return translate("{$values} Adventure Points for the {$labels} purchase", {
|
|
284
|
-
values,
|
|
285
|
+
values: values.join("/"),
|
|
285
286
|
labels,
|
|
286
287
|
});
|
|
287
288
|
}
|
|
288
289
|
else {
|
|
289
290
|
return translate("{$values}/and so on Adventure Points for the {$labels}/and so on purchase", {
|
|
290
|
-
values,
|
|
291
|
+
values: values.join("/"),
|
|
291
292
|
labels,
|
|
292
293
|
});
|
|
293
294
|
}
|
|
@@ -3,11 +3,11 @@ import { translateR } from "./reader.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Renders a dice expression like "2D6" into a localized string.
|
|
5
5
|
*/
|
|
6
|
-
export const renderDice = (translate, dice) => translate("{$count}D{$sides}", { count: dice.number, sides: dice.sides });
|
|
6
|
+
export const renderDice = (translate, dice) => translate("{$count}D{$sides}", { count: dice.number.toFixed(), sides: dice.sides.toFixed() });
|
|
7
7
|
/**
|
|
8
8
|
* Renders a dice expression like "2D6" into a localized string.
|
|
9
9
|
*/
|
|
10
|
-
export const renderDiceR = (dice) => translateR("{$count}D{$sides}", { count: dice.number, sides: dice.sides });
|
|
10
|
+
export const renderDiceR = (dice) => translateR("{$count}D{$sides}", { count: dice.number.toFixed(), sides: dice.sides.toFixed() });
|
|
11
11
|
/**
|
|
12
12
|
* Renders a dice expression with an additional flat modifier, like "2D6+3" or "2D6-1".
|
|
13
13
|
*/
|
|
@@ -22,8 +22,8 @@ const renderEnhancement = (enhancement, parentImprovementCost) => {
|
|
|
22
22
|
const { prerequisites } = enhancement.content;
|
|
23
23
|
return translateMapR(enhancement.content.translations).thenW(translation => translation === undefined
|
|
24
24
|
? Reader.of(undefined)
|
|
25
|
-
: translateR("SR {$value}", { value: enhancement.content.skill_rating }).thenW(sr => translateR("{$value} AP", {
|
|
26
|
-
value: getEnhancementAPValue(enhancement.content.adventurePoints, parentImprovementCost),
|
|
25
|
+
: translateR("SR {$value}", { value: enhancement.content.skill_rating.toFixed() }).thenW(sr => translateR("{$value} AP", {
|
|
26
|
+
value: getEnhancementAPValue(enhancement.content.adventurePoints, parentImprovementCost).toFixed(),
|
|
27
27
|
}).thenW(ap => (prerequisites === undefined
|
|
28
28
|
? Reader.of("")
|
|
29
29
|
: translateR(".input {$hiddenCount :number} {{Prerequisites}}", {
|
|
@@ -47,7 +47,7 @@ export const renderChance = (translate, translateMap, item, includePercentage =
|
|
|
47
47
|
(item.chance === undefined
|
|
48
48
|
? undefined
|
|
49
49
|
: translate("{$valueRange} on {$dice}", {
|
|
50
|
-
valueRange: item.chance === 5 ? 1 : `1–${(item.chance / 5).toFixed()}`,
|
|
50
|
+
valueRange: item.chance === 5 ? "1" : `1–${(item.chance / 5).toFixed()}`,
|
|
51
51
|
dice: renderDice(translate, { number: 1, sides: 20 }),
|
|
52
52
|
}) +
|
|
53
53
|
(includePercentage
|
|
@@ -12,7 +12,7 @@ export const printPersonalityTraitPrerequisite = (prerequisite) => prerequisite.
|
|
|
12
12
|
: attributedNameFromInstanceR(personalityTrait, "prerequisite", "PersonalityTrait", prerequisite.id)
|
|
13
13
|
.map(name => name ?? MISSING_VALUE)
|
|
14
14
|
.thenW(name => translateR("Level {$level}", {
|
|
15
|
-
level: personalityTrait.level,
|
|
15
|
+
level: personalityTrait.level.toFixed(),
|
|
16
16
|
}).map(level => `${name} (${level})`))
|
|
17
17
|
.then(name => prerequisite.active
|
|
18
18
|
? translateR("must have {$trait}", {
|
|
@@ -13,7 +13,7 @@ export const printRatedSumPrerequisite = (prerequisite) => prerequisite.display_
|
|
|
13
13
|
.thenW(skills => localeJoinR(skills, "conjunction"))
|
|
14
14
|
.thenW(skills => translateR("the SR for {$skill} combined must add up to at least {$minRating}", {
|
|
15
15
|
skill: skills,
|
|
16
|
-
minRating: prerequisite.sum,
|
|
16
|
+
minRating: prerequisite.sum.toFixed(),
|
|
17
17
|
}))
|
|
18
18
|
.map(value => ({
|
|
19
19
|
value,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NonEmptyArray } from "@elyukai/utils/array/nonEmpty";
|
|
2
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
+
import type { CombatTechniquesForAdventurePointsOption, VariantOptionAction } from "@optolith/database-schema/gen";
|
|
4
|
+
import type { StdEnv } from "../../../../env.js";
|
|
5
|
+
import { type PreparedProfessionPackage } from "../packages.js";
|
|
6
|
+
type Option = CombatTechniquesForAdventurePointsOption;
|
|
7
|
+
type OptionEnv = StdEnv<"t" | "tm" | "ibi", "CloseCombatTechnique" | "RangedCombatTechnique">;
|
|
8
|
+
/**
|
|
9
|
+
* Renders the combat techniques for adventure points option for a list of profession packages.
|
|
10
|
+
*/
|
|
11
|
+
export declare const renderBaseCombatTechniquesForAdventurePointsOption: (professionPackages: NonEmptyArray<PreparedProfessionPackage>) => Reader<OptionEnv, string | undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Renders the combat techniques for adventure points option for a variant of a profession package.
|
|
14
|
+
*/
|
|
15
|
+
export declare const renderVariantCombatTechniquesForAdventurePointsOption: (baseOptions: Option | undefined, variantOptions: VariantOptionAction<Option>) => Reader<OptionEnv, string>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { deepEqual, equal } from "@elyukai/utils/equality";
|
|
2
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
+
import { attributedNameR, fixedNumberOrString, translateR } from "../../reader.js";
|
|
4
|
+
import { MISSING_VALUE } from "../../unknown.js";
|
|
5
|
+
import { renderParameterizedOptionForBaseAndVariant, renderParameterizedOptionForMultiplePackages, } from "../packages.js";
|
|
6
|
+
const renderCombatTechniquesForAdventurePointsOptionList = (options) => Reader.traverse(options, id => attributedNameR("profession", id).map(name => name ?? MISSING_VALUE)).map(list => list.join(", "));
|
|
7
|
+
const renderCombatTechniquesForAdventurePointsOptionText = (apValue, list) => translateR("{$apValue} AP to distribute among the following combat techniques: {$list}", {
|
|
8
|
+
apValue: fixedNumberOrString(apValue),
|
|
9
|
+
list,
|
|
10
|
+
});
|
|
11
|
+
const renderStandaloneOption = (option) => renderCombatTechniquesForAdventurePointsOptionList(option.options).thenW(list => renderCombatTechniquesForAdventurePointsOptionText(option.apValue.toFixed(), list));
|
|
12
|
+
const configs = [
|
|
13
|
+
{
|
|
14
|
+
selector: option => option.apValue,
|
|
15
|
+
equality: equal,
|
|
16
|
+
render: apValue => Reader.of(apValue),
|
|
17
|
+
renderAsString: apValue => Reader.of(apValue.toFixed()),
|
|
18
|
+
emptyText: "—",
|
|
19
|
+
joiner: "/",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
selector: option => option.options,
|
|
23
|
+
equality: deepEqual,
|
|
24
|
+
render: renderCombatTechniquesForAdventurePointsOptionList,
|
|
25
|
+
renderAsString: renderCombatTechniquesForAdventurePointsOptionList,
|
|
26
|
+
emptyText: "—",
|
|
27
|
+
joiner: " / ",
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
const render = (apValue, list) => translateR("{$apValue} AP to distribute among the following combat techniques: {$list}", {
|
|
31
|
+
apValue: fixedNumberOrString(apValue),
|
|
32
|
+
list,
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* Renders the combat techniques for adventure points option for a list of profession packages.
|
|
36
|
+
*/
|
|
37
|
+
export const renderBaseCombatTechniquesForAdventurePointsOption = (professionPackages) => renderParameterizedOptionForMultiplePackages(professionPackages, pkg => pkg.content.options?.combatTechniquesForAdventurePoints, { type: "separate", emptyText: "—", joiner: " / " }, render, ...configs);
|
|
38
|
+
/**
|
|
39
|
+
* Renders the combat techniques for adventure points option for a variant of a profession package.
|
|
40
|
+
*/
|
|
41
|
+
export const renderVariantCombatTechniquesForAdventurePointsOption = (baseOptions, variantOptions) => renderParameterizedOptionForBaseAndVariant(baseOptions, variantOptions, { type: "separate", emptyText: "—", joiner: " / " }, base => renderCombatTechniquesForAdventurePointsOptionList(base.options).thenW(list => translateR("no AP to distribute among the following combat techniques: {$list}", {
|
|
42
|
+
list,
|
|
43
|
+
})), renderStandaloneOption, render, ...configs);
|
package/lib/entities/partial/profession/options/numberOfSpellworksTotalingAdventurePoints.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NonEmptyArray } from "@elyukai/utils/array/nonEmpty";
|
|
2
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
+
import type { SpellworksOptions, VariantOptionAction } from "@optolith/database-schema/gen";
|
|
4
|
+
import type { StdEnv } from "../../../../env.js";
|
|
5
|
+
import { type PreparedProfessionPackage } from "../packages.js";
|
|
6
|
+
type Option = SpellworksOptions;
|
|
7
|
+
type OptionEnv = StdEnv<"t">;
|
|
8
|
+
/**
|
|
9
|
+
* Renders the combat techniques for adventure points option for a list of profession packages.
|
|
10
|
+
*/
|
|
11
|
+
export declare const renderBaseNumberOfSpellworksTotalingAdventurePoints: (professionPackages: NonEmptyArray<PreparedProfessionPackage>) => Reader<OptionEnv, string | undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Renders the combat techniques for adventure points option for a variant of a profession package.
|
|
14
|
+
*/
|
|
15
|
+
export declare const renderVariantNumberOfSpellworksTotalingAdventurePoints: (baseOptions: Option | undefined, variantOptions: VariantOptionAction<Option>) => Reader<OptionEnv, string>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { equal } from "@elyukai/utils/equality";
|
|
2
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
+
import { fixedNumberOrString, translateR } from "../../reader.js";
|
|
4
|
+
import { renderParameterizedOptionForBaseAndVariant, renderParameterizedOptionForMultiplePackages, } from "../packages.js";
|
|
5
|
+
const configs = [
|
|
6
|
+
{
|
|
7
|
+
selector: option => option.number,
|
|
8
|
+
equality: equal,
|
|
9
|
+
render: apValue => Reader.of(apValue),
|
|
10
|
+
renderAsString: apValue => Reader.of(apValue.toFixed()),
|
|
11
|
+
emptyText: "—",
|
|
12
|
+
joiner: "/",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
selector: option => option.apValue,
|
|
16
|
+
equality: equal,
|
|
17
|
+
render: apValue => Reader.of(apValue),
|
|
18
|
+
renderAsString: apValue => Reader.of(apValue.toFixed()),
|
|
19
|
+
emptyText: "—",
|
|
20
|
+
joiner: "/",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
const render = (count, apValue) => typeof count === "number"
|
|
24
|
+
? translateR(".input {$count :number} {{{$count} more spellworks totaling {$apValue} AP}}", {
|
|
25
|
+
count,
|
|
26
|
+
apValue: fixedNumberOrString(apValue),
|
|
27
|
+
})
|
|
28
|
+
: translateR("{$count} more spellworks totaling {$apValue} AP", {
|
|
29
|
+
count,
|
|
30
|
+
apValue: fixedNumberOrString(apValue),
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Renders the combat techniques for adventure points option for a list of profession packages.
|
|
34
|
+
*/
|
|
35
|
+
export const renderBaseNumberOfSpellworksTotalingAdventurePoints = (professionPackages) => renderParameterizedOptionForMultiplePackages(professionPackages, pkg => pkg.content.options?.spellworks, { type: "separate", emptyText: "—", joiner: " / " }, render, ...configs);
|
|
36
|
+
/**
|
|
37
|
+
* Renders the combat techniques for adventure points option for a variant of a profession package.
|
|
38
|
+
*/
|
|
39
|
+
export const renderVariantNumberOfSpellworksTotalingAdventurePoints = (baseOptions, variantOptions) => renderParameterizedOptionForBaseAndVariant(baseOptions, variantOptions, { type: "separate", emptyText: "—", joiner: " / " }, base => render(0, base.apValue), base => render(base.number, base.apValue), render, ...configs);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { NonEmptyArray } from "@elyukai/utils/array/nonEmpty";
|
|
2
|
+
import type { Equality } from "@elyukai/utils/equality";
|
|
3
|
+
import type { AnyNonNullish } from "@elyukai/utils/nullable";
|
|
4
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
5
|
+
import type { ExperienceLevel, Profession_ID, ProfessionPackage, VariantOptionAction } from "@optolith/database-schema/gen";
|
|
6
|
+
import type { StdEnv, StdReader } from "../../../env.js";
|
|
7
|
+
/**
|
|
8
|
+
* Profession Package with its associated experience level.
|
|
9
|
+
*/
|
|
10
|
+
export type PreparedProfessionPackage = {
|
|
11
|
+
id: string;
|
|
12
|
+
content: ProfessionPackage;
|
|
13
|
+
experienceLevel: ExperienceLevel;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Collects all profession packages for a given profession and returns them sorted by their associated experience level.
|
|
17
|
+
*/
|
|
18
|
+
export declare const prepareProfessionPackages: (professionId: Profession_ID) => StdReader<PreparedProfessionPackage[], "ibi" | "acibp", "ExperienceLevel", never, "ProfessionPackage">;
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for retrieving, rendering and combining an option parameter.
|
|
21
|
+
*/
|
|
22
|
+
export type OptionParameterConfig<T, P, R, E> = {
|
|
23
|
+
selector: (option: T) => P;
|
|
24
|
+
equality: Equality<P | undefined>;
|
|
25
|
+
render: (option: P) => Reader<E, R>;
|
|
26
|
+
renderAsString: (option: P) => Reader<E, string>;
|
|
27
|
+
emptyText: string;
|
|
28
|
+
joiner: string;
|
|
29
|
+
};
|
|
30
|
+
type OptionParameterValueMap = [value: unknown, result: unknown][];
|
|
31
|
+
/**
|
|
32
|
+
* Option parameter configurations for multiple parameters.
|
|
33
|
+
*/
|
|
34
|
+
export type OptionParameterConfigs<T, P extends OptionParameterValueMap, E> = {
|
|
35
|
+
[K in keyof P]: OptionParameterConfig<T, P[K][0], P[K][1], E>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Extract parameters for final text render.
|
|
39
|
+
*/
|
|
40
|
+
type OptionParameterTextRenderParameters<P extends OptionParameterValueMap> = {
|
|
41
|
+
[K in keyof P]: P[K][1] | string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Renders the text for multiple parameters.
|
|
45
|
+
*/
|
|
46
|
+
export type OptionParameterTextRender<P extends OptionParameterValueMap, E> = (...args: OptionParameterTextRenderParameters<P>) => Reader<E, string>;
|
|
47
|
+
/**
|
|
48
|
+
* Renders a two-parameter option for a list of profession packages.
|
|
49
|
+
*/
|
|
50
|
+
export declare const renderParameterizedOptionForMultiplePackages: <T, P extends OptionParameterValueMap, E>(professionPackages: NonEmptyArray<PreparedProfessionPackage>, optionSelector: (pkg: PreparedProfessionPackage) => T | undefined, allDifferent: {
|
|
51
|
+
type: "separate";
|
|
52
|
+
emptyText: string;
|
|
53
|
+
joiner: string;
|
|
54
|
+
} | {
|
|
55
|
+
type: "combined";
|
|
56
|
+
}, renderText: OptionParameterTextRender<P, E>, ...configs: OptionParameterConfigs<T, P, E>) => Reader<E, string | undefined>;
|
|
57
|
+
/**
|
|
58
|
+
* Renders a two-parameter option for a list of profession packages.
|
|
59
|
+
*/
|
|
60
|
+
export declare const renderParameterizedOptionForBaseAndVariant: <T extends AnyNonNullish, P extends OptionParameterValueMap, E>(baseOptions: T | undefined, variantOptions: VariantOptionAction<T>, allDifferent: {
|
|
61
|
+
type: "separate";
|
|
62
|
+
emptyText: string;
|
|
63
|
+
joiner: string;
|
|
64
|
+
} | {
|
|
65
|
+
type: "combined";
|
|
66
|
+
}, renderRemove: (base: T) => Reader<E, string>, renderAdd: (override: T) => Reader<E, string>, renderText: OptionParameterTextRender<P, E>, ...configs: OptionParameterConfigs<T, P, E>) => Reader<E & StdEnv<"t">, string>;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { allSame } from "@elyukai/utils/array/filters";
|
|
2
|
+
import { on } from "@elyukai/utils/function";
|
|
3
|
+
import { compareNumber } from "@elyukai/utils/ordering";
|
|
4
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
5
|
+
import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
6
|
+
import { LOGIC_ERROR } from "../unknown.js";
|
|
7
|
+
import { insteadOfR, renderVariantOptionPaths } from "./variants.js";
|
|
8
|
+
/**
|
|
9
|
+
* Collects all profession packages for a given profession and returns them sorted by their associated experience level.
|
|
10
|
+
*/
|
|
11
|
+
export const prepareProfessionPackages = (professionId) => Reader.asks(({ getInstanceById, getChildInstancesForInstanceId }) => getChildInstancesForInstanceId("ProfessionPackage", professionId)
|
|
12
|
+
.map((professionPackage) => ({
|
|
13
|
+
...professionPackage,
|
|
14
|
+
experienceLevel: getInstanceById("ExperienceLevel", professionPackage.content.experience_level),
|
|
15
|
+
}))
|
|
16
|
+
.filter((professionPackage) => professionPackage.experienceLevel !== undefined)
|
|
17
|
+
.toSorted(on(item => item.experienceLevel.adventure_points, compareNumber)));
|
|
18
|
+
const renderSeparatedOptionList = (options, render, emptyText, joiner) => Reader.traverse(options, option => option === undefined ? Reader.of(emptyText) : render(option)).map(list => list.join(joiner));
|
|
19
|
+
/**
|
|
20
|
+
* Renders a two-parameter option for a list of profession packages.
|
|
21
|
+
*/
|
|
22
|
+
export const renderParameterizedOptionForMultiplePackages = (professionPackages, optionSelector, allDifferent, renderText, ...configs) => {
|
|
23
|
+
const options = professionPackages.map(optionSelector);
|
|
24
|
+
const [firstOption] = options;
|
|
25
|
+
const sames = configs.map(config => allSame(options, on(option => (option === undefined ? undefined : config.selector(option)), config.equality)));
|
|
26
|
+
if (sames.some(same => same)) {
|
|
27
|
+
if (firstOption === undefined) {
|
|
28
|
+
return Reader.of(undefined);
|
|
29
|
+
}
|
|
30
|
+
return Reader.traverse(configs, (config, configIndex) => sames[configIndex] === true
|
|
31
|
+
? config.render(config.selector(firstOption))
|
|
32
|
+
: renderSeparatedOptionList(options, option => config.renderAsString(config.selector(option)), config.emptyText, config.joiner)).thenW(parameterValues => renderText(...parameterValues));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
switch (allDifferent.type) {
|
|
36
|
+
case "combined":
|
|
37
|
+
return Reader.traverse(configs, config => renderSeparatedOptionList(options, option => config.renderAsString(config.selector(option)), config.emptyText, config.joiner)).thenW(parameterValues => renderText(...parameterValues));
|
|
38
|
+
case "separate":
|
|
39
|
+
return renderSeparatedOptionList(options, option => Reader.traverse(configs, config => config.render(config.selector(option))).thenW(parameterValues => renderText(...parameterValues)), allDifferent.emptyText, allDifferent.joiner);
|
|
40
|
+
default:
|
|
41
|
+
return assertExhaustive(allDifferent);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Renders a two-parameter option for a list of profession packages.
|
|
47
|
+
*/
|
|
48
|
+
export const renderParameterizedOptionForBaseAndVariant = (baseOptions, variantOptions, allDifferent, renderRemove, renderAdd, renderText, ...configs) => renderVariantOptionPaths(baseOptions, variantOptions, {
|
|
49
|
+
remove: renderRemove,
|
|
50
|
+
add: renderAdd,
|
|
51
|
+
update: (base, override) => {
|
|
52
|
+
const sames = configs.map(config => config.equality(config.selector(base), config.selector(override)));
|
|
53
|
+
if (sames.every(same => same)) {
|
|
54
|
+
return Reader.of(LOGIC_ERROR); // Base and variant would be equal then, so the variant option should be removed or changed
|
|
55
|
+
}
|
|
56
|
+
if (sames.some(same => same)) {
|
|
57
|
+
return Reader.traverse(configs, (config, configIndex) => sames[configIndex] === true
|
|
58
|
+
? config.render(config.selector(base))
|
|
59
|
+
: config
|
|
60
|
+
.renderAsString(config.selector(base))
|
|
61
|
+
.thenW(baseValue => config
|
|
62
|
+
.renderAsString(config.selector(override))
|
|
63
|
+
.thenW(overrideValue => insteadOfR(overrideValue, baseValue)))).thenW(parameterValues => renderText(...parameterValues));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
switch (allDifferent.type) {
|
|
67
|
+
case "combined":
|
|
68
|
+
return Reader.traverse(configs, config => config
|
|
69
|
+
.renderAsString(config.selector(base))
|
|
70
|
+
.thenW(baseValue => config
|
|
71
|
+
.renderAsString(config.selector(override))
|
|
72
|
+
.thenW(overrideValue => insteadOfR(overrideValue, baseValue)))).thenW(parameterValues => renderText(...parameterValues));
|
|
73
|
+
case "separate":
|
|
74
|
+
return Reader.traverse(configs, config => config.render(config.selector(base)))
|
|
75
|
+
.thenW(parameterValues => renderText(...parameterValues))
|
|
76
|
+
.thenW(baseValue => Reader.traverse(configs, config => config.render(config.selector(override)))
|
|
77
|
+
.thenW(parameterValues => renderText(...parameterValues))
|
|
78
|
+
.thenW(overrideValue => insteadOfR(overrideValue, baseValue)));
|
|
79
|
+
default:
|
|
80
|
+
return assertExhaustive(allDifferent);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AnyNonNullish } from "@elyukai/utils/nullable";
|
|
2
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
+
import type { ProfessionPackage, ProfessionPackageOptions, ProfessionVariant, ProfessionVariantPackageOptions, VariantOptionAction } from "@optolith/database-schema/gen";
|
|
4
|
+
/**
|
|
5
|
+
* Renders an option for a profession variant, using the base profession package and the variant's options.
|
|
6
|
+
*/
|
|
7
|
+
export declare const renderVariantOption: <Env, K extends keyof ProfessionVariantPackageOptions>(base: ProfessionPackage, variant: ProfessionVariant, key: K, render: (base: ProfessionPackageOptions[K] | undefined, variant: NonNullable<ProfessionVariantPackageOptions[K]>) => Reader<Env, string>) => Reader<Env, string[]>;
|
|
8
|
+
/**
|
|
9
|
+
* Render a variant option based on whether it is a removal, addition, or override of the base option.
|
|
10
|
+
*/
|
|
11
|
+
export declare const renderVariantOptionPaths: <T extends AnyNonNullish, Env1, Env2, Env3>(base: T | undefined, variant: VariantOptionAction<T>, paths: {
|
|
12
|
+
remove: (base: T) => Reader<Env1, string>;
|
|
13
|
+
add: (variant: T) => Reader<Env2, string>;
|
|
14
|
+
update: (base: T, variant: T) => Reader<Env3, string>;
|
|
15
|
+
}) => Reader<Env1 & Env2 & Env3, string>;
|
|
16
|
+
/**
|
|
17
|
+
* Helper for "X instead of Y" text.
|
|
18
|
+
*/
|
|
19
|
+
export declare const insteadOfR: (replacement: string | number, base: string | number) => Reader<{
|
|
20
|
+
translate: import("../../../helpers/translate.js").Translate;
|
|
21
|
+
}, string>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { nullableToArray } from "@elyukai/utils/nullable";
|
|
2
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
+
import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
4
|
+
import { fixedNumberOrString, translateR } from "../reader.js";
|
|
5
|
+
import { MISSING_VALUE } from "../unknown.js";
|
|
6
|
+
/**
|
|
7
|
+
* Renders an option for a profession variant, using the base profession package and the variant's options.
|
|
8
|
+
*/
|
|
9
|
+
export const renderVariantOption = (base, variant, key, render) => (variant.options?.[key] === undefined
|
|
10
|
+
? Reader.of(undefined)
|
|
11
|
+
: render(base.options?.[key], variant.options[key])).map(text => nullableToArray(text));
|
|
12
|
+
/**
|
|
13
|
+
* Render a variant option based on whether it is a removal, addition, or override of the base option.
|
|
14
|
+
*/
|
|
15
|
+
export const renderVariantOptionPaths = (base, variant, paths) => {
|
|
16
|
+
switch (variant.kind) {
|
|
17
|
+
case "Remove":
|
|
18
|
+
return base === undefined ? Reader.of(MISSING_VALUE) : paths.remove(base);
|
|
19
|
+
case "Override":
|
|
20
|
+
return base === undefined ? paths.add(variant.Override) : paths.update(base, variant.Override);
|
|
21
|
+
default:
|
|
22
|
+
return assertExhaustive(variant);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Helper for "X instead of Y" text.
|
|
27
|
+
*/
|
|
28
|
+
export const insteadOfR = (replacement, base) => translateR("{$replacement} instead of {$base}", {
|
|
29
|
+
replacement: fixedNumberOrString(replacement),
|
|
30
|
+
base: fixedNumberOrString(base),
|
|
31
|
+
});
|
|
@@ -3,7 +3,7 @@ import { Reader } from "@elyukai/utils/reader";
|
|
|
3
3
|
import { mapNullable } from "@optolith/helpers/nullable";
|
|
4
4
|
import { assertExhaustive } from "@optolith/helpers/typeSafety";
|
|
5
5
|
import { renderParameterMap } from "../../map.js";
|
|
6
|
-
import { formatEnergyFnR, formatEnergyR, getInstanceByIdFnR, modifiableBySpeedR, responsiveLocaleJoinR, responsiveR, responsiveTextR, responsiveTranslateR, translateMapR, translateR, } from "../../reader.js";
|
|
6
|
+
import { fixedNumberOrString, formatEnergyFnR, formatEnergyR, getInstanceByIdFnR, modifiableBySpeedR, responsiveLocaleJoinR, responsiveR, responsiveTextR, responsiveTranslateR, responsiveTranslateSplitR, translateMapR, translateR, } from "../../reader.js";
|
|
7
7
|
import { appendNoteIfNeeded, replaceTextIfNeeded } from "../../responsiveText.js";
|
|
8
8
|
import { formatCombinedTimeSpanR } from "../../units/timeSpan.js";
|
|
9
9
|
import { MISSING_VALUE } from "../../unknown.js";
|
|
@@ -33,9 +33,11 @@ const appendPerCountableToCostIfNeeded = (value, baseCost) => {
|
|
|
33
33
|
})));
|
|
34
34
|
});
|
|
35
35
|
};
|
|
36
|
-
const appendPermanentCostIfNeeded = (permanentValue, baseCost) => mapNullable(permanentValue, value =>
|
|
37
|
-
const appendElvenPermanentCostIfNeeded = (permanent, baseCost) => mapNullable(permanent, value =>
|
|
36
|
+
const appendPermanentCostIfNeeded = (permanentValue, baseCost) => mapNullable(permanentValue, value => responsiveTranslateSplitR(".input {$value :number} {{, {$value} of which are permanent}}", " ({$value} perm.)", { value }, { value: value.toFixed() }).map(text => baseCost + text)) ?? Reader.of(baseCost);
|
|
37
|
+
const appendElvenPermanentCostIfNeeded = (permanent, baseCost) => mapNullable(permanent, value => responsiveTranslateSplitR(".input {$value :number} {{{$value} permanent AE}}", "{$value} pAE", {
|
|
38
38
|
value: value.value,
|
|
39
|
+
}, {
|
|
40
|
+
value: value.value.toFixed(),
|
|
39
41
|
})
|
|
40
42
|
.thenW(text => replaceTextIfNeeded(value.translations, text))
|
|
41
43
|
.map(text => baseCost + text)) ?? Reader.of(baseCost);
|
|
@@ -117,7 +119,7 @@ const renderMultipleOneTimeCosts = (type, value) => {
|
|
|
117
119
|
export const renderStandaloneCostMap = (value) => renderParameterMap(value, option => Reader.of(option.value), formatEnergyR, value.options.every(option => option.value.permanentValue !== undefined)
|
|
118
120
|
? {
|
|
119
121
|
format: values => translateR(", {$value} of which are permanent", {
|
|
120
|
-
value: values,
|
|
122
|
+
value: fixedNumberOrString(values),
|
|
121
123
|
}),
|
|
122
124
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
|
123
125
|
render: option => Reader.of(option.value.permanentValue),
|
|
@@ -132,7 +134,7 @@ const renderOneTimeCostMap = (value) => {
|
|
|
132
134
|
return renderParameterMap(value.Modifiable.map, option => deriveModifiableCost(option.initialModificationLevel).map(cost => cost ?? MISSING_VALUE), singleValue => formatEnergyR(singleValue), value.Modifiable.map.options.every(option => option.value.permanentValue !== undefined)
|
|
133
135
|
? {
|
|
134
136
|
format: values => translateR(", {$value} of which are permanent", {
|
|
135
|
-
value: values,
|
|
137
|
+
value: fixedNumberOrString(values),
|
|
136
138
|
}),
|
|
137
139
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
|
138
140
|
render: option => Reader.of(option.value.permanentValue),
|
|
@@ -142,7 +144,7 @@ const renderOneTimeCostMap = (value) => {
|
|
|
142
144
|
return renderParameterMap(value.NonModifiable.map, option => Reader.of(option.value), formatEnergyR, value.NonModifiable.map.options.every(option => option.value.permanentValue !== undefined)
|
|
143
145
|
? {
|
|
144
146
|
format: values => translateR(", {$value} of which are permanent", {
|
|
145
|
-
value: values,
|
|
147
|
+
value: fixedNumberOrString(values),
|
|
146
148
|
}),
|
|
147
149
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
|
148
150
|
render: option => Reader.of(option.value.permanentValue),
|
|
@@ -240,10 +242,10 @@ export const renderMagicalActionCost = (cost) => {
|
|
|
240
242
|
case "FirstPerson":
|
|
241
243
|
return Reader.asks(({ translate }) => translate("{$firstPersonValue} for the first person; {$additionalPersonValue} for each additional person", {
|
|
242
244
|
firstPersonValue: translate("{$value} AE", {
|
|
243
|
-
value: cost.FirstPerson.value,
|
|
245
|
+
value: cost.FirstPerson.value.toFixed(),
|
|
244
246
|
}),
|
|
245
247
|
additionalPersonValue: translate("{$value} AE", {
|
|
246
|
-
value: cost.FirstPerson.value / 2,
|
|
248
|
+
value: (cost.FirstPerson.value / 2).toFixed(),
|
|
247
249
|
}),
|
|
248
250
|
}));
|
|
249
251
|
case "ByPrimaryPatron":
|
|
@@ -252,7 +254,7 @@ export const renderMagicalActionCost = (cost) => {
|
|
|
252
254
|
return Reader.asks(({ translate }) => cost.All.minimum === undefined
|
|
253
255
|
? translate("All AE")
|
|
254
256
|
: translate("All AE, at least {$value} AE", {
|
|
255
|
-
value: cost.All.minimum,
|
|
257
|
+
value: cost.All.minimum.toFixed(),
|
|
256
258
|
}));
|
|
257
259
|
case "Map":
|
|
258
260
|
return renderStandaloneCostMap(cost.Map);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mapNullable } from "@optolith/helpers/nullable";
|
|
2
2
|
import { assertExhaustive } from "@optolith/helpers/typeSafety";
|
|
3
|
-
import { translateFnR } from "../../reader.js";
|
|
3
|
+
import { fixedNumberOrString, translateFnR } from "../../reader.js";
|
|
4
4
|
const getContentPartsForQualityLevels = (getQualityLevelString, source) => translateFnR.map((translate) => ({
|
|
5
5
|
label: translate("Effect"),
|
|
6
6
|
value: [
|
|
@@ -13,7 +13,7 @@ const getContentPartsForQualityLevels = (getQualityLevelString, source) => trans
|
|
|
13
13
|
style: "hidden",
|
|
14
14
|
items: source.quality_levels.map((text, index) => ({
|
|
15
15
|
label: translate("QL {$value}", {
|
|
16
|
-
value: getQualityLevelString(index),
|
|
16
|
+
value: fixedNumberOrString(getQualityLevelString(index)),
|
|
17
17
|
}),
|
|
18
18
|
value: text,
|
|
19
19
|
})),
|