@optolith/entity-descriptions 0.11.2 → 0.13.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 +22 -0
- package/lib/entities/activatable.js +3 -3
- package/lib/entities/partial/activatableNameChunks.js +1 -1
- package/lib/entities/partial/adventurePointsValue.js +20 -3
- package/lib/entities/partial/commonnessRatedAdvantagesAndDisadvantages.js +3 -3
- package/lib/entities/partial/map.d.ts +17 -31
- package/lib/entities/partial/map.js +35 -48
- package/lib/entities/partial/markdown.d.ts +5 -1
- package/lib/entities/partial/markdown.js +13 -1
- package/lib/entities/partial/prerequisites/index.d.ts +5 -1
- package/lib/entities/partial/prerequisites/index.js +21 -0
- package/lib/entities/partial/prerequisites/part.d.ts +1 -0
- package/lib/entities/partial/prerequisites/part.js +14 -7
- package/lib/entities/partial/rated/activatable/cost.d.ts +4 -2
- package/lib/entities/partial/rated/activatable/cost.js +12 -12
- package/lib/entities/partial/reader.d.ts +4 -0
- package/lib/entities/partial/reader.js +4 -0
- package/lib/entities/profession.js +16 -5
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [0.13.0](https://github.com/Optolith/entity-descriptions/compare/v0.12.0...v0.13.0) (2026-09-16)
|
|
6
|
+
|
|
7
|
+
### ⚠ BREAKING CHANGES
|
|
8
|
+
|
|
9
|
+
* **deps:** upgrade to schema 0.63.0
|
|
10
|
+
|
|
11
|
+
### build
|
|
12
|
+
|
|
13
|
+
* **deps:** upgrade to schema 0.63.0 ([e879198](https://github.com/Optolith/entity-descriptions/commit/e8791981d739ad4ea786d566eeeb80c918f6e7d5))
|
|
14
|
+
## [0.12.0](https://github.com/Optolith/entity-descriptions/compare/v0.11.2...v0.12.0) (2026-09-15)
|
|
15
|
+
|
|
16
|
+
### ⚠ BREAKING CHANGES
|
|
17
|
+
|
|
18
|
+
* **deps:** upgrade to schema 0.62.0
|
|
19
|
+
|
|
20
|
+
### build
|
|
21
|
+
|
|
22
|
+
* **deps:** upgrade to schema 0.62.0 ([13badb3](https://github.com/Optolith/entity-descriptions/commit/13badb304c61a1f4b48241435d5e19400bbbd145))
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* nested attributed strings in professions ([cdecedd](https://github.com/Optolith/entity-descriptions/commit/cdecedd5552bb22af4bfec73c4d06ca4b30e3121))
|
|
5
27
|
## [0.11.2](https://github.com/Optolith/entity-descriptions/compare/v0.11.1...v0.11.2) (2026-09-14)
|
|
6
28
|
|
|
7
29
|
### Bug Fixes
|
|
@@ -15,7 +15,7 @@ import { Case, fromUniformCase } from "tsondb/schema/gen";
|
|
|
15
15
|
import { createEntityDescriptionCreator } from "../creator.js";
|
|
16
16
|
import { renderActivatableNameComponents } from "./partial/activatableNameChunks.js";
|
|
17
17
|
import { renderAdventurePointsValue } from "./partial/adventurePointsValue.js";
|
|
18
|
-
import {
|
|
18
|
+
import { renderParameterMap } from "./partial/map.js";
|
|
19
19
|
import { attributedName } from "./partial/markdown.js";
|
|
20
20
|
import { additionFormatter } from "./partial/mathOperation.js";
|
|
21
21
|
import { printAdvantageDisadvantagePrerequisites, printGeneralPrerequisites, } from "./partial/prerequisites/index.js";
|
|
@@ -311,7 +311,7 @@ const renderVolumeValue = (translate, translateMap, getAllResolvedSelectOptions,
|
|
|
311
311
|
levels: volume.ByLevel.list.map((_, index) => romanize(index + 1)).join("/"),
|
|
312
312
|
});
|
|
313
313
|
case "Map":
|
|
314
|
-
return
|
|
314
|
+
return renderParameterMap(volume.Map, option => Reader.of(option.points), values => translateR("{$points} points", { points: values })).run({
|
|
315
315
|
translate,
|
|
316
316
|
translateMap,
|
|
317
317
|
responsiveTextSize,
|
|
@@ -454,7 +454,7 @@ const renderBindingCost = (translate, translateMap, localeCompare, getAllResolve
|
|
|
454
454
|
}),
|
|
455
455
|
});
|
|
456
456
|
case "Map":
|
|
457
|
-
return
|
|
457
|
+
return renderParameterMap(cost.Map, option => Reader.of(option.permanentValue), values => translateR("{$value} permanent AE", { value: values })).run({
|
|
458
458
|
translate,
|
|
459
459
|
translateMap,
|
|
460
460
|
responsiveTextSize,
|
|
@@ -136,7 +136,7 @@ export const renderCombinedActivatableNameComponents = (translateMap, components
|
|
|
136
136
|
useParenthesis: true,
|
|
137
137
|
},
|
|
138
138
|
options: join(components.options.map(chunk => renderActivatableNameChunk(translateMap, normalizeChunks(chunk)))),
|
|
139
|
-
}, formatAsPrerequisite), components.id.kind, fromUniformCase(components.id), { context });
|
|
139
|
+
}, formatAsPrerequisite), components.id.kind, fromUniformCase(components.id), { context: `"${context}"` });
|
|
140
140
|
/**
|
|
141
141
|
* Renders the name components of multiple activatable entries.
|
|
142
142
|
*/
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { on } from "@elyukai/utils/function";
|
|
2
2
|
import { isNotNullish } from "@elyukai/utils/nullable";
|
|
3
|
+
import { Reader } from "@elyukai/utils/reader";
|
|
3
4
|
import { unique } from "@optolith/helpers/array";
|
|
4
5
|
import { deepEqual, numAsc } from "@optolith/helpers/compare";
|
|
5
6
|
import { romanize } from "@optolith/helpers/roman";
|
|
6
7
|
import { assertExhaustive } from "@optolith/helpers/typeSafety";
|
|
8
|
+
import { renderDefaultValueMapLabel, renderValueMap } from "./map.js";
|
|
7
9
|
import { evaluateMathOperation } from "./mathOperation.js";
|
|
10
|
+
import { translateR } from "./reader.js";
|
|
11
|
+
import { ResponsiveTextSize } from "./responsiveText.js";
|
|
8
12
|
import { MISSING_VALUE } from "./unknown.js";
|
|
9
13
|
const typographicSign = (num) => (num > 0 ? "" : "−") + Math.abs(num).toFixed();
|
|
10
14
|
const renderFixedSelectOptionsAdventurePointsValue = (locale, getAllSelectOptions, getNameForSelectOptionId, getApValueForSelectOption, applyNegative) => {
|
|
@@ -250,8 +254,10 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
|
|
|
250
254
|
});
|
|
251
255
|
}
|
|
252
256
|
case "Expression": {
|
|
253
|
-
const expression = value.DependingOnActiveInstances.Expression;
|
|
254
|
-
const values = Array.from({
|
|
257
|
+
const { expression, customLabels } = value.DependingOnActiveInstances.Expression;
|
|
258
|
+
const values = Array.from({
|
|
259
|
+
length: entry.maximum ?? (customLabels === undefined ? 3 : customLabels.options.length),
|
|
260
|
+
}, (_, index) => applyNegative(evaluateMathOperation(expression, exprValue => {
|
|
255
261
|
switch (exprValue.kind) {
|
|
256
262
|
case "Constant":
|
|
257
263
|
return exprValue.Constant;
|
|
@@ -260,7 +266,18 @@ export const renderAdventurePointsValue = (locale, getNameForId, getNameForSelec
|
|
|
260
266
|
default:
|
|
261
267
|
return assertExhaustive(exprValue);
|
|
262
268
|
}
|
|
263
|
-
})))
|
|
269
|
+
})));
|
|
270
|
+
if (customLabels !== undefined) {
|
|
271
|
+
return renderValueMap(entry.maximum === undefined
|
|
272
|
+
? customLabels
|
|
273
|
+
: { ...customLabels, options: customLabels.options.slice(0, entry.maximum) },
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- customLabels.options is guaranteed to have at least as many entries as values
|
|
275
|
+
(_, index) => Reader.of(values[index]), renderDefaultValueMapLabel, singleValue => typeof singleValue === "number"
|
|
276
|
+
? translateR(".input {$value :number} {{{$value} Adventure Points}}", {
|
|
277
|
+
value: singleValue,
|
|
278
|
+
})
|
|
279
|
+
: translateR("{$value} Adventure Points", { value: singleValue })).run({ translate, translateMap, responsiveTextSize: ResponsiveTextSize.Full });
|
|
280
|
+
}
|
|
264
281
|
const labels = Array.from({ length: entry.maximum ?? 3 }, (_, index) => `${(index + 1).toFixed()}.`).join("/");
|
|
265
282
|
if (entry.maximum !== undefined) {
|
|
266
283
|
return translate("{$values} Adventure Points for the {$labels} purchase", {
|
|
@@ -5,7 +5,7 @@ import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
|
5
5
|
import { ensureNonEmpty } from "@optolith/helpers/array";
|
|
6
6
|
import { Case } from "tsondb/schema/gen";
|
|
7
7
|
import { makeNameBuilderRulesWithDefaults, renderCombinedActivatableNameComponents, renderNameComponentsOptions, } from "./activatableNameChunks.js";
|
|
8
|
-
import {
|
|
8
|
+
import { attributedNameFromText, customName } from "./markdown.js";
|
|
9
9
|
import { MISSING_VALUE } from "./unknown.js";
|
|
10
10
|
const convertCommonnessRatedAdvantageOrDisadvantageLevel = (level) => {
|
|
11
11
|
switch (level.kind) {
|
|
@@ -29,9 +29,9 @@ const makeActivatableIdentifierForCommonnessRatedAdvantageOrDisadvantage = (enti
|
|
|
29
29
|
};
|
|
30
30
|
const renderCommonnessRatedAdvantageOrDisadvantageName = (translateMap, getInstanceById, getResolvedSelectOptionById, localeCompare, entity, item) => {
|
|
31
31
|
const customTranslation = translateMap(item.translations);
|
|
32
|
-
return (
|
|
32
|
+
return (customName(translateMap, getInstanceById, (translation, instance) => {
|
|
33
33
|
if (customTranslation?.full !== undefined) {
|
|
34
|
-
return customTranslation.full;
|
|
34
|
+
return attributedNameFromText(customTranslation.full, "commonness", entity, item.id);
|
|
35
35
|
}
|
|
36
36
|
const id = makeActivatableIdentifierForCommonnessRatedAdvantageOrDisadvantage(entity, item.id);
|
|
37
37
|
const level = item.level === undefined
|
|
@@ -1,40 +1,26 @@
|
|
|
1
1
|
import { Reader } from "@elyukai/utils/reader";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ParameterMap, ParameterMapOption, ResponsiveTextOptional, ValueMap } from "@optolith/database-schema/gen";
|
|
3
3
|
import type { StdEnv, StdReader } from "../../env.js";
|
|
4
4
|
import type { LocaleMap } from "../../helpers/translate.js";
|
|
5
5
|
/**
|
|
6
6
|
* Renders a map of options to a string.
|
|
7
7
|
*/
|
|
8
|
-
export declare const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}[];
|
|
13
|
-
style: ParameterMapStyle;
|
|
14
|
-
translations?: LocaleMap<T>;
|
|
15
|
-
}, getValue: (option: V) => Reader<VEnv, string | number>, surroundValues: ((values: string) => Reader<SVEnv, string>) | undefined, getLabel: (optionTranslation: OT) => string, getListPrefix: (translation: T) => string | undefined, getListSuffix: (translation: T) => string | undefined, getReplacement: (translation: T) => string | undefined, ...moreToAppend: ({
|
|
16
|
-
surround: (values: string) => StdReader<string, "t">;
|
|
17
|
-
getAdditionalValue: (option: V, optionTranslation: OT) => string | number;
|
|
18
|
-
} | undefined)[]) => Reader<StdEnv<"t" | "tm"> & VEnv & SVEnv, string>;
|
|
8
|
+
export declare const renderValueMap: <T, TE1, TE2, TE3, TE4, TE5, V extends string | number>(map: ValueMap<T>, renderValueMapOptionValue: (option: T, index: number) => Reader<TE1, V>, renderValueMapOptionLabel: (option: T, index: number) => Reader<TE2, string>, formatValue: (value: V | string) => Reader<TE3, string>, ...moreToAppend: ({
|
|
9
|
+
render: (option: T, index: number) => Reader<TE4, V>;
|
|
10
|
+
format: (value: V | string) => Reader<TE5, string>;
|
|
11
|
+
} | undefined)[]) => Reader<TE1 & TE2 & TE3 & TE4 & TE5 & StdEnv<"t" | "tm" | "rts">, string>;
|
|
19
12
|
/**
|
|
20
|
-
*
|
|
13
|
+
* Render the label of a value map option.
|
|
21
14
|
*/
|
|
22
|
-
export declare const
|
|
23
|
-
|
|
24
|
-
value: T;
|
|
25
|
-
translations?: LocaleMap<{
|
|
26
|
-
label: ResponsiveTextOptional;
|
|
27
|
-
}>;
|
|
28
|
-
}[];
|
|
29
|
-
style: ParameterMapStyle;
|
|
30
|
-
translations?: LocaleMap<{
|
|
31
|
-
listPrefix?: ResponsiveTextOptional;
|
|
32
|
-
listSuffix?: ResponsiveTextOptional;
|
|
33
|
-
replacement?: ResponsiveTextOptional;
|
|
34
|
-
}>;
|
|
35
|
-
}, getValue: (option: T) => Reader<VEnv, string | number>, surroundValues: ((values: string) => Reader<SVEnv, string>) | undefined, ...moreToAppend: ({
|
|
36
|
-
surround: (values: string) => StdReader<string, "t">;
|
|
37
|
-
getAdditionalValue: (option: T, optionTranslation: {
|
|
15
|
+
export declare const renderDefaultValueMapLabel: (option: {
|
|
16
|
+
translations: LocaleMap<{
|
|
38
17
|
label: ResponsiveTextOptional;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
18
|
+
}>;
|
|
19
|
+
}) => StdReader<string, "tm" | "rts">;
|
|
20
|
+
/**
|
|
21
|
+
* Renders a map of parameter options to a string.
|
|
22
|
+
*/
|
|
23
|
+
export declare const renderParameterMap: <T, TE1, TE2, TE3, TE4, V extends string | number>(map: ParameterMap<T>, renderParameterMapValue: (value: T, index: number) => Reader<TE1, V>, formatValue: (value: V | string) => Reader<TE2, string>, ...moreToAppend: ({
|
|
24
|
+
render: (option: ParameterMapOption<T>, index: number) => Reader<TE3, V>;
|
|
25
|
+
format: (value: V | string) => Reader<TE4, string>;
|
|
26
|
+
} | undefined)[]) => Reader<TE1 & StdEnv<"tm" | "rts"> & TE2 & TE3 & TE4 & StdEnv<"t" | "tm" | "rts">, string>;
|
|
@@ -1,57 +1,44 @@
|
|
|
1
|
-
import { mapNullableDefault } from "@elyukai/utils/nullable";
|
|
2
1
|
import { Reader } from "@elyukai/utils/reader";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
3
|
+
import { isNotNullish } from "@optolith/helpers/nullable";
|
|
4
|
+
import { responsiveTextOptionalR, sequence, translateMapR, translateR } from "./reader.js";
|
|
5
5
|
import { MISSING_VALUE } from "./unknown.js";
|
|
6
|
+
const renderReplacement = (translation, alt) => translation?.replacement !== undefined
|
|
7
|
+
? responsiveTextOptionalR(translation.replacement).thenW(replacement => replacement === undefined ? alt() : Reader.of(replacement))
|
|
8
|
+
: alt();
|
|
9
|
+
const renderListJoiner = (joiner, spacePosition) => joiner === undefined
|
|
10
|
+
? ""
|
|
11
|
+
: responsiveTextOptionalR(joiner).map(text => text === undefined ? "" : spacePosition === "before" ? ` ${text}` : `${text} `);
|
|
6
12
|
/**
|
|
7
13
|
* Renders a map of options to a string.
|
|
8
14
|
*/
|
|
9
|
-
export const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const optionTranslation = env.translateMap(option.translations);
|
|
20
|
-
return `${surroundValues(typeof value === "number" ? value.toFixed() : value).run(env)} ${env.translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${optionTranslation === undefined ? MISSING_VALUE : getLabel(optionTranslation)}${mapNullableDefault(mapNullable(translation, getListSuffix), listSuffix => ` ${listSuffix}`, "")}${moreToAppend
|
|
21
|
-
.filter(isNotNullish)
|
|
22
|
-
.map(({ surround, getAdditionalValue }) => {
|
|
23
|
-
const additionalValue = optionTranslation === undefined
|
|
24
|
-
? MISSING_VALUE
|
|
25
|
-
: getAdditionalValue(option.value, optionTranslation);
|
|
26
|
-
return surround(typeof additionalValue === "number" ? additionalValue.toFixed() : additionalValue).run(env);
|
|
27
|
-
})
|
|
28
|
-
.join("")}`;
|
|
29
|
-
})
|
|
30
|
-
.join(", ");
|
|
31
|
-
}
|
|
32
|
-
return `${surroundValues(map.options.map(option => getValue(option.value).run(env)).join("/")).run(env)} ${env.translate("for")} ${mapNullableDefault(mapNullable(translation, getListPrefix), listPrefix => `${listPrefix} `, "")}${map.options
|
|
33
|
-
.map(option => {
|
|
34
|
-
const optionTranslation = env.translateMap(option.translations);
|
|
35
|
-
if (optionTranslation === undefined) {
|
|
36
|
-
return MISSING_VALUE;
|
|
15
|
+
export const renderValueMap = (map, renderValueMapOptionValue, renderValueMapOptionLabel, formatValue, ...moreToAppend) => translateMapR(map.translations).thenW(translation => renderReplacement(translation, () => {
|
|
16
|
+
const { listPrefix, listSuffix } = translation ?? {};
|
|
17
|
+
const listPrefixR = renderListJoiner(listPrefix, "after");
|
|
18
|
+
const listSuffixR = renderListJoiner(listSuffix, "before");
|
|
19
|
+
const moreToAppendFns = moreToAppend.filter(isNotNullish);
|
|
20
|
+
switch (map.style.kind) {
|
|
21
|
+
case "Compressed": {
|
|
22
|
+
return Reader.traverse(map.options, (option, index) => renderValueMapOptionValue(option, index).thenW(value => renderValueMapOptionLabel(option, index).map(label => ({ value, label })))).thenW(renderedOptions => sequence `${formatValue(renderedOptions.map(option => (typeof option.value === "number" ? option.value.toFixed() : option.value)).join("/"))} ${translateR("for")} ${listPrefixR}${renderedOptions
|
|
23
|
+
.map(option => option.label)
|
|
24
|
+
.join("/")}${listSuffixR}${Reader.traverse(moreToAppendFns, more => Reader.traverse(map.options, more.render).thenW(values => more.format(values.join("/")))).map(values => values.join(""))}`);
|
|
37
25
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.map(({ surround, getAdditionalValue }) => surround(map.options
|
|
43
|
-
.map(option => {
|
|
44
|
-
const optionTranslation = env.translateMap(option.translations);
|
|
45
|
-
if (optionTranslation === undefined) {
|
|
46
|
-
return MISSING_VALUE;
|
|
26
|
+
case "Verbose": {
|
|
27
|
+
return Reader.traverse(map.options, (option, index) => renderValueMapOptionValue(option, index)
|
|
28
|
+
.thenW(value => renderValueMapOptionLabel(option, index).map(label => ({ value, label })))
|
|
29
|
+
.thenW(({ value, label }) => sequence `${formatValue(value)} ${translateR("for")} ${listPrefixR}${label}${listSuffixR}${Reader.traverse(moreToAppendFns, more => more.render(option, index).thenW(more.format)).map(values => values.join(""))}`)).map(values => values.join(", "));
|
|
47
30
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
31
|
+
default:
|
|
32
|
+
return assertExhaustive(map.style);
|
|
33
|
+
}
|
|
34
|
+
}));
|
|
53
35
|
/**
|
|
54
|
-
*
|
|
36
|
+
* Render the label of a value map option.
|
|
37
|
+
*/
|
|
38
|
+
export const renderDefaultValueMapLabel = (option) => translateMapR(option.translations).thenW(translation => translation?.label === undefined
|
|
39
|
+
? Reader.of(MISSING_VALUE)
|
|
40
|
+
: responsiveTextOptionalR(translation.label).map(text => text ?? translation.label.full));
|
|
41
|
+
/**
|
|
42
|
+
* Renders a map of parameter options to a string.
|
|
55
43
|
*/
|
|
56
|
-
export const
|
|
57
|
-
translation.label.full, translation => mapNullable(translation.listPrefix, text => getResponsiveTextOptional(text, env.responsiveTextSize)), translation => mapNullable(translation.listSuffix, text => getResponsiveTextOptional(text, env.responsiveTextSize)), translation => mapNullable(translation.replacement, text => getResponsiveTextOptional(text, env.responsiveTextSize)), ...moreToAppend).run(env));
|
|
44
|
+
export const renderParameterMap = (map, renderParameterMapValue, formatValue, ...moreToAppend) => renderValueMap(map, (option, index) => renderParameterMapValue(option.value, index), renderDefaultValueMapLabel, formatValue, ...moreToAppend);
|
|
@@ -26,7 +26,7 @@ export declare const attributedName: <E extends { [K in keyof EntityMap]: Entity
|
|
|
26
26
|
*/
|
|
27
27
|
export declare const customName: <E extends { [K in keyof EntityMap]: EntityMap[K] extends {
|
|
28
28
|
translations: LocaleMap<object>;
|
|
29
|
-
} ? K : never; }[keyof EntityMap]>(translateMap: TranslateMap, getInstanceById: GetInstanceById<E>, fn: (translation: EntityMap[E]["translations"][string]) => string, ...args: IdArgsVariant<EntityMap, E>) => string | undefined;
|
|
29
|
+
} ? K : never; }[keyof EntityMap]>(translateMap: TranslateMap, getInstanceById: GetInstanceById<E>, fn: (translation: EntityMap[E]["translations"][string], instance: EntityMap[E]) => string, ...args: IdArgsVariant<EntityMap, E>) => string | undefined;
|
|
30
30
|
/**
|
|
31
31
|
* Renders the name of an instance according to a custom function in an attributed string.
|
|
32
32
|
*/
|
|
@@ -47,6 +47,10 @@ export declare const attributedNameFromInstance: (translateMap: TranslateMap, in
|
|
|
47
47
|
export declare const attributedNameFromTranslation: (translation: {
|
|
48
48
|
name: string;
|
|
49
49
|
} | undefined, context: string, ...args: IdArgsVariant<EntityMap, keyof EntityMap>) => string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Renders the name of an instance in an attributed string.
|
|
52
|
+
*/
|
|
53
|
+
export declare const attributedNameFromText: (text: string, context: string, ...args: IdArgsVariant<EntityMap, keyof EntityMap>) => string;
|
|
50
54
|
/**
|
|
51
55
|
* Renders the name of an instance in an attributed string.
|
|
52
56
|
*/
|
|
@@ -28,7 +28,12 @@ export const attributedName = (translateMap, getInstanceById, context, ...args)
|
|
|
28
28
|
/**
|
|
29
29
|
* Renders the name of an instance according to a custom function.
|
|
30
30
|
*/
|
|
31
|
-
export const customName = (translateMap, getInstanceById, fn, ...args) =>
|
|
31
|
+
export const customName = (translateMap, getInstanceById, fn, ...args) => {
|
|
32
|
+
const instance = getInstanceById(...args);
|
|
33
|
+
return mapNullable(translateMap(instance?.translations), translation =>
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- instance must be non-nullish if translation is non-nullish
|
|
35
|
+
fn(translation, instance));
|
|
36
|
+
};
|
|
32
37
|
/**
|
|
33
38
|
* Renders the name of an instance according to a custom function in an attributed string.
|
|
34
39
|
*/
|
|
@@ -53,6 +58,13 @@ export const attributedNameFromTranslation = (translation, context, ...args) =>
|
|
|
53
58
|
const id = normalizedIdArgs(args);
|
|
54
59
|
return mapNullable(translation?.name, name => attributedInstance(name, id.entityName, id.id, { context: `"${context}"` }));
|
|
55
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Renders the name of an instance in an attributed string.
|
|
63
|
+
*/
|
|
64
|
+
export const attributedNameFromText = (text, context, ...args) => {
|
|
65
|
+
const id = normalizedIdArgs(args);
|
|
66
|
+
return attributedInstance(text, id.entityName, id.id, { context: `"${context}"` });
|
|
67
|
+
};
|
|
56
68
|
/**
|
|
57
69
|
* Renders the name of an instance in an attributed string.
|
|
58
70
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Reader } from "@elyukai/utils/reader";
|
|
2
|
-
import type { ActivatableIdentifier, AdvantageDisadvantagePrerequisites, AnimistPowerPrerequisites, ArcaneTraditionPrerequisites, DerivedCharacteristicPrerequisites, EnhancementPrerequisites, GeneralPrerequisites, GeodeRitualPrerequisites, InfluencePrerequisites, LanguagePrerequisites, LiturgyPrerequisites, PersonalityTraitPrerequisites, PlainGeneralPrerequisites, ProfessionPrerequisites, PublicationPrerequisites, SpellworkPrerequisites } from "@optolith/database-schema/gen";
|
|
2
|
+
import type { ActivatableIdentifier, AdvantageDisadvantagePrerequisites, AnimistPowerPrerequisites, ArcaneTraditionPrerequisites, DerivedCharacteristicPrerequisites, EnhancementPrerequisites, GeneralPrerequisites, GeodeRitualPrerequisites, InfluencePrerequisites, LanguagePrerequisites, LiturgyPrerequisites, PersonalityTraitPrerequisites, PlainGeneralPrerequisites, ProfessionPrerequisites, ProfessionVariantPrerequisites, PublicationPrerequisites, SpellworkPrerequisites } from "@optolith/database-schema/gen";
|
|
3
3
|
import type { StdEnv } from "../../../env.js";
|
|
4
4
|
import { printAdvantageDisadvantagePrerequisiteGroup, printAnimistPowerPrerequisiteGroup, printArcaneTraditionPrerequisiteGroup, printDerivedCharacteristicPrerequisiteGroup, printEnhancementPrerequisiteGroup, printGeneralPrerequisiteGroup, printGeodeRitualPrerequisiteGroup, printInfluencePrerequisiteGroup, printLanguagePrerequisiteGroup, printLiturgyPrerequisiteGroup, printPersonalityTraitPrerequisiteGroup, printProfessionPrerequisiteGroup, printPublicationPrerequisiteGroup, printSpellworkPrerequisiteGroup } from "./prerequisiteGroups.js";
|
|
5
5
|
type DeriveEnvF<T extends (...args: any[]) => any> = ReturnType<T> extends Reader<infer E, unknown> ? E : never;
|
|
@@ -26,6 +26,10 @@ export declare const printGeneralPrerequisites: (value: GeneralPrerequisites, pr
|
|
|
26
26
|
* Print profession prerequisites as a string.
|
|
27
27
|
*/
|
|
28
28
|
export declare const printProfessionPrerequisites: (value: ProfessionPrerequisites) => Reader<DeriveEnvF<typeof printProfessionPrerequisiteGroup> & StdEnv<"lj" | "lc">, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Print profession prerequisites as a string.
|
|
31
|
+
*/
|
|
32
|
+
export declare const printProfessionVariantPrerequisites: (value: ProfessionVariantPrerequisites) => Reader<DeriveEnvF<typeof printProfessionPrerequisiteGroup> & StdEnv<"lj" | "lc">, string>;
|
|
29
33
|
/**
|
|
30
34
|
* Print advantage disadvantage prerequisites as a string.
|
|
31
35
|
*/
|
|
@@ -76,6 +76,23 @@ const printPlainPrerequisites = (printPrerequisite, prerequisites) => Reader.tra
|
|
|
76
76
|
type: element.kind === "Single" ? element.Single.kind : element.kind,
|
|
77
77
|
part,
|
|
78
78
|
})))).thenW(list => joinPrerequisiteParts(list.filter(isNotNullish)));
|
|
79
|
+
const printPrerequisiteDiff = (printPrerequisite, map, element) => {
|
|
80
|
+
switch (element.kind) {
|
|
81
|
+
case "Add":
|
|
82
|
+
return printPrerequisite(element.Add).map(part => map(element.Add, part));
|
|
83
|
+
case "Remove":
|
|
84
|
+
return printPrerequisite(element.Remove).map(part => map(element.Remove, part === undefined ? undefined : { ...part, isRemoved: true }));
|
|
85
|
+
default:
|
|
86
|
+
return assertExhaustive(element);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Print plain prerequisites as a string.
|
|
91
|
+
*/
|
|
92
|
+
const printPlainDiffPrerequisites = (printPrerequisite, prerequisites) => Reader.traverse(prerequisites, diffElement => printPrerequisiteDiff(element => printPrerequisitesElement(printPrerequisite, element), (element, nullablePart) => mapNullable(nullablePart, part => ({
|
|
93
|
+
type: element.kind === "Single" ? element.Single.kind : element.kind,
|
|
94
|
+
part,
|
|
95
|
+
})), diffElement)).thenW(list => joinPrerequisiteParts(list.filter(isNotNullish)));
|
|
79
96
|
/**
|
|
80
97
|
* Print prerequisite for level as a string.
|
|
81
98
|
*/
|
|
@@ -169,6 +186,10 @@ export const printGeneralPrerequisites = (value, printPreviousLevelPrerequisites
|
|
|
169
186
|
* Print profession prerequisites as a string.
|
|
170
187
|
*/
|
|
171
188
|
export const printProfessionPrerequisites = (value) => printPlainPrerequisites(printProfessionPrerequisiteGroup, value);
|
|
189
|
+
/**
|
|
190
|
+
* Print profession prerequisites as a string.
|
|
191
|
+
*/
|
|
192
|
+
export const printProfessionVariantPrerequisites = (value) => printPlainDiffPrerequisites(printProfessionPrerequisiteGroup, value);
|
|
172
193
|
/**
|
|
173
194
|
* Print advantage disadvantage prerequisites as a string.
|
|
174
195
|
*/
|
|
@@ -15,7 +15,8 @@ export const hasPartValueObject = (part) => typeof part.value !== "string";
|
|
|
15
15
|
const countFurtherToCombined = (part, remainingParts) => {
|
|
16
16
|
const indexHasDifferentBaseValues = remainingParts.findIndex(remaining => !(part.label === remaining.label &&
|
|
17
17
|
deepEqual(part.value.id, remaining.value.id) &&
|
|
18
|
-
part.value.level === remaining.value.level
|
|
18
|
+
part.value.level === remaining.value.level &&
|
|
19
|
+
part.isRemoved === remaining.isRemoved));
|
|
19
20
|
return indexHasDifferentBaseValues === -1
|
|
20
21
|
? // if -1 is returned, all remaining parts have the same base values, so we can combine with all of them
|
|
21
22
|
remainingParts.length
|
|
@@ -59,8 +60,11 @@ const appendPrerequisitePartGroup = (previous, parts, isLast) => {
|
|
|
59
60
|
if (parts.length === 0) {
|
|
60
61
|
return Reader.of(previous);
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
const wrapInNoPrerequisite = (text) => parts[0]?.isRemoved === true
|
|
64
|
+
? translateR("no prerequisite {$prerequisite}", { prerequisite: text })
|
|
65
|
+
: Reader.of(text);
|
|
66
|
+
if (parts.every(hasPartValueObject)) {
|
|
67
|
+
return Reader.asks(({ translate, translateMap, localeCompare }) => appendBySentenceType(previous, wrapInNoPrerequisite(parts
|
|
64
68
|
.toSorted(on(part => fromUniformCase(part.value.id), localeCompare))
|
|
65
69
|
.reduce((acc, current, i, arr) => {
|
|
66
70
|
if (acc[1] > 0) {
|
|
@@ -70,20 +74,23 @@ const appendPrerequisitePartGroup = (previous, parts, isLast) => {
|
|
|
70
74
|
return [
|
|
71
75
|
[
|
|
72
76
|
...acc[0],
|
|
73
|
-
[
|
|
77
|
+
[
|
|
78
|
+
activatableKindToGroup(current.value.id.kind),
|
|
79
|
+
(current.label ?? "") + rendered,
|
|
80
|
+
],
|
|
74
81
|
],
|
|
75
82
|
furtherIncluded,
|
|
76
83
|
];
|
|
77
84
|
}, [[], 0])[0]
|
|
78
85
|
.toSorted(sortByActivatableGroupAndName(localeCompare))
|
|
79
86
|
.map(grouped => grouped[1])
|
|
80
|
-
.join(", "), undefined, isLast));
|
|
87
|
+
.join(", ")).run({ translate }), undefined, isLast));
|
|
81
88
|
}
|
|
82
89
|
else {
|
|
83
|
-
return Reader.asks(({ translateMap }) => parts.reduce((acc, current, i, arr) => appendBySentenceType(acc, (current.label ?? "") +
|
|
90
|
+
return Reader.asks(({ translate, translateMap }) => parts.reduce((acc, current, i, arr) => appendBySentenceType(acc, wrapInNoPrerequisite((current.label ?? "") +
|
|
84
91
|
(typeof current.value === "string"
|
|
85
92
|
? current.value
|
|
86
|
-
: wrapActivatableInAttributedString(renderActivatableNameComponents(translateMap, current.value, true), current.value.id)), current.sentenceType, isLast && i === arr.length - 1), previous));
|
|
93
|
+
: wrapActivatableInAttributedString(renderActivatableNameComponents(translateMap, current.value, true), current.value.id))).run({ translate }), current.sentenceType, isLast && i === arr.length - 1), previous));
|
|
87
94
|
}
|
|
88
95
|
};
|
|
89
96
|
/**
|
|
@@ -42,10 +42,12 @@ export declare const renderIndefiniteCost: (value: IndefiniteCost, shouldAppendN
|
|
|
42
42
|
/**
|
|
43
43
|
* Returns the text for a standalone one-time cost map of an activatable skill.
|
|
44
44
|
*/
|
|
45
|
-
export declare const renderStandaloneCostMap: (value: StandaloneCostMap) => Reader<StdEnv<"
|
|
45
|
+
export declare const renderStandaloneCostMap: (value: StandaloneCostMap) => Reader<StdEnv<"tm" | "rts"> & {
|
|
46
46
|
translate: import("../../../../helpers/translate.js").Translate;
|
|
47
47
|
energyUnit: import("../../units/energy.js").EnergyUnit;
|
|
48
|
-
}
|
|
48
|
+
} & {
|
|
49
|
+
translate: import("../../../../helpers/translate.js").Translate;
|
|
50
|
+
} & StdEnv<"t" | "tm" | "rts">, string>;
|
|
49
51
|
/**
|
|
50
52
|
* Returns the text for the cost of a one-time activatable skill.
|
|
51
53
|
*/
|
|
@@ -2,7 +2,7 @@ import { identity } from "@elyukai/utils/function";
|
|
|
2
2
|
import { Reader } from "@elyukai/utils/reader";
|
|
3
3
|
import { mapNullable } from "@optolith/helpers/nullable";
|
|
4
4
|
import { assertExhaustive } from "@optolith/helpers/typeSafety";
|
|
5
|
-
import {
|
|
5
|
+
import { renderParameterMap } from "../../map.js";
|
|
6
6
|
import { formatEnergyFnR, formatEnergyR, getInstanceByIdFnR, modifiableBySpeedR, responsiveLocaleJoinR, responsiveR, responsiveTextR, responsiveTranslateR, translateMapR, translateR, } from "../../reader.js";
|
|
7
7
|
import { appendNoteIfNeeded, replaceTextIfNeeded } from "../../responsiveText.js";
|
|
8
8
|
import { formatCombinedTimeSpanR } from "../../units/timeSpan.js";
|
|
@@ -114,13 +114,13 @@ const renderMultipleOneTimeCosts = (type, value) => {
|
|
|
114
114
|
/**
|
|
115
115
|
* Returns the text for a standalone one-time cost map of an activatable skill.
|
|
116
116
|
*/
|
|
117
|
-
export const renderStandaloneCostMap = (value) =>
|
|
117
|
+
export const renderStandaloneCostMap = (value) => renderParameterMap(value, option => Reader.of(option.value), formatEnergyR, value.options.every(option => option.value.permanentValue !== undefined)
|
|
118
118
|
? {
|
|
119
|
-
|
|
119
|
+
format: values => translateR(", {$value} of which are permanent", {
|
|
120
120
|
value: values,
|
|
121
121
|
}),
|
|
122
122
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
|
123
|
-
|
|
123
|
+
render: option => Reader.of(option.value.permanentValue),
|
|
124
124
|
}
|
|
125
125
|
: undefined);
|
|
126
126
|
/**
|
|
@@ -129,23 +129,23 @@ export const renderStandaloneCostMap = (value) => renderResponsiveMap(value, opt
|
|
|
129
129
|
const renderOneTimeCostMap = (value) => {
|
|
130
130
|
switch (value.kind) {
|
|
131
131
|
case "Modifiable":
|
|
132
|
-
return
|
|
132
|
+
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
133
|
? {
|
|
134
|
-
|
|
134
|
+
format: values => translateR(", {$value} of which are permanent", {
|
|
135
135
|
value: values,
|
|
136
136
|
}),
|
|
137
137
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
|
138
|
-
|
|
138
|
+
render: option => Reader.of(option.value.permanentValue),
|
|
139
139
|
}
|
|
140
140
|
: undefined);
|
|
141
141
|
case "NonModifiable":
|
|
142
|
-
return
|
|
142
|
+
return renderParameterMap(value.NonModifiable.map, option => Reader.of(option.value), formatEnergyR, value.NonModifiable.map.options.every(option => option.value.permanentValue !== undefined)
|
|
143
143
|
? {
|
|
144
|
-
|
|
144
|
+
format: values => translateR(", {$value} of which are permanent", {
|
|
145
145
|
value: values,
|
|
146
146
|
}),
|
|
147
147
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
|
148
|
-
|
|
148
|
+
render: option => Reader.of(option.value.permanentValue),
|
|
149
149
|
}
|
|
150
150
|
: undefined).then(text => appendNonModifiableSuffix(ModifiableParameter.Cost, text));
|
|
151
151
|
default:
|
|
@@ -155,9 +155,9 @@ const renderOneTimeCostMap = (value) => {
|
|
|
155
155
|
const renderSustainedCostMap = (value) => {
|
|
156
156
|
switch (value.kind) {
|
|
157
157
|
case "Modifiable":
|
|
158
|
-
return
|
|
158
|
+
return renderParameterMap(value.Modifiable.map, option => deriveModifiableCost(option.initialModificationLevel).map(cost => cost ?? MISSING_VALUE), singleValue => formatEnergyR(singleValue).thenW(energy => appendIntervalToCostR(value.Modifiable.interval, energy)));
|
|
159
159
|
case "NonModifiable":
|
|
160
|
-
return
|
|
160
|
+
return renderParameterMap(value.NonModifiable.map, option => Reader.of(option.value.toFixed()), singleValue => formatEnergyR(singleValue).thenW(energy => appendIntervalToCostR(value.NonModifiable.interval, energy))).then(text => appendNonModifiableSuffix(ModifiableParameter.Cost, text));
|
|
161
161
|
default:
|
|
162
162
|
return assertExhaustive(value);
|
|
163
163
|
}
|
|
@@ -262,4 +262,8 @@ export declare const modifyBySpeedR: Reader<{
|
|
|
262
262
|
* Returns the default values wrapped in a reader if the value is nullish, otherwise applies the given function to the value and returns the result.
|
|
263
263
|
*/
|
|
264
264
|
export declare const mapNullableR: <T, U, R>(defaultValue: U, fn: (value: NonNullable<T>) => Reader<U, R>) => (value: T) => Reader<U, R> | Reader<unknown, U>;
|
|
265
|
+
/**
|
|
266
|
+
* Use `Reader` instances directly in a template string.
|
|
267
|
+
*/
|
|
268
|
+
export declare const sequence: <E>(string: TemplateStringsArray, ...values: (Reader<E, string> | string)[]) => Reader<E, string>;
|
|
265
269
|
export {};
|
|
@@ -194,3 +194,7 @@ export const modifyBySpeedR = Reader.asks(({ speed }) => (key, level) => {
|
|
|
194
194
|
* Returns the default values wrapped in a reader if the value is nullish, otherwise applies the given function to the value and returns the result.
|
|
195
195
|
*/
|
|
196
196
|
export const mapNullableR = (defaultValue, fn) => (value) => isNotNullish(value) ? fn(value) : Reader.of(defaultValue);
|
|
197
|
+
/**
|
|
198
|
+
* Use `Reader` instances directly in a template string.
|
|
199
|
+
*/
|
|
200
|
+
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] ?? ""), ""));
|